[Markups]
[aprog.git] / Markups / src / net / sourceforge / aprog / markups / MarkupsComponents.java
bloba527068cfc068b7ef6c1ccb625038922d2694ad3
1 /*
2 * The MIT License
3 *
4 * Copyright 2010 Codist Monk.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 package net.sourceforge.aprog.markups;
27 import static javax.swing.KeyStroke.getKeyStroke;
29 import static net.sourceforge.aprog.i18n.Messages.*;
30 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.*;
31 import static net.sourceforge.aprog.markups.MarkupsTools.*;
32 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.*;
33 import static net.sourceforge.aprog.swing.SwingTools.checkAWT;
34 import static net.sourceforge.aprog.swing.SwingTools.menuBar;
35 import static net.sourceforge.aprog.swing.SwingTools.packAndCenter;
36 import static net.sourceforge.aprog.swing.SwingTools.scrollable;
37 import static net.sourceforge.aprog.tools.Tools.*;
38 import static net.sourceforge.aprog.xml.XMLTools.*;
40 import java.awt.BorderLayout;
41 import java.awt.Color;
42 import java.awt.dnd.DropTarget;
43 import java.awt.dnd.DropTargetAdapter;
44 import java.awt.dnd.DropTargetDragEvent;
45 import java.awt.dnd.DropTargetDropEvent;
46 import java.awt.dnd.DropTargetEvent;
47 import java.awt.event.WindowListener;
48 import java.io.File;
49 import java.util.List;
50 import java.util.WeakHashMap;
52 import javax.swing.BorderFactory;
53 import javax.swing.DefaultListModel;
54 import javax.swing.JButton;
55 import javax.swing.JFrame;
56 import javax.swing.JList;
57 import javax.swing.JMenuBar;
58 import javax.swing.JMenuItem;
59 import javax.swing.JPanel;
60 import javax.swing.JSplitPane;
61 import javax.swing.JTabbedPane;
62 import javax.swing.JTextArea;
63 import javax.swing.JTextField;
64 import javax.swing.JTree;
65 import javax.swing.SwingUtilities;
66 import javax.swing.WindowConstants;
67 import javax.swing.border.TitledBorder;
68 import javax.swing.event.DocumentEvent;
69 import javax.swing.event.DocumentListener;
70 import javax.swing.event.TreeSelectionEvent;
71 import javax.swing.event.TreeSelectionListener;
72 import javax.swing.tree.DefaultMutableTreeNode;
73 import javax.swing.tree.DefaultTreeModel;
74 import javax.swing.tree.TreePath;
76 import net.sourceforge.aprog.context.Context;
77 import net.sourceforge.aprog.events.Variable;
78 import net.sourceforge.aprog.events.Variable.ValueChangedEvent;
79 import net.sourceforge.aprog.i18n.Translator;
80 import net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterActions;
81 import net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterComponents;
82 import net.sourceforge.aprog.swing.SwingTools;
83 import net.sourceforge.aprog.tools.IllegalInstantiationException;
84 import net.sourceforge.jmacadapter.MacAdapterTools;
86 import org.w3c.dom.Node;
87 import org.w3c.dom.NodeList;
88 import org.w3c.dom.events.Event;
89 import org.w3c.dom.events.EventListener;
91 /**
93 * @author codistmonk (creation 2010-07-03)
95 public final class MarkupsComponents {
97 /**
98 * @throws IllegalInstantiationException To prevent instantiation
100 private MarkupsComponents() {
101 throw new IllegalInstantiationException();
106 * @param context
107 * <br>Not null
108 * <br>Input-output
109 * @return
110 * <br>Not null
111 * <br>New
113 public static final JFrame newMainFrame(final Context context) {
114 final JFrame result = new JFrame();
116 context.set(MAIN_FRAME, result);
118 result.setJMenuBar(newMenuBar(context));
119 result.add(newMainPanel(context));
120 result.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
122 result.addWindowListener(newListener(WindowListener.class, "windowClosing",
123 MarkupsActions.class, "quit", context));
125 invokeOnVariableChanged(context, FILE,
126 SubtitlesAdjusterActions.class, "updateMainFrameTitle", context);
127 invokeOnVariableChanged(context, FILE_MODIFIED,
128 SubtitlesAdjusterActions.class, "updateMainFrameTitle", context);
130 Translator.getDefaultTranslator().addListener(newListener(Translator.Listener.class, "localeChanged",
131 SwingTools.class, "packAndUpdateMinimumSize", result));
133 return packAndCenter(result);
138 * @param context
139 * <br>Not null
140 * <br>Shared
141 * @return
142 * <br>Not null
143 * <br>New
145 public static final JMenuBar newMenuBar(final Context context) {
146 checkAWT();
148 if (MacAdapterTools.isMacOSX()) {
149 MacAdapterTools.setUseScreenMenuBar(true);
152 return menuBar(
153 menu("Application",
154 newAboutMenuItem(context),
155 null,
156 newPreferencesMenuItem(context),
157 null,
158 newQuitMenuItem(context)
160 menu("File",
161 newNewMenuItem(context),
162 null,
163 newOpenMenuItem(context),
164 null,
165 newSaveMenuItem(context),
166 newSaveAsMenuItem(context)
168 menu("Edit",
169 newUndoMenuItem(context),
170 newRedoMenuItem(context),
171 null,
172 newCopyMenuItem(context),
173 newCutMenuItem(context),
174 newPasteMenuItem(context)
176 menu("Node",
177 newAppendNewNodeMenuItem(context),
178 null,
179 newMoveUpMenuItem(context),
180 newMoveDownMenuItem(context)
182 menu("Help",
183 newManualMenuItem(context)
189 * @param context
190 * <br>Not null
191 * <br>Shared
192 * @return
193 * <br>Not null
194 * <br>New
196 public static final JMenuItem newAboutMenuItem(final Context context) {
197 checkAWT();
199 return SubtitlesAdjusterComponents.newAboutMenuItem(context);
204 * @param context
205 * <br>Not null
206 * <br>Shared
207 * @return
208 * <br>Not null
209 * <br>New
211 public static final JMenuItem newPreferencesMenuItem(final Context context) {
212 checkAWT();
214 return SubtitlesAdjusterComponents.newPreferencesMenuItem(context);
219 * @param context
220 * <br>Not null
221 * <br>Shared
222 * @return
223 * <br>Not null
224 * <br>New
226 public static final JMenuItem newQuitMenuItem(final Context context) {
227 checkAWT();
229 if (MacAdapterTools.isMacOSX() && MacAdapterTools.getUseScreenMenuBar()) {
230 if (registerMacOSXApplicationListener("handleQuit",
231 MarkupsActions.class, "quit", context)) {
232 return null;
236 return item("Quit", getKeyStroke(META + " Q"),
237 MarkupsActions.class, "quit", context);
242 * @param context
243 * <br>Not null
244 * <br>Shared
245 * @return
246 * <br>Not null
247 * <br>New
249 public static final JMenuItem newNewMenuItem(final Context context) {
250 return item("New", getKeyStroke(META + " N"),
251 MarkupsActions.class, "newFile", context);
256 * @param context
257 * <br>Not null
258 * <br>Shared
259 * @return
260 * <br>Not null
261 * <br>New
263 public static final JMenuItem newOpenMenuItem(final Context context) {
264 return item("Open...", getKeyStroke(META + " O"),
265 MarkupsActions.class, "open", context);
270 * @param context
271 * <br>Not null
272 * <br>Shared
273 * @return
274 * <br>Not null
275 * <br>New
277 public static final JMenuItem newSaveMenuItem(final Context context) {
278 final JMenuItem result = item("Save", getKeyStroke(META + " S"),
279 MarkupsActions.class, "save", context);
280 final Variable<Boolean> fileModifiedVariable = context.getVariable(FILE_MODIFIED);
282 fileModifiedVariable.addListener(new Variable.Listener<Boolean>() {
284 @Override
285 public final void valueChanged(final ValueChangedEvent<Boolean, ?> event) {
286 result.setEnabled(event.getNewValue());
291 return result;
296 * @param context
297 * <br>Not null
298 * <br>Shared
299 * @return
300 * <br>Not null
301 * <br>New
303 public static final JMenuItem newSaveAsMenuItem(final Context context) {
304 final JMenuItem result = item("Save As...", getKeyStroke(META + " shift S"),
305 MarkupsActions.class, "saveAs", context);
307 return result;
312 * @param context
313 * <br>Not null
314 * <br>Shared
315 * @return
316 * <br>Not null
317 * <br>New
319 public static final JMenuItem newUndoMenuItem(final Context context) {
320 final JMenuItem result = item("Undo", getKeyStroke(META + " Z"),
321 MarkupsActions.class, "undo", context);
323 return result;
328 * @param context
329 * <br>Not null
330 * <br>Shared
331 * @return
332 * <br>Not null
333 * <br>New
335 public static final JMenuItem newRedoMenuItem(final Context context) {
336 final JMenuItem result = item("Redo", getKeyStroke(META + " Y"),
337 MarkupsActions.class, "redo", context);
339 return result;
344 * @param context
345 * <br>Not null
346 * <br>Shared
347 * @return
348 * <br>Not null
349 * <br>New
351 public static final JMenuItem newCopyMenuItem(final Context context) {
352 final JMenuItem result = item("Copy", getKeyStroke(META + " C"),
353 MarkupsActions.class, "copy", context);
355 return result;
360 * @param context
361 * <br>Not null
362 * <br>Shared
363 * @return
364 * <br>Not null
365 * <br>New
367 public static final JMenuItem newCutMenuItem(final Context context) {
368 final JMenuItem result = item("Cut", getKeyStroke(META + " X"),
369 MarkupsActions.class, "cut", context);
371 return result;
376 * @param context
377 * <br>Not null
378 * <br>Shared
379 * @return
380 * <br>Not null
381 * <br>New
383 public static final JMenuItem newPasteMenuItem(final Context context) {
384 final JMenuItem result = item("Paste", getKeyStroke(META + " V"),
385 MarkupsActions.class, "paste", context);
387 return result;
392 * @param context
393 * <br>Not null
394 * <br>Shared
395 * @return
396 * <br>Not null
397 * <br>New
399 public static final JMenuItem newAppendNewNodeMenuItem(final Context context) {
400 final JMenuItem result = item("Append New Node",
401 MarkupsActions.class, "appendNewNode", context);
403 return result;
408 * @param context
409 * <br>Not null
410 * <br>Shared
411 * @return
412 * <br>Not null
413 * <br>New
415 public static final JMenuItem newMoveUpMenuItem(final Context context) {
416 final JMenuItem result = item("Move Up",
417 MarkupsActions.class, "moveUp", context);
419 return result;
424 * @param context
425 * <br>Not null
426 * <br>Shared
427 * @return
428 * <br>Not null
429 * <br>New
431 public static final JMenuItem newMoveDownMenuItem(final Context context) {
432 final JMenuItem result = item("Move Down",
433 MarkupsActions.class, "moveDown", context);
435 return result;
440 * @param context
441 * <br>Not null
442 * <br>Shared
443 * @return
444 * <br>Not null
445 * <br>New
447 public static final JMenuItem newManualMenuItem(final Context context) {
448 return item(
449 "Manual", getKeyStroke("F1"),
450 SubtitlesAdjusterActions.class, "showManual", context);
455 * @param context
456 * <br>Not null
457 * @return
458 * <br>Not null
459 * <br>New
461 public static final JPanel newMainPanel(final Context context) {
462 final JPanel result = new JPanel(new BorderLayout());
464 result.add(newMainSplitPane(context));
466 new DropTarget(result, new DropTargetAdapter() {
468 @Override
469 public final void dragEnter(final DropTargetDragEvent event) {
470 result.setBorder(BorderFactory.createLineBorder(Color.GREEN, 2));
473 @Override
474 public final void dragExit(final DropTargetEvent event) {
475 result.setBorder(null);
478 @Override
479 public final void drop(final DropTargetDropEvent event) {
480 result.setBorder(null);
482 final List<File> files = SwingTools.getFiles(event);
484 if (!files.isEmpty() && MarkupsActions.confirm(context)) {
485 MarkupsActions.open(context, files.get(0));
491 final EventListener domListener = new EventListener() {
493 @Override
494 public final void handleEvent(final Event event) {
495 result.repaint();
500 addListener(context, DOM, new AbstractDOMListenerReattacher(domListener) {
501 // Deliberately left empty
504 return result;
509 * @param context
510 * <br>Not null
511 * @return
512 * <br>Not null
513 * <br>New
515 public static final JSplitPane newMainSplitPane(final Context context) {
516 return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
517 scrollable(newDOMTreeView(context)),
518 newContextualSplitPane(context));
523 * @param context
524 * <br>Not null
525 * @return
526 * <br>Not null
527 * <br>New
529 public static final JSplitPane newContextualSplitPane(final Context context) {
530 return new JSplitPane(JSplitPane.VERTICAL_SPLIT,
531 newNodePanel(context),
532 newXPathPanel(context));
537 * @param context
538 * <br>Not null
539 * @return
540 * <br>Not null
541 * <br>New
543 public static final JPanel newNodePanel(final Context context) {
544 final JPanel result = new JPanel(new BorderLayout());
546 result.add(newNodeNamePanel(context), BorderLayout.NORTH);
547 result.add(newNodeValuePanel(context), BorderLayout.CENTER);
549 return result;
554 * @param context
555 * <br>Not null
556 * @return
557 * <br>Not null
558 * <br>New
560 public static final JPanel newNodeNamePanel(final Context context) {
561 return newTitledPanel("Name", newNodeNameTextField(context));
566 * @param context
567 * <br>Not null
568 * @return
569 * <br>Not null
570 * <br>New
572 public static final JTextField newNodeNameTextField(final Context context) {
573 final JTextField result = new JTextField();
574 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
575 final EventListener nodeListener = new EventListener() {
577 @Override
578 public final void handleEvent(final Event event) {
579 final Node node = selectedNodeVariable.getValue();
581 result.setText(node == null ? "" : node.getNodeName());
586 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
588 @Override
589 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
590 if (event.getOldValue() != null) {
591 removeDOMEventListener(event.getOldValue(), nodeListener);
594 final Node node = event.getNewValue();
596 if (node != null) {
597 addDOMEventListener(node, nodeListener);
600 result.setEditable(node != null && set(Node.ATTRIBUTE_NODE,
601 Node.ELEMENT_NODE).contains(node.getNodeType()));
603 result.setText(node == null ? "" : node.getNodeName());
608 result.getDocument().addDocumentListener(new AbstractDocumentHandler() {
610 @Override
611 protected final void eventReceived(final DocumentEvent event) {
612 SwingUtilities.invokeLater(new Runnable() {
614 @Override
615 public final void run() {
616 final Node node = selectedNodeVariable.getValue();
617 final String text = result.getText();
619 if (node == null || node.getNodeName().equals(text)) {
620 return;
623 try {
624 rename(
625 node,
626 node.getNamespaceURI(),
627 node.getNamespaceURI() == null ? text : node.getPrefix() + ":" + text);
628 } catch (final Exception exception) {
629 exception.printStackTrace();
630 result.setText(node.getNodeName());
639 return result;
644 * @param context
645 * <br>Not null
646 * @return
647 * <br>Not null
648 * <br>New
650 public static final JPanel newNodeValuePanel(final Context context) {
651 return newTitledPanel("Value", scrollable(newNodeValueTextArea(context)));
656 * @param context
657 * <br>Not null
658 * @return
659 * <br>Not null
660 * <br>New
662 public static final JTextArea newNodeValueTextArea(final Context context) {
663 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
664 final JTextArea result = new JTextArea();
666 final EventListener nodeListener = new EventListener() {
668 @Override
669 public final void handleEvent(final Event event) {
670 final Node node = selectedNodeVariable.getValue();
672 if (event.getTarget() == node) {
673 result.setText(node == null ? "" : node.getNodeValue());
679 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
681 @Override
682 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
683 if (event.getOldValue() != null) {
684 removeDOMEventListener(event.getOldValue(), nodeListener);
687 final Node node = event.getNewValue();
689 if (node != null) {
690 addDOMEventListener(node, nodeListener);
693 result.setEditable(node != null && set(Node.ATTRIBUTE_NODE, Node.CDATA_SECTION_NODE, Node.COMMENT_NODE,
694 Node.PROCESSING_INSTRUCTION_NODE, Node.TEXT_NODE).contains(node.getNodeType()));
696 result.setText(node == null ? "" : node.getNodeValue());
701 result.getDocument().addDocumentListener(new AbstractDocumentHandler() {
703 @Override
704 protected final void eventReceived(final DocumentEvent event) {
705 SwingUtilities.invokeLater(new Runnable() {
707 @Override
708 public final void run() {
709 final Node node = selectedNodeVariable.getValue();
710 final String text = result.getText();
712 if (node == null || emptyIfNull(node.getNodeValue()).equals(emptyIfNull(text))) {
713 return;
716 try {
717 node.setNodeValue(text);
718 } catch (final Exception exception) {
719 exception.printStackTrace();
720 result.setText(node.getNodeValue());
729 return result;
734 * @param context
735 * <br>Not null
736 * @return
737 * <br>Not null
738 * <br>New
740 public static final JPanel newXPathPanel(final Context context) {
741 final JPanel result = new JPanel(new BorderLayout());
743 result.setBorder(BorderFactory.createTitledBorder(
744 BorderFactory.createEtchedBorder(),
745 "XPath",
746 TitledBorder.CENTER,
747 TitledBorder.TOP));
749 result.add(newTitledPanel("Context", newContextTextField(context)), BorderLayout.NORTH);
750 result.add(newXPathTabbedPane(context), BorderLayout.CENTER);
752 return result;
757 * @param context
758 * <br>Not null
759 * @return
760 * <br>Not null
761 * <br>New
763 public static final JTextField newContextTextField(final Context context) {
764 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
765 final JTextField result = new JTextField();
767 final EventListener nodeListener = new EventListener() {
769 @Override
770 public final void handleEvent(final Event event) {
771 final Node node = selectedNodeVariable.getValue();
773 if (event.getTarget() == node) {
774 result.setText(node == null ? "" : getIdentifyingXPath(node));
780 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
782 @Override
783 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
784 if (event.getOldValue() != null) {
785 removeDOMEventListener(event.getOldValue(), nodeListener);
788 final Node node = event.getNewValue();
790 if (node != null) {
791 addDOMEventListener(node, nodeListener);
794 result.setText(node == null ? "" : getIdentifyingXPath(node));
799 result.setEditable(false);
801 return result;
806 * @param context
807 * <br>Not null
808 * @return
809 * <br>Not null
810 * <br>New
812 public static final JTabbedPane newXPathTabbedPane(final Context context) {
813 final JTabbedPane result = new JTabbedPane();
815 result.add("List", newXPathListSplitPane(context));
816 result.add("Create", newQuasiXPathPanel(context));
818 return result;
823 * @param context
824 * <br>Not null
825 * @return
826 * <br>Not null
827 * <br>New
829 public static final JSplitPane newXPathListSplitPane(final Context context) {
830 return new JSplitPane(JSplitPane.VERTICAL_SPLIT,
831 newTitledPanel("XPath Expression", scrollable(newXPathExpressionTextArea(context))),
832 newTitledPanel("Matches", scrollable(newXPathList(context))));
837 * @param context
838 * <br>Not null
839 * @return
840 * <br>Not null
841 * <br>New
843 public static final JTextArea newXPathExpressionTextArea(final Context context) {
844 final JTextArea result = new JTextArea();
846 updateVariableOnTextChanged(context, XPATH_EXPRESSION, result);
848 highlightBackgroundOnError(context, XPATH_ERROR, result);
850 return result;
855 * @param context
856 * <br>Not null
857 * @return
858 * <br>Not null
859 * <br>New
861 public static final JList newXPathList(final Context context) {
862 final DefaultListModel model = new DefaultListModel();
863 final JList result = new JList(model);
865 addListener(context, XPATH_RESULT, new Variable.Listener<NodeList>() {
867 @Override
868 public final void valueChanged(final ValueChangedEvent<NodeList, ?> event) {
869 model.clear();
871 if (event.getNewValue() != null) {
872 for (final Node node : toList(event.getNewValue())) {
873 model.addElement(node);
880 return result;
885 * @param context
886 * <br>Not null
887 * @return
888 * <br>Not null
889 * <br>New
891 public static final JPanel newQuasiXPathPanel(final Context context) {
892 final JPanel result = new JPanel(new BorderLayout());
894 result.add(newTitledPanel("Quasi-XPath Expression", newQuasiXPathExpressionTextArea(context)), BorderLayout.CENTER);
895 result.add(newQuasiXPathCreateButton(context), BorderLayout.SOUTH);
897 return result;
902 * @param context
903 * <br>Not null
904 * @return
905 * <br>Not null
906 * <br>New
908 public static final JTextArea newQuasiXPathExpressionTextArea(final Context context) {
909 final JTextArea result = new JTextArea();
911 updateVariableOnTextChanged(context, QUASI_XPATH_EXPRESSION, result);
913 highlightBackgroundOnError(context, QUASI_XPATH_ERROR, result);
915 return result;
920 * @param context
921 * <br>Not null
922 * @return
923 * <br>Not null
924 * <br>New
926 public static final JButton newQuasiXPathCreateButton(final Context context) {
927 final JButton result = translate(new JButton("Create Node"));
929 result.addActionListener(SwingTools.action(
930 MarkupsActions.class, "evaluateQuasiXPathExpression", context));
932 return result;
937 * @param context
938 * <br>Not null
939 * @return
940 * <br>Not null
941 * <br>New
943 public static final JTree newDOMTreeView(final Context context) {
944 final Variable<Node> domVariable = context.getVariable(DOM);
945 final JTree result = new JTree(new DOMTreeModel(domVariable.getValue()));
947 domVariable.addListener(new Variable.Listener<Node>() {
949 @Override
950 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
951 result.setModel(new DOMTreeModel(event.getNewValue()));
956 result.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
958 @Override
959 public final void valueChanged(final TreeSelectionEvent event) {
960 if (event.getNewLeadSelectionPath() == null) {
961 context.set(SELECTED_NODE, null);
963 return;
966 final DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) event.getNewLeadSelectionPath().getLastPathComponent();
968 if (selectedTreeNode != null) {
969 context.set(SELECTED_NODE, (Node) selectedTreeNode.getUserObject());
975 addListener(context, SELECTED_NODE, new Variable.Listener<Node>() {
977 @Override
978 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
979 final DOMTreeModel treeModel = (DOMTreeModel) result.getModel();
980 final DOMTreeModel.DOMTreeNode treeNode = treeModel.get(event.getNewValue());
982 if (treeNode != null) {
983 result.setSelectionPath(new TreePath(treeModel.getPathToRoot(treeNode)));
984 } else {
985 result.setSelectionPath(null);
991 return result;
996 * @author codistmonk (creation 2010-07-04)
998 public static final class DOMTreeModel extends DefaultTreeModel {
1000 private final WeakHashMap<Node, DOMTreeNode> xmlTreeNodes;
1004 * @param domNode
1005 * <br>Not null
1006 * <br>Shared
1008 public DOMTreeModel(final Node domNode) {
1009 super(new DefaultMutableTreeNode());
1010 this.xmlTreeNodes = new WeakHashMap<Node, DOMTreeNode>();
1012 this.setRoot(this.new DOMTreeNode(domNode));
1017 * @return
1018 * <br>Not null
1019 * <br>Shared
1021 public final Node getDOMNode() {
1022 return (Node) ((DefaultMutableTreeNode) this.getRoot()).getUserObject();
1027 * @param domNode
1028 * <br>Not null
1029 * <br>Shared
1030 * @param xmlTreeNode
1031 * <br>Not null
1032 * <br>Shared
1034 final void put(final Node domNode, final DOMTreeNode xmlTreeNode) {
1035 this.xmlTreeNodes.put(domNode, xmlTreeNode);
1040 * @param domNode
1041 * <br>Not null
1042 * @return
1043 * <br>Maybe null
1044 * <br>Shared
1046 final DOMTreeNode get(final Node domNode) {
1047 return this.xmlTreeNodes.get(domNode);
1050 private static final long serialVersionUID = 4264388285566053331L;
1054 * @author codistmonk (creation 2010-07-10)
1056 public final class DOMTreeNode extends DefaultMutableTreeNode {
1060 * @param domNode
1061 * <br>Not null
1062 * <br>Shared
1064 public DOMTreeNode(final Node domNode) {
1065 super(domNode);
1067 this.getTreeModel().put(domNode, this);
1069 addDOMEventListener(domNode, new EventListener() {
1071 @Override
1072 public final void handleEvent(final Event event) {
1073 debugPrint("\ntype", event.getType(), "\ntarget", event.getTarget());
1074 if (event.getTarget() == domNode) {
1075 if (DOM_EVENT_NODE_REMOVED.equals(event.getType())) {
1076 DOMTreeNode.this.getTreeModel().removeNodeFromParent(DOMTreeNode.this);
1077 } else if (DOM_EVENT_NODE_INSERTED.equals(event.getType())) {
1078 final Node domParent = getNode(domNode, "..");
1079 final DOMTreeNode parent = DOMTreeNode.this.getTreeModel().get(domParent);
1080 final List<Node> siblings = getChildren(domParent);
1082 DOMTreeNode.this.getTreeModel().insertNodeInto(DOMTreeNode.this, parent, siblings.indexOf(domNode));
1083 } else {
1084 DOMTreeNode.this.refresh();
1091 for (final Node domChild : getChildren(domNode)) {
1092 this.add(new DOMTreeNode(domChild));
1096 final void refresh() {
1097 int i = 0;
1099 for (final Node domChild : getChildren(this.getDomNode())) {
1100 DOMTreeModel.this.insertNodeInto(this.getOrCreate(domChild), DOMTreeNode.this, i++);
1106 * @param domNode
1107 * <br>Not null
1108 * @return
1109 * <br>Not null
1110 * <br>Maybe new
1111 * <br>Shared
1113 private final DOMTreeNode getOrCreate(final Node domNode) {
1114 DOMTreeNode result = DOMTreeModel.this.get(domNode);
1116 if (result == null) {
1117 result = DOMTreeModel.this.new DOMTreeNode(domNode);
1120 return result;
1125 * @return
1126 * <br>Not null
1127 * <br>Shared
1129 public final Node getDomNode() {
1130 return (Node) this.getUserObject();
1135 * @return
1136 * <br>Not null
1137 * <br>Shared
1139 public final DOMTreeModel getTreeModel() {
1140 return DOMTreeModel.this;
1143 @Override
1144 public final String toString() {
1145 switch (this.getDomNode().getNodeType()) {
1146 case Node.ATTRIBUTE_NODE:
1147 return this.getDomNode().getNodeName() +
1148 (this.getDomNode().getNodeValue() == null ? "" : "=\"" + this.getDomNode().getNodeValue() + "\"");
1149 default:
1150 return this.getDomNode().getNodeName() +
1151 (this.getDomNode().getNodeValue() == null ? "" : "[" + this.getDomNode().getNodeValue() + "]");
1155 private static final long serialVersionUID = 8090552131823122052L;
1163 * @author codistmonk (creation 2010-07-07)
1165 public static abstract class AbstractDocumentHandler implements DocumentListener {
1167 @Override
1168 public final void insertUpdate(final DocumentEvent event) {
1169 this.doInsertUpdate(event);
1172 @Override
1173 public final void removeUpdate(final DocumentEvent event) {
1174 this.doRemoveUpdate(event);
1177 @Override
1178 public final void changedUpdate(final DocumentEvent event) {
1179 this.doChangedUpdate(event);
1184 * @param event
1185 * <br>Not null
1187 protected void doInsertUpdate(final DocumentEvent event) {
1188 this.eventReceived(event);
1193 * @param event
1194 * <br>Not null
1196 protected void doRemoveUpdate(final DocumentEvent event) {
1197 this.eventReceived(event);
1202 * @param event
1203 * <br>Not null
1205 protected void doChangedUpdate(final DocumentEvent event) {
1206 this.eventReceived(event);
1211 * @param event
1212 * <br>Not null
1214 protected abstract void eventReceived(DocumentEvent event);