/** * @file ResourceInfoFrame.java * @brief Class implementing a resource information frame. * * @section License * * Copyright (C) 2013-2014 Robert B. Colton * This file is a part of the LateralGM IDE. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ package org.lateralgm.subframes; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.io.FileWriter; import java.util.List; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.JToolBar; import javax.swing.KeyStroke; import org.lateralgm.components.CustomFileChooser; import org.lateralgm.components.impl.CustomFileFilter; import org.lateralgm.components.impl.TextAreaFocusTraversalPolicy; import org.lateralgm.main.LGM; import org.lateralgm.messages.Messages; import org.lateralgm.resources.GmObject; import org.lateralgm.resources.ResourceReference; import org.lateralgm.resources.GmObject.PGmObject; import org.lateralgm.resources.Timeline; import org.lateralgm.resources.library.LibAction; import org.lateralgm.resources.library.LibArgument; import org.lateralgm.resources.sub.Action; import org.lateralgm.resources.sub.Argument; import org.lateralgm.resources.sub.Event; import org.lateralgm.resources.sub.MainEvent; import org.lateralgm.resources.sub.Moment; import org.lateralgm.resources.sub.ShapePoint; public class ResourceInfoFrame extends JFrame implements ActionListener { private static final long serialVersionUID = 1L; protected JSpinner sSizes; protected JTextArea editor; protected Color fgColor; private CustomFileChooser fc; private int linesOfCode = 0; public JToolBar makeToolbar() { JToolBar tb = new JToolBar(); tb.add(addToolbarItem("ResourceInfoFrame.CONFIRM")); tb.addSeparator(); tb.add(addToolbarItem("ResourceInfoFrame.FILESAVE")); tb.add(addToolbarItem("ResourceInfoFrame.PRINT")); tb.addSeparator(); tb.add(addToolbarItem("ResourceInfoFrame.COPY")); return tb; } public JPopupMenu makeContextMenu() { // build popup menu final JPopupMenu popup = new JPopupMenu(); JMenuItem item; item = addItem("ResourceInfoFrame.COPY"); //$NON-NLS-1$ item.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("ResourceInfoFrame.COPY"))); popup.add(item); popup.addSeparator(); item = addItem("ResourceInfoFrame.SELECTALL"); //$NON-NLS-1$ item.setAccelerator(KeyStroke.getKeyStroke(Messages.getKeyboardString("ResourceInfoFrame.SELECTALL"))); popup.add(item); editor.setComponentPopupMenu(popup); return popup; } public JMenuItem addItem(String key) { JMenuItem item = new JMenuItem(Messages.getString(key)); item.setIcon(LGM.getIconForKey(key)); item.setActionCommand(key); item.addActionListener(this); return item; } public JButton addToolbarItem(String key) { JButton item = new JButton(); item.setToolTipText(Messages.getString(key)); item.setIcon(LGM.getIconForKey(key)); item.setActionCommand(key); item.addActionListener(this); return item; } public static int countLines(String str) { if (str == null || str.length() == 0) return 0; int lines = 1; int len = str.length(); for (int pos = 0; pos < len; pos++) { char c = str.charAt(pos); if (c == '\r') { lines++; if (pos + 1 < len && str.charAt(pos + 1) == '\n') pos++; } else if (c == '\n') { lines++; } } return lines; } public String loopActionsToString(List<Action> list) { String info = ""; linesOfCode = 0; int lms = list.size(); for (int i = 0; i < lms; i++) { Action a = list.get(i); LibAction la = a.getLibAction(); List<Argument> args = a.getArguments(); LibArgument[] libargs = la.libArguments; /* this is code that could be used to make the information more detailed with exact parameter values * i would also suggest adding this as a function to the action or lib action to convert it into a descriptive * string instead of just a generic list description String text = la.hintText; for (int ii = 0; ii < args.size(); ii++) { text = text.replace("@" + ii, args.get(ii).toString(libargs[ii])); } */ String text = la.description; String code = ""; if (la.actionKind == Action.ACT_CODE) { code = args.get(args.size() - 1).toString(libargs[args.size() - 1]); linesOfCode += countLines(code); text += " (" + linesOfCode + " Lines)"; text += "\n------ BEGIN ------"; text += "\n" + code; text += "\n------ END ------"; } info += "\n" + i + " " + text; } return info; } public void updateTimelineInfo(ResourceReference<Timeline> res) { setIconImage(LGM.getIconForKey("Resource.TML").getImage()); setTitle(Messages.getString("ResourceInfoFrame.TIMELINE_TITLE")); if (res == null) { editor.setText("ERROR! Timeline does not exist."); editor.setCaretPosition(0); editor.getCaret().setVisible(true); // show the caret return; } int totalLinesOfCode = 0; Timeline tml = res.get(); String propInfo = "**** Properties ****\n\n"; propInfo += Messages.getString("TimelineFrame.NAME") + " " + tml.getName() + "\n"; propInfo += Messages.getString("TimelineFrame.MOMENTS") + " " + tml.moments.size() + "\n"; propInfo += "Total Lines of Code" + ": "; String momInfo = "\n**** Moments ****"; String actInfo; for (Moment mom : tml.moments) { momInfo += "\n\n " + mom.toString(); if (mom.actions.size() > 0) { actInfo = loopActionsToString(mom.actions); totalLinesOfCode += linesOfCode; momInfo += " (" + linesOfCode + " Lines Of Code) :"; momInfo += actInfo; } else { momInfo += ":\n " + Messages.getString("TimelineFrame.EMPTY"); } } editor.setText(propInfo + totalLinesOfCode + "\n" + momInfo + "\n"); editor.setCaretPosition(0); editor.getCaret().setVisible(true); // show the caret } public void updateObjectInfo(ResourceReference<GmObject> ref) { setIconImage(LGM.getIconForKey("Resource.OBJ").getImage()); setTitle(Messages.getString("ResourceInfoFrame.OBJECT_TITLE")); if (ref == null) { editor.setText("ERROR! Object does not exist."); editor.setCaretPosition(0); editor.getCaret().setVisible(true); // show the caret return; } int totalLinesOfCode = 0; GmObject obj = ref.get(); String propInfo = "**** Properties ****\n\n"; propInfo += Messages.getString("GmObjectFrame.NAME") + ": " + obj.getName() + "\n"; ResourceReference<?> res = obj.get(PGmObject.PARENT); propInfo += Messages.getString("GmObjectFrame.PARENT") + ": "; if (res != null) { propInfo += res.get().getName(); } else { propInfo += Messages.getString("GmObjectFrame.NO_PARENT"); } propInfo += "\n"; res = obj.get(PGmObject.SPRITE); propInfo += Messages.getString("GmObjectFrame.SPRITE") + ": "; if (res != null) { propInfo += res.get().getName(); } else { propInfo += Messages.getString("GmObjectFrame.NO_SPRITE"); } propInfo += "\n"; res = obj.get(PGmObject.MASK); propInfo += Messages.getString("GmObjectFrame.MASK") + ": "; if (res != null) { propInfo += res.get().getName(); } else { propInfo += Messages.getString("GmObjectFrame.SAME_AS_SPRITE"); } propInfo += "\n"; propInfo += Messages.getString("GmObjectFrame.VISIBLE") + ": " + obj.get(PGmObject.VISIBLE) + "\n"; propInfo += Messages.getString("GmObjectFrame.SOLID") + ": " + obj.get(PGmObject.SOLID) + "\n"; propInfo += Messages.getString("GmObjectFrame.DEPTH") + ": " + obj.get(PGmObject.DEPTH) + "\n"; propInfo += Messages.getString("GmObjectFrame.PERSISTENT") + ": " + obj.get(PGmObject.PERSISTENT) + "\n"; propInfo += "Total Lines of Code" + ": "; String phyInfo = "**** Physics ****\n\n"; phyInfo += Messages.getString("GmObjectFrame.USES_PHYSICS") + ": " + obj.get(PGmObject.PHYSICS_OBJECT) + "\n"; phyInfo += Messages.getString("GmObjectFrame.AWAKE") + ": " + obj.get(PGmObject.PHYSICS_AWAKE) + "\n"; phyInfo += Messages.getString("GmObjectFrame.SENSOR") + ": " + obj.get(PGmObject.PHYSICS_SENSOR) + "\n"; phyInfo += Messages.getString("GmObjectFrame.KINEMATIC") + ": " + obj.get(PGmObject.PHYSICS_KINEMATIC) + "\n"; phyInfo += Messages.getString("GmObjectFrame.DENSITY") + ": " + obj.get(PGmObject.PHYSICS_DENSITY) + "\n"; phyInfo += Messages.getString("GmObjectFrame.RESTITUTION") + ": " + obj.get(PGmObject.PHYSICS_RESTITUTION) + "\n"; phyInfo += Messages.getString("GmObjectFrame.COLLISION_GROUP") + ": " + obj.get(PGmObject.PHYSICS_GROUP) + "\n"; phyInfo += Messages.getString("GmObjectFrame.DAMPING_LINEAR") + ": " + obj.get(PGmObject.PHYSICS_DAMPING_LINEAR) + "\n"; phyInfo += Messages.getString("GmObjectFrame.DAMPING_ANGULAR") + ": " + obj.get(PGmObject.PHYSICS_DAMPING_ANGULAR) + "\n"; phyInfo += Messages.getString("GmObjectFrame.FRICTION") + ": " + obj.get(PGmObject.PHYSICS_FRICTION) + "\n"; phyInfo += Messages.getString("GmObjectFrame.COLLISION_SHAPE") + ": " + obj.get(PGmObject.PHYSICS_SHAPE) + "\n"; phyInfo += Messages.getString("GmObjectFrame.SHAPE_POINTS") + ": " + obj.shapePoints.size() + "\n"; for (ShapePoint sp : obj.shapePoints) { phyInfo += sp.getX() + ", " + sp.getY() + "\n"; } String evtInfo = "\n**** Events ****"; for (MainEvent me : obj.mainEvents) { for (Event ev : me.events) { if (ev.actions.size() > 0) { evtInfo += "\n\n " + Event.eventName(ev.mainId,ev.id); String actInfo = loopActionsToString(ev.actions); totalLinesOfCode += linesOfCode; evtInfo += " (" + linesOfCode + " Lines Of Code) :"; evtInfo += actInfo; } else { evtInfo += ":\n " + Messages.getString("GmObjectFrame.EMPTY"); } } } editor.setText(propInfo + totalLinesOfCode + "\n\n" + phyInfo + evtInfo + "\n"); editor.setCaretPosition(0); editor.getCaret().setVisible(true); // show the caret } public ResourceInfoFrame() { //setAlwaysOnTop(true); setDefaultCloseOperation(HIDE_ON_CLOSE); setSize(440,500); setLocationRelativeTo(LGM.frame); fc = new CustomFileChooser("/org/lateralgm","LAST_GAMEINFO_DIR"); //$NON-NLS-1$ //$NON-NLS-2$ fc.setFileFilter(new CustomFileFilter(Messages.getString("ResourceInfoFrame.TYPE_TXT"),".txt")); //$NON-NLS-1$ //$NON-NLS-2$ add(makeToolbar(),BorderLayout.NORTH); editor = new JTextArea(); editor.setWrapStyleWord(false); JScrollPane scrollable = new JScrollPane(editor); add(scrollable,BorderLayout.CENTER); setFocusTraversalPolicy(new TextAreaFocusTraversalPolicy(editor)); editor.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); editor.setText("object info will be displayed here when loaded"); editor.setEditable(false); editor.getCaret().setVisible(true); // show the caret anyway editor.addFocusListener(new FocusListener() { public void focusLost(FocusEvent e) { return; } public void focusGained(FocusEvent e) { editor.getCaret().setVisible(true); // show the caret anyway } }); makeContextMenu(); } public void saveToFile() { fc.setDialogTitle(Messages.getString("ResourceInfoFrame.SAVE_TITLE")); //$NON-NLS-1$ if (fc.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) return; String name = fc.getSelectedFile().getPath(); if (CustomFileFilter.getExtension(name) == null) name += ".txt"; //$NON-NLS-1$ try { FileWriter out = new FileWriter(name); out.write(editor.getText()); out.close(); } catch (Exception e) { e.printStackTrace(); } } public void actionPerformed(ActionEvent ev) { String com = ev.getActionCommand(); if (com.equals("ResourceInfoFrame.FILESAVE")) //$NON-NLS-1$ { saveToFile(); return; } else if (com.equals("ResourceInfoFrame.COPY")) //$NON-NLS-1$ { editor.copy(); return; } else if (com.equals("ResourceInfoFrame.SELECTALL")) //$NON-NLS-1$ { editor.selectAll(); return; } else if (com.equals("ResourceInfoFrame.CONFIRM")) //$NON-NLS-1$ { this.setVisible(false); return; } else if (com.equals("ResourceInfoFrame.PRINT")) //$NON-NLS-1$ { try { editor.print(); } catch (Exception pex) { LGM.showDefaultExceptionHandler(pex); } return; } editor.getCaret().setVisible(true); // make sure caret stays visible } }