Allow separator in menu
[sw/motorsim] / gui / com / billkuker / rocketry / motorsim / visual / MultiObjectEditor.java
1 package com.billkuker.rocketry.motorsim.visual;\r
2 import java.awt.Component;\r
3 import java.awt.FileDialog;\r
4 import java.awt.Frame;\r
5 import java.awt.event.ActionEvent;\r
6 import java.awt.event.ActionListener;\r
7 import java.io.File;\r
8 import java.io.IOException;\r
9 import java.util.HashMap;\r
10 import java.util.HashSet;\r
11 import java.util.List;\r
12 import java.util.Map;\r
13 import java.util.Set;\r
14 import java.util.Vector;\r
15 \r
16 import javax.swing.JComponent;\r
17 import javax.swing.JMenu;\r
18 import javax.swing.JMenuItem;\r
19 import javax.swing.JOptionPane;\r
20 import javax.swing.JSeparator;\r
21 import javax.swing.JTabbedPane;\r
22 \r
23 import org.apache.log4j.Logger;\r
24 \r
25 \r
26 public abstract class MultiObjectEditor<OBJECT, EDITOR extends Component> extends JTabbedPane {\r
27 \r
28         private static final long serialVersionUID = 1L;\r
29 \r
30         private static final Logger log = Logger.getLogger(MultiObjectEditor.class);\r
31         \r
32         protected abstract class ObjectCreator {\r
33                 public abstract OBJECT newObject();\r
34                 public abstract String getName();\r
35         }\r
36         \r
37         protected final Frame frame;\r
38         \r
39         private final String noun;\r
40         \r
41         private List<ObjectCreator> creators = new Vector<ObjectCreator>();\r
42         \r
43         private final Map<OBJECT, EDITOR> objectToEditor = new HashMap<OBJECT, EDITOR>();\r
44         private final Map<EDITOR, OBJECT> editorToObject = new HashMap<EDITOR, OBJECT>();\r
45         private final Map<File, EDITOR> fileToEditor = new HashMap<File, EDITOR>();\r
46         private final Map<EDITOR, File> editorToFile = new HashMap<EDITOR, File>();\r
47         \r
48         \r
49         private final Set<OBJECT> dirty = new HashSet<OBJECT>();\r
50         \r
51         public MultiObjectEditor(final Frame frame, final String noun){\r
52                 this.frame = frame;\r
53                 this.noun = " " + noun.trim();\r
54         }\r
55         \r
56         protected final void addCreator(ObjectCreator c){\r
57                 creators.add(c);\r
58         }\r
59         \r
60         public final void dirty(final OBJECT o){\r
61                 if ( !dirty.contains(o) )\r
62                         setTitleAt(indexOfComponent(objectToEditor.get(o)), "*" + getTitleAt(indexOfComponent(objectToEditor.get(o))));\r
63                 dirty.add(o);\r
64         }\r
65         \r
66         public final void dirty(final EDITOR e){\r
67                 dirty(editorToObject.get(e));\r
68         }\r
69         \r
70         private final void undirty(final OBJECT o){\r
71                 if ( dirty.contains(o) )\r
72                         setTitleAt(indexOfComponent(objectToEditor.get(o)), getTitleAt(indexOfComponent(objectToEditor.get(o))).replaceAll("^\\*", ""));\r
73                 dirty.remove(o);\r
74         }\r
75         \r
76         public JMenu getMenu(){\r
77                 JMenu ret = new JMenu("File");\r
78                 for ( JComponent i : getMenuItems() )\r
79                         ret.add(i);\r
80                 return ret;\r
81         }\r
82         \r
83         private void menuNew(ObjectCreator c){\r
84                 add(c.newObject());\r
85         }\r
86         \r
87         @SuppressWarnings("unchecked")\r
88         public EDITOR getSelectedEditor(){\r
89                 try {\r
90                         return (EDITOR)super.getSelectedComponent();\r
91                 } catch ( ClassCastException e ){\r
92                         return null;\r
93                 }\r
94         }\r
95         \r
96         private void close(){\r
97                 EDITOR e = getSelectedEditor();\r
98                 OBJECT o = editorToObject.get(e);\r
99                 File f = editorToFile.get(e);\r
100                 \r
101                 if ( dirty.contains(o) ){\r
102                          int response = JOptionPane.showConfirmDialog(this, "Object is unsaved. Save Before Closing?", "Confirm", JOptionPane.YES_NO_CANCEL_OPTION);\r
103                          if ( response == JOptionPane.YES_OPTION ){\r
104                                  saveDialog();\r
105                          } else if ( response == JOptionPane.CANCEL_OPTION ){\r
106                                  return;\r
107                          }\r
108                 }\r
109                 \r
110                 objectToEditor.remove(o);\r
111                 editorToObject.remove(e);\r
112                 fileToEditor.remove(f);\r
113                 editorToFile.remove(e);\r
114                 remove(e);\r
115         }\r
116         \r
117         private void saveDialog(){\r
118                 EDITOR e = getSelectedEditor();\r
119                 if ( !editorToFile.containsKey(e) ){\r
120                         log.debug("Editor has no file, saving as...");\r
121                         saveAsDialog();\r
122                         return;\r
123                 }\r
124                 File file = editorToFile.get(e);\r
125                 log.debug("Saving to " + file.getAbsolutePath());\r
126                 try {\r
127                         saveToFile(editorToObject.get(e), file);\r
128                         undirty(editorToObject.get(e));\r
129                 } catch (IOException e1) {\r
130                         errorDialog(e1);\r
131                 }\r
132         }\r
133         private void saveAsDialog(){\r
134                 EDITOR e = getSelectedEditor();\r
135                 final FileDialog fd = new FileDialog(frame, "Save" + noun + " As", FileDialog.SAVE);\r
136                 fd.setVisible(true);\r
137                 if (fd.getFile() != null ) {\r
138                         File file = new File(fd.getDirectory() + fd.getFile());\r
139                         try {\r
140                                 OBJECT o = editorToObject.get(e);\r
141                                 saveToFile(o, file);\r
142                                 undirty(o);\r
143                                 objectToEditor.put(o, e);\r
144                                 editorToObject.put(e, o);\r
145                                 fileToEditor.put(file, e);\r
146                                 editorToFile.put(e, file);\r
147                                 setTitleAt(\r
148                                                 getSelectedIndex(),\r
149                                                 file.getName());\r
150                         } catch (Exception e1) {\r
151                                 errorDialog(e1);\r
152                         }\r
153                 }\r
154         }\r
155         \r
156         private void openDialog(){\r
157                 final FileDialog fd = new FileDialog(frame, "Open" + noun + "...", FileDialog.LOAD);\r
158                 fd.setVisible(true);\r
159                 if ( fd.getFile() != null ) {\r
160                         File file = new File(fd.getDirectory() + fd.getFile());\r
161                         log.debug("Opening File " + file.getAbsolutePath());\r
162                         if ( fileToEditor.containsKey(file) ){\r
163                                 log.debug("File " + file.getAbsolutePath() + "Already open, focusing");\r
164                                 setSelectedComponent(fileToEditor.get(file));\r
165                                 return;\r
166                         }\r
167                         try {\r
168                                 OBJECT o = loadFromFile(file);\r
169                                 EDITOR e = createEditor(o);\r
170                                 objectToEditor.put(o, e);\r
171                                 editorToObject.put(e, o);\r
172                                 fileToEditor.put(file, e);\r
173                                 editorToFile.put(e, file);\r
174                                 addTab(file.getName(), e);                                                                      \r
175                         } catch (Exception e) {\r
176                                 errorDialog(e);\r
177                         }\r
178                 }\r
179         }\r
180         \r
181         public final List<JComponent> getMenuItems(){\r
182                 List<JComponent> ret = new Vector<JComponent>();\r
183                 if ( creators.size() == 1 ){\r
184                         final ObjectCreator c = creators.get(0);\r
185                         ret.add(new JMenuItem("New " + c.getName()){\r
186                                 private static final long serialVersionUID = 1L;\r
187                                 {\r
188                                         addActionListener(new ActionListener() {\r
189                                                 @Override\r
190                                                 public void actionPerformed(ActionEvent ae) {\r
191                                                         log.debug("New");\r
192                                                         menuNew(c);\r
193                                                 }\r
194                                         });\r
195                                 }\r
196                         });\r
197                 } else {\r
198                         ret.add(new JMenu("New"){\r
199                                 private static final long serialVersionUID = 1L;\r
200                                 {\r
201                                         for (final ObjectCreator c : creators ){\r
202                                                 add(new JMenuItem("New " + c.getName()){\r
203                                                         private static final long serialVersionUID = 1L;\r
204                                                         {\r
205                                                                 addActionListener(new ActionListener() {\r
206                                                                         @Override\r
207                                                                         public void actionPerformed(ActionEvent ae) {\r
208                                                                                 log.debug("New");\r
209                                                                                 menuNew(c);\r
210                                                                         }\r
211                                                                 });\r
212                                                         }\r
213                                                 });\r
214                                         }\r
215                                 }\r
216                         });\r
217                 }\r
218                 ret.add(new JMenuItem("Open" + noun + "..."){\r
219                                 private static final long serialVersionUID = 1L;\r
220                                 {\r
221                                         addActionListener(new ActionListener() {\r
222                                                 @Override\r
223                                                 public void actionPerformed(ActionEvent ae) {\r
224                                                         log.debug("Open...");\r
225                                                         openDialog();\r
226                                                 }\r
227                                         });\r
228                                 }\r
229                         });\r
230                 ret.add(new JMenuItem("Close" + noun){\r
231                         private static final long serialVersionUID = 1L;\r
232                         {\r
233                                 addActionListener(new ActionListener() {\r
234                                         @Override\r
235                                         public void actionPerformed(ActionEvent ae) {\r
236                                                 log.debug("Close");\r
237                                                 close();\r
238                                         }\r
239                                 });\r
240                         }\r
241                 });\r
242                 ret.add(new JSeparator());\r
243                 ret.add(new JMenuItem("Save" + noun){\r
244                         private static final long serialVersionUID = 1L;\r
245                         {\r
246                                 addActionListener(new ActionListener() {\r
247                                         @Override\r
248                                         public void actionPerformed(ActionEvent ae) {\r
249                                                 log.debug("Save");\r
250                                                 saveDialog();\r
251                                         }\r
252                                 });\r
253                         }\r
254                 });\r
255                 ret.add(new JMenuItem("Save" + noun + " As..."){\r
256                         private static final long serialVersionUID = 1L;\r
257                         {\r
258                                 addActionListener(new ActionListener() {\r
259                                         @Override\r
260                                         public void actionPerformed(ActionEvent ae) {\r
261                                                 log.debug("Save As...");\r
262                                                 saveAsDialog();\r
263                                         }\r
264                                 });\r
265                         }\r
266                 });\r
267                 return ret;\r
268         }\r
269 \r
270         protected final void add(final OBJECT o){\r
271                 EDITOR e = createEditor(o);\r
272                 objectToEditor.put(o, e);\r
273                 editorToObject.put(e, o);\r
274                 addTab("new", e);\r
275                 dirty(o);\r
276         }\r
277         \r
278         public final void load(final File f) throws IOException{\r
279                 OBJECT o = loadFromFile(f);\r
280                 EDITOR e = createEditor(o);\r
281                 objectToEditor.put(o, e);\r
282                 editorToObject.put(e, o);\r
283                 fileToEditor.put(f, e);\r
284                 editorToFile.put(e, f);\r
285                 addTab(f.getName(), e);\r
286         }\r
287         \r
288         public abstract EDITOR createEditor(final OBJECT o);\r
289         \r
290         protected abstract OBJECT loadFromFile(final File f) throws IOException;\r
291         \r
292         protected abstract void saveToFile(final OBJECT o, final File f) throws IOException;\r
293 \r
294         \r
295         private final void errorDialog(final Throwable t){\r
296                 t.printStackTrace();\r
297                 JOptionPane.showMessageDialog(MultiObjectEditor.this, t.getClass().getSimpleName() + ": " + t.getMessage());\r
298         }\r
299 }\r