Update README.md
[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 boolean has(OBJECT o){\r
57                 return objectToEditor.containsKey(o);\r
58         }\r
59         \r
60         protected void objectAdded(OBJECT o, EDITOR e){}\r
61         \r
62         protected void objectRemoved(OBJECT o, EDITOR e){}\r
63         \r
64         protected final void addCreator(ObjectCreator c){\r
65                 creators.add(c);\r
66         }\r
67         \r
68         public boolean hasDirty(){\r
69                 return dirty.size() > 0;\r
70         }\r
71         \r
72         public final void dirty(final OBJECT o){\r
73                 if ( !dirty.contains(o) )\r
74                         setTitleAt(indexOfComponent(objectToEditor.get(o)), "*" + getTitleAt(indexOfComponent(objectToEditor.get(o))));\r
75                 dirty.add(o);\r
76         }\r
77         \r
78         public final void dirty(final EDITOR e){\r
79                 dirty(editorToObject.get(e));\r
80         }\r
81         \r
82         private final void undirty(final OBJECT o){\r
83                 if ( dirty.contains(o) )\r
84                         setTitleAt(indexOfComponent(objectToEditor.get(o)), getTitleAt(indexOfComponent(objectToEditor.get(o))).replaceAll("^\\*", ""));\r
85                 dirty.remove(o);\r
86         }\r
87         \r
88         public JMenu getMenu(){\r
89                 JMenu ret = new JMenu("File");\r
90                 for ( JComponent i : getMenuItems() )\r
91                         ret.add(i);\r
92                 return ret;\r
93         }\r
94         \r
95         private void menuNew(ObjectCreator c){\r
96                 add(c.newObject());\r
97         }\r
98 \r
99         @SuppressWarnings("unchecked")\r
100         public EDITOR getSelectedEditor() {\r
101                 EDITOR e = (EDITOR) super.getSelectedComponent();\r
102                 if (editorToObject.containsKey(e))\r
103                         return e;\r
104                 return null;\r
105         }\r
106 \r
107         private void close(){\r
108                 EDITOR e = getSelectedEditor();\r
109                 if ( e == null )\r
110                         return;\r
111                 OBJECT o = editorToObject.get(e);\r
112                 File f = editorToFile.get(e);\r
113                 \r
114                 if ( dirty.contains(o) ){\r
115                          int response = JOptionPane.showConfirmDialog(this, "Object is unsaved. Save Before Closing?", "Confirm", JOptionPane.YES_NO_CANCEL_OPTION);\r
116                          if ( response == JOptionPane.YES_OPTION ){\r
117                                  saveDialog();\r
118                          } else if ( response == JOptionPane.CANCEL_OPTION ){\r
119                                  return;\r
120                          }\r
121                 }\r
122                 \r
123                 undirty(o);\r
124                 objectToEditor.remove(o);\r
125                 editorToObject.remove(e);\r
126                 fileToEditor.remove(f);\r
127                 editorToFile.remove(e);\r
128                 objectRemoved(o, e);\r
129                 remove(e);\r
130         }\r
131         \r
132         private void saveDialog(){\r
133                 EDITOR e = getSelectedEditor();\r
134                 if ( e == null )\r
135                         return;\r
136                 if ( !editorToFile.containsKey(e) ){\r
137                         log.debug("Editor has no file, saving as...");\r
138                         saveAsDialog();\r
139                         return;\r
140                 }\r
141                 File file = editorToFile.get(e);\r
142                 log.debug("Saving to " + file.getAbsolutePath());\r
143                 try {\r
144                         saveToFile(editorToObject.get(e), file);\r
145                         undirty(editorToObject.get(e));\r
146                 } catch (IOException e1) {\r
147                         errorDialog(e1);\r
148                 }\r
149         }\r
150         private void saveAsDialog(){\r
151                 EDITOR e = getSelectedEditor();\r
152                 if ( e == null )\r
153                         return;\r
154                 final FileDialog fd = new FileDialog(frame, "Save" + noun + " As", FileDialog.SAVE);\r
155                 fd.setVisible(true);\r
156                 if (fd.getFile() != null ) {\r
157                         File file = new File(fd.getDirectory() + fd.getFile());\r
158                         try {\r
159                                 OBJECT o = editorToObject.get(e);\r
160                                 saveToFile(o, file);\r
161                                 undirty(o);\r
162                                 objectToEditor.put(o, e);\r
163                                 editorToObject.put(e, o);\r
164                                 fileToEditor.put(file, e);\r
165                                 editorToFile.put(e, file);\r
166                                 setTitleAt(\r
167                                                 getSelectedIndex(),\r
168                                                 file.getName());\r
169                         } catch (Exception e1) {\r
170                                 errorDialog(e1);\r
171                         }\r
172                 }\r
173         }\r
174         \r
175         private void openDialog(){\r
176                 final FileDialog fd = new FileDialog(frame, "Open" + noun + "...", FileDialog.LOAD);\r
177                 fd.setVisible(true);\r
178                 if ( fd.getFile() != null ) {\r
179                         File file = new File(fd.getDirectory() + fd.getFile());\r
180                         log.debug("Opening File " + file.getAbsolutePath());\r
181                         if ( fileToEditor.containsKey(file) ){\r
182                                 log.debug("File " + file.getAbsolutePath() + "Already open, focusing");\r
183                                 setSelectedComponent(fileToEditor.get(file));\r
184                                 return;\r
185                         }\r
186                         try {\r
187                                 OBJECT o = loadFromFile(file);\r
188                                 EDITOR e = createEditor(o);\r
189                                 objectToEditor.put(o, e);\r
190                                 editorToObject.put(e, o);\r
191                                 fileToEditor.put(file, e);\r
192                                 editorToFile.put(e, file);\r
193                                 addTab(file.getName(), e);      \r
194                                 objectAdded(o, e);\r
195                                 setSelectedComponent(e);\r
196                         } catch (Exception e) {\r
197                                 errorDialog(e);\r
198                         }\r
199                 }\r
200         }\r
201         \r
202         public final List<JComponent> getMenuItems(){\r
203                 List<JComponent> ret = new Vector<JComponent>();\r
204                 if ( creators.size() == 1 ){\r
205                         final ObjectCreator c = creators.get(0);\r
206                         ret.add(new JMenuItem("New " + c.getName()){\r
207                                 private static final long serialVersionUID = 1L;\r
208                                 {\r
209                                         addActionListener(new ActionListener() {\r
210                                                 @Override\r
211                                                 public void actionPerformed(ActionEvent ae) {\r
212                                                         log.debug("New");\r
213                                                         menuNew(c);\r
214                                                 }\r
215                                         });\r
216                                 }\r
217                         });\r
218                 } else {\r
219                         ret.add(new JMenu("New"){\r
220                                 private static final long serialVersionUID = 1L;\r
221                                 {\r
222                                         for (final ObjectCreator c : creators ){\r
223                                                 add(new JMenuItem("New " + c.getName()){\r
224                                                         private static final long serialVersionUID = 1L;\r
225                                                         {\r
226                                                                 addActionListener(new ActionListener() {\r
227                                                                         @Override\r
228                                                                         public void actionPerformed(ActionEvent ae) {\r
229                                                                                 log.debug("New");\r
230                                                                                 menuNew(c);\r
231                                                                         }\r
232                                                                 });\r
233                                                         }\r
234                                                 });\r
235                                         }\r
236                                 }\r
237                         });\r
238                 }\r
239                 ret.add(new JMenuItem("Open" + noun + "..."){\r
240                                 private static final long serialVersionUID = 1L;\r
241                                 {\r
242                                         addActionListener(new ActionListener() {\r
243                                                 @Override\r
244                                                 public void actionPerformed(ActionEvent ae) {\r
245                                                         log.debug("Open...");\r
246                                                         openDialog();\r
247                                                 }\r
248                                         });\r
249                                 }\r
250                         });\r
251                 ret.add(new JMenuItem("Close" + noun){\r
252                         private static final long serialVersionUID = 1L;\r
253                         {\r
254                                 addActionListener(new ActionListener() {\r
255                                         @Override\r
256                                         public void actionPerformed(ActionEvent ae) {\r
257                                                 log.debug("Close");\r
258                                                 close();\r
259                                         }\r
260                                 });\r
261                         }\r
262                 });\r
263                 ret.add(new JSeparator());\r
264                 ret.add(new JMenuItem("Save" + noun){\r
265                         private static final long serialVersionUID = 1L;\r
266                         {\r
267                                 addActionListener(new ActionListener() {\r
268                                         @Override\r
269                                         public void actionPerformed(ActionEvent ae) {\r
270                                                 log.debug("Save");\r
271                                                 saveDialog();\r
272                                         }\r
273                                 });\r
274                         }\r
275                 });\r
276                 ret.add(new JMenuItem("Save" + noun + " As..."){\r
277                         private static final long serialVersionUID = 1L;\r
278                         {\r
279                                 addActionListener(new ActionListener() {\r
280                                         @Override\r
281                                         public void actionPerformed(ActionEvent ae) {\r
282                                                 log.debug("Save As...");\r
283                                                 saveAsDialog();\r
284                                         }\r
285                                 });\r
286                         }\r
287                 });\r
288                 return ret;\r
289         }\r
290 \r
291         protected final void add(final OBJECT o){\r
292                 EDITOR e = createEditor(o);\r
293                 objectToEditor.put(o, e);\r
294                 editorToObject.put(e, o);\r
295                 addTab("new", e);\r
296                 dirty(o);\r
297                 objectAdded(o, e);\r
298                 setSelectedComponent(e);\r
299         }\r
300         \r
301         protected final void add(final OBJECT o, final File f){\r
302                 EDITOR e = createEditor(o);\r
303                 objectToEditor.put(o, e);\r
304                 editorToObject.put(e, o);\r
305                 fileToEditor.put(f, e);\r
306                 editorToFile.put(e, f);\r
307                 addTab(f.getName(), e);\r
308                 objectAdded(o, e);\r
309                 setSelectedComponent(e);\r
310         }\r
311         \r
312         public final void load(final File f) throws IOException{\r
313                 OBJECT o = loadFromFile(f);\r
314                 EDITOR e = createEditor(o);\r
315                 objectToEditor.put(o, e);\r
316                 editorToObject.put(e, o);\r
317                 fileToEditor.put(f, e);\r
318                 editorToFile.put(e, f);\r
319                 addTab(f.getName(), e);\r
320                 objectAdded(o, e);\r
321                 setSelectedComponent(e);\r
322         }\r
323         \r
324         public abstract EDITOR createEditor(final OBJECT o);\r
325         \r
326         protected abstract OBJECT loadFromFile(final File f) throws IOException;\r
327         \r
328         protected abstract void saveToFile(final OBJECT o, final File f) throws IOException;\r
329 \r
330         \r
331         private final void errorDialog(final Throwable t){\r
332                 t.printStackTrace();\r
333                 JOptionPane.showMessageDialog(MultiObjectEditor.this, t.getClass().getSimpleName() + ": " + t.getMessage());\r
334         }\r
335 }\r