1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.cosylab.gui.components.customizer;
21
22 import com.cosylab.gui.components.SimpleDisplayer;
23 import com.cosylab.gui.components.util.ColorHelper;
24 import com.cosylab.gui.components.util.IconHelper;
25
26 import com.cosylab.logging.DebugLogger;
27
28 import java.awt.BorderLayout;
29 import java.awt.Component;
30 import java.awt.FlowLayout;
31 import java.awt.Graphics;
32 import java.awt.GridBagConstraints;
33 import java.awt.GridBagLayout;
34 import java.awt.Insets;
35 import java.awt.event.ActionEvent;
36 import java.awt.event.ActionListener;
37 import java.awt.event.ComponentAdapter;
38 import java.awt.event.ComponentEvent;
39 import java.awt.event.WindowAdapter;
40 import java.awt.event.WindowEvent;
41
42 import java.io.File;
43 import java.io.IOException;
44 import java.io.InputStream;
45
46 import java.util.Arrays;
47 import java.util.Enumeration;
48 import java.util.HashMap;
49 import java.util.Iterator;
50 import java.util.LinkedHashMap;
51 import java.util.Map;
52 import java.util.Properties;
53 import java.util.logging.Level;
54 import java.util.logging.Logger;
55
56 import javax.swing.Icon;
57 import javax.swing.JButton;
58 import javax.swing.JComponent;
59 import javax.swing.JDialog;
60 import javax.swing.JPanel;
61 import javax.swing.JScrollPane;
62 import javax.swing.JSplitPane;
63 import javax.swing.JTree;
64 import javax.swing.SwingUtilities;
65 import javax.swing.event.AncestorEvent;
66 import javax.swing.event.AncestorListener;
67 import javax.swing.event.TreeSelectionEvent;
68 import javax.swing.event.TreeSelectionListener;
69 import javax.swing.tree.DefaultMutableTreeNode;
70 import javax.swing.tree.DefaultTreeCellRenderer;
71 import javax.swing.tree.DefaultTreeModel;
72 import javax.swing.tree.TreeCellRenderer;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public class Customizer extends JPanel
89 {
90 private static final long serialVersionUID = 1L;
91 private final static Logger log = DebugLogger.getLogger(Customizer.class
92 .getName(), Level.OFF);
93 private JComponent displayer;
94 private Editor editor;
95 private Map<String, Class> editorTypes;
96 private HashMap<Class, Editor> editors;
97 private JPanel aspectPanel;
98 private JTree aspectTree;
99 private JDialog dialog;
100 private JPanel panel;
101 private boolean editing = false;
102 private String lastAspect;
103
104
105
106
107 private class AspectTreeCellRenderer extends DefaultTreeCellRenderer
108 implements TreeCellRenderer
109 {
110 private static final long serialVersionUID = 1L;
111
112
113
114
115 public AspectTreeCellRenderer()
116 {
117 try {
118 setDisabledIcon(null);
119 setLeafIcon(new Icon() {
120 public int getIconHeight()
121 {
122 return 5;
123 }
124
125 public int getIconWidth()
126 {
127 return 5;
128 }
129
130 public void paintIcon(Component c, Graphics g, int x,
131 int y)
132 {
133 g.setColor(ColorHelper.getCosyControlShadow());
134 g.drawOval(x, y, 5, 5);
135 }
136 });
137 setOpenIcon(IconHelper.createIcon("icons/navigation/Down16.gif"));
138 setClosedIcon(IconHelper.createIcon(
139 "icons/navigation/Forward16.gif"));
140
141
142 } catch (Exception e) {
143 setClosedIcon(null);
144 setDisabledIcon(null);
145 setLeafIcon(null);
146 setOpenIcon(null);
147 }
148 }
149 }
150
151
152
153
154 public Customizer()
155 {
156 super();
157 editors = new HashMap<Class, Editor>();
158 editorTypes = new LinkedHashMap<String, Class>();
159 }
160
161
162
163
164
165
166
167 public Customizer(SimpleDisplayer disp)
168 {
169 this((JComponent)disp, disp.getEditors());
170 }
171
172
173
174
175
176
177
178
179
180 public Customizer(JComponent disp, Map<String, Class> editorTypes)
181 {
182 this();
183 setDisplayer(disp);
184 addEditors(editorTypes);
185 }
186
187
188
189
190 private void testEditors(Map map)
191 {
192 Iterator it = map.values().iterator();
193
194 while (it.hasNext()) {
195 Object element = it.next();
196
197 if (!(element instanceof Class)
198 || !Editor.class.isAssignableFrom((Class)element)) {
199 throw new IllegalArgumentException("Element '" + element
200 + "' does not implement editor interface "
201 + Editor.class.getName() + ".");
202 }
203 }
204 }
205
206
207
208
209
210
211
212
213
214
215
216 public Editor getEditor(String aspect)
217 throws InstantiationException, IllegalAccessException
218 {
219 return getEditor((Class)editorTypes.get(aspect));
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233 public Editor getEditor(Class type)
234 throws InstantiationException, IllegalAccessException
235 {
236 if (type == null) {
237 return null;
238 }
239
240 if (!editorTypes.values().contains(type)) {
241 return null;
242 }
243
244 return _getEditor(type);
245 }
246
247
248
249
250 private Editor _getEditor(Class type)
251 throws InstantiationException, IllegalAccessException
252 {
253 if (type == null) {
254 return null;
255 }
256
257 Editor e = editors.get(type);
258
259 if (e != null) {
260 return e;
261 }
262
263 e = (Editor)type.newInstance();
264
265 editors.put(type, e);
266
267 return e;
268 }
269
270
271
272
273 private void initialize()
274 {
275 initAspectTree();
276 initAspectPanel(null);
277 setLayout(new BorderLayout());
278 add(getButtonsPanel(), BorderLayout.SOUTH);
279 add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
280 new JScrollPane(aspectTree), new JScrollPane(aspectPanel)));
281 }
282
283
284
285
286 private void initAspectTree()
287 {
288 if (aspectTree == null) {
289 aspectTree = new JTree();
290 aspectTree.setCellRenderer(new AspectTreeCellRenderer());
291 aspectTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
292 public void valueChanged(TreeSelectionEvent e)
293 {
294 Object[] aspectPath = ((DefaultMutableTreeNode)e.getNewLeadSelectionPath()
295 .getLastPathComponent()).getUserObjectPath();
296 String aspect = "";
297
298 for (int i = 1; i < aspectPath.length; i++) {
299 aspect += "/" + (String)aspectPath[i];
300 }
301
302 initAspectPanel(aspect.substring(1));
303 }
304 });
305
306
307 }
308
309 fillAspectTree();
310
311 for (int i = 0; i < aspectTree.getRowCount(); i++) {
312 aspectTree.expandRow(i);
313 }
314
315
316
317
318 }
319
320
321
322
323 private void fillAspectTree()
324 {
325 Iterator<String> keys = editorTypes.keySet().iterator();
326
327
328 ((DefaultTreeModel)aspectTree.getModel()).setRoot(new DefaultMutableTreeNode(
329 ""));
330 aspectTree.setRootVisible(false);
331
332 while(keys.hasNext()) {
333 addAspectToTree(new String(keys.next()));
334 }
335 ((DefaultTreeModel)aspectTree.getModel()).reload();
336 }
337
338
339
340
341 private void addAspectToTree(String aspect)
342 {
343 DefaultMutableTreeNode parent = (DefaultMutableTreeNode)((DefaultTreeModel)aspectTree
344 .getModel()).getRoot();
345
346 while (aspect.indexOf('/') >= 0) {
347 String parentAspect = aspect.substring(0, aspect.indexOf('/'));
348 aspect = aspect.substring(aspect.indexOf('/') + 1);
349
350 Enumeration en = parent.children();
351 DefaultMutableTreeNode tempParent = null;
352
353 while (en.hasMoreElements()) {
354 tempParent = (DefaultMutableTreeNode)en.nextElement();
355
356 if (tempParent.getUserObject().equals(parentAspect)) {
357 parent = tempParent;
358
359 break;
360 }
361 }
362
363 if (tempParent != parent) {
364 tempParent = new DefaultMutableTreeNode(parentAspect);
365 parent.add(tempParent);
366 parent = tempParent;
367 }
368 }
369
370 parent.add(new DefaultMutableTreeNode(aspect));
371 }
372
373
374
375
376
377 private JComponent getButtonsPanel()
378 {
379 if (panel == null) {
380 panel = new JPanel();
381
382 panel.setLayout(new FlowLayout(FlowLayout.RIGHT, 11, 11));
383
384
385 JButton button = new JButton("OK");
386 button.addActionListener(new ActionListener() {
387 public void actionPerformed(ActionEvent e)
388 {
389 commitEditing();
390 }
391 });
392 panel.add(button);
393
394
395 button = new JButton("Apply");
396 button.addActionListener(new ActionListener() {
397 public void actionPerformed(ActionEvent e)
398 {
399 applyEditing();
400 }
401 });
402 panel.add(button);
403
404
405 button = new JButton("Cancel");
406 button.addActionListener(new ActionListener() {
407 public void actionPerformed(ActionEvent e)
408 {
409 cancelEditing();
410 }
411 });
412 panel.add(button);
413 }
414
415 return panel;
416 }
417
418
419
420
421
422 private synchronized void initAspectPanel(final String aspect)
423 {
424 log.fine("Invoking display for '" + aspect + "'");
425 lastAspect = aspect;
426
427 if (aspectPanel == null) {
428 aspectPanel = new JPanel();
429 aspectPanel.setLayout(new GridBagLayout());
430 }
431
432 if (editor!=null) {
433 editor.stopEditing();
434 }
435 aspectPanel.removeAll();
436
437 try {
438 if (containsEditor(aspect)) {
439 editor = getEditor(aspect);
440
441
442
443
444
445
446
447
448 aspectPanel.add(editor.getEditorComponent(displayer, aspect),
449 new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0,
450 GridBagConstraints.NORTH,
451 GridBagConstraints.HORIZONTAL, new Insets(4, 4, 4, 4),
452 0, 0));
453
454
455
456 }
457 } catch (Exception e) {
458 System.out.println("Failed to load editor '"
459 + editorTypes.get(aspect) + "' for aspect '" + aspect + "': "
460 + e);
461
462 e.printStackTrace();
463 }
464
465 log.fine("Calling repaint for '" + aspect + "'");
466
467 revalidate();
468 repaint();
469 }
470
471
472
473
474
475
476
477
478
479
480 public boolean containsEditor(String aspect)
481 {
482 return editorTypes.containsKey(aspect);
483 }
484
485
486
487
488 private void _stopEditing()
489 {
490 editing = false;
491
492 Iterator<Editor> it = editors.values().iterator();
493 Editor element;
494 while (it.hasNext()) {
495 element = it.next();
496
497 try {
498 element.stopEditing();
499 } catch (Exception e) {
500 e.printStackTrace();
501 }
502 }
503
504 if (dialog != null) {
505 dialog.dispose();
506 }
507 }
508
509
510
511
512 private void cancelEditing()
513 {
514 SwingUtilities.invokeLater(new Runnable() {
515 public void run()
516 {
517 _cancelEditing();
518 }
519 });
520 }
521
522
523
524
525 private void _cancelEditing()
526 {
527 Iterator<Editor> it = editors.values().iterator();
528
529 Editor element;
530 while (it.hasNext()) {
531 element = it.next();
532
533 try {
534 element.revertSettings();
535 } catch (Exception e) {
536 e.printStackTrace();
537 }
538 }
539
540 _stopEditing();
541 }
542
543
544
545
546 private void applyEditing()
547 {
548 SwingUtilities.invokeLater(new Runnable() {
549 public void run()
550 {
551 _applyEditing();
552 }
553 });
554 }
555
556
557
558
559 private void _applyEditing()
560 {
561 Iterator<Editor> it = editors.values().iterator();
562
563 Editor element;
564 while (it.hasNext()) {
565 element = it.next();
566
567 try {
568 element.applySettings();
569 } catch (Exception e) {
570 e.printStackTrace();
571 }
572 }
573 }
574
575
576
577
578 private void commitEditing()
579 {
580 SwingUtilities.invokeLater(new Runnable() {
581 public void run()
582 {
583 _applyEditing();
584 _stopEditing();
585 }
586 });
587 }
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603 public JDialog showDialog(Component c)
604 {
605 if (dialog == null) {
606 dialog = new JDialog();
607 dialog.setTitle(getName() + " Customizer");
608 dialog.setModal(false);
609 dialog.addWindowListener(new WindowAdapter() {
610 public void windowClosing(WindowEvent e)
611 {
612 if (editing) {
613 cancelEditing();
614 }
615
616 Iterator it = editors.values().iterator();
617
618 while (it.hasNext()) {
619 Editor element = (Editor)it.next();
620 element.stopEditing();
621 }
622 }
623 });
624 dialog.setSize(getSize().width + dialog.getInsets().left
625 + dialog.getInsets().right,
626 getSize().height + dialog.getInsets().top
627 + dialog.getInsets().bottom);
628 dialog.getContentPane().setLayout(new BorderLayout());
629 dialog.getContentPane().add(this, BorderLayout.CENTER);
630
631 dialog.setLocation((int)((c.getWidth() - dialog.getWidth()) / 2.0),
632 (int)((c.getHeight() - dialog.getHeight()) / 2.0));
633 dialog.setLocationRelativeTo(c);
634 }
635
636 initAspectPanel(lastAspect);
637 dialog.setVisible(true);
638 editing = true;
639
640 return dialog;
641 }
642
643
644
645
646
647
648
649
650
651
652
653
654
655 public JDialog showDialog()
656 {
657 return showDialog(displayer);
658 }
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676 public static Customizer loadDefaultCustomizer(JComponent disp)
677 {
678 final Customizer c = new Customizer(disp, loadDefaultEditorTypes(disp));
679
680
681 c.setName(disp.getClass().getName().substring(disp.getClass().getName()
682 .lastIndexOf('.') + 1));
683
684
685 disp.addComponentListener(new ComponentAdapter() {
686 public void componentHidden(ComponentEvent e)
687 {
688 c.cancelEditing();
689 }
690 });
691
692 disp.addAncestorListener(new AncestorListener() {
693 public void ancestorAdded(AncestorEvent event)
694 {
695
696 }
697
698 public void ancestorMoved(AncestorEvent event)
699 {
700
701 }
702
703 public void ancestorRemoved(AncestorEvent event)
704 {
705 c.cancelEditing();
706 }
707 });
708
709 c.setSize(400, 300);
710
711 return c;
712 }
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744 public static Map<String, Class> loadDefaultEditorTypes(Object disp)
745 {
746 log.fine("Loading for displayer '" + disp.getClass().getName() + "'.");
747
748 Map<String, Class> map = new LinkedHashMap<String, Class>();
749
750
751 Class cl = disp.getClass();
752
753 boolean found = false;
754
755 while (!cl.equals(Object.class) && !found) {
756 String editor = cl.getName() + "Editor";
757
758 try {
759 Class c = Class.forName(editor);
760
761 String[] aspects = (String[])c.getField("ASPECTS").get(null);
762
763 if (aspects != null) {
764 log.finer("Loaded from ASPECT field :'"
765 + Arrays.asList(aspects));
766
767 for (int i = 0; i < aspects.length; i++) {
768 map.put(aspects[i], c);
769 }
770 }
771
772 found = true;
773 } catch (Exception e) {
774
775 }
776
777
778 String file = cl.getName().replace('.', File.separatorChar)
779 + ".editors";
780
781 Properties p = new Properties();
782
783 try {
784 InputStream in = ClassLoader.getSystemResourceAsStream(file);
785
786 if (in != null) {
787 p.load(in);
788 log.finer("Loaded from file :'" + file + "': '" + p + "'.");
789 found = true;
790 } else {
791 log.finer("File not found :'" + file + "'.");
792 }
793 } catch (IOException e) {
794 log.finer("Failed to load from file :'" + file + "': '" + e
795 + "'.");
796 e.printStackTrace();
797 }
798
799 Iterator it = p.keySet().iterator();
800
801 while (it.hasNext()) {
802 String element = (String)it.next();
803
804 try {
805 map.put(element, Class.forName(p.getProperty(element)));
806 } catch (Exception e) {
807 e.printStackTrace();
808 }
809 }
810
811 cl = cl.getSuperclass();
812 }
813
814
815 if (disp instanceof SimpleDisplayer) {
816 map.putAll(((SimpleDisplayer)disp).getEditors());
817 }
818
819 return map;
820 }
821
822
823
824
825
826
827
828
829 public void addEditors(Map<String, Class> newEditorTypes)
830 {
831 testEditors(newEditorTypes);
832 editorTypes.putAll(newEditorTypes);
833 editors.clear();
834 initialize();
835 }
836
837
838
839
840
841
842 public JComponent getDisplayer()
843 {
844 return displayer;
845 }
846
847
848
849
850
851
852 public void setDisplayer(JComponent component)
853 {
854 displayer = component;
855 }
856
857 public JPanel getAspectPanel() {
858 return aspectPanel;
859 }
860
861 public JTree getAspectTree() {
862 return aspectTree;
863 }
864 }
865
866