[Markups]
[aprog.git] / Markups / src / net / sourceforge / aprog / markups / MarkupsComponents.java
blob3a992e33a111f85b06a52c5494efea574d7a0621
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.subtitlesadjuster.SubtitlesAdjusterTools.menu;
34 import static net.sourceforge.aprog.swing.SwingTools.checkAWT;
35 import static net.sourceforge.aprog.swing.SwingTools.menuBar;
36 import static net.sourceforge.aprog.swing.SwingTools.packAndCenter;
37 import static net.sourceforge.aprog.swing.SwingTools.scrollable;
38 import static net.sourceforge.aprog.tools.Tools.*;
39 import static net.sourceforge.aprog.xml.XMLTools.*;
41 import java.awt.BorderLayout;
42 import java.awt.Color;
43 import java.awt.Component;
44 import java.awt.dnd.DropTarget;
45 import java.awt.dnd.DropTargetAdapter;
46 import java.awt.dnd.DropTargetDragEvent;
47 import java.awt.dnd.DropTargetDropEvent;
48 import java.awt.dnd.DropTargetEvent;
49 import java.awt.event.ActionEvent;
50 import java.awt.event.ActionListener;
51 import java.awt.event.WindowListener;
52 import java.io.ByteArrayOutputStream;
53 import java.io.File;
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.WeakHashMap;
58 import javax.swing.BorderFactory;
59 import javax.swing.DefaultListModel;
60 import javax.swing.JButton;
61 import javax.swing.JFrame;
62 import javax.swing.JList;
63 import javax.swing.JMenuBar;
64 import javax.swing.JMenuItem;
65 import javax.swing.JPanel;
66 import javax.swing.JSplitPane;
67 import javax.swing.JTabbedPane;
68 import javax.swing.JTextArea;
69 import javax.swing.JTextField;
70 import javax.swing.JTree;
71 import javax.swing.SwingUtilities;
72 import javax.swing.border.TitledBorder;
73 import javax.swing.event.DocumentEvent;
74 import javax.swing.event.DocumentListener;
75 import javax.swing.event.TreeSelectionEvent;
76 import javax.swing.event.TreeSelectionListener;
77 import javax.swing.text.JTextComponent;
78 import javax.swing.tree.DefaultMutableTreeNode;
79 import javax.swing.tree.DefaultTreeModel;
80 import javax.swing.tree.TreeModel;
81 import javax.swing.tree.TreePath;
83 import net.sourceforge.aprog.context.Context;
84 import net.sourceforge.aprog.events.Variable;
85 import net.sourceforge.aprog.events.Variable.ValueChangedEvent;
86 import net.sourceforge.aprog.i18n.Translator;
87 import net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterActions;
88 import net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterComponents;
89 import net.sourceforge.aprog.swing.SwingTools;
90 import net.sourceforge.aprog.tools.IllegalInstantiationException;
91 import net.sourceforge.aprog.tools.Tools;
92 import net.sourceforge.aprog.xml.XMLTools;
93 import net.sourceforge.jmacadapter.MacAdapterTools;
95 import org.w3c.dom.NamedNodeMap;
96 import org.w3c.dom.Node;
97 import org.w3c.dom.NodeList;
98 import org.w3c.dom.events.Event;
99 import org.w3c.dom.events.EventListener;
100 import org.w3c.dom.events.MutationEvent;
104 * @author codistmonk (creation 2010-07-03)
106 public final class MarkupsComponents {
109 * @throws IllegalInstantiationException To prevent instantiation
111 private MarkupsComponents() {
112 throw new IllegalInstantiationException();
117 * @param context
118 * <br>Not null
119 * <br>Input-output
120 * @return
121 * <br>Not null
122 * <br>New
124 public static final JFrame newMainFrame(final Context context) {
125 final JFrame result = new JFrame();
127 context.set(MAIN_FRAME, result);
129 result.setJMenuBar(newMenuBar(context));
130 result.add(newMainPanel(context));
131 result.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
133 result.addWindowListener(newListener(WindowListener.class, "windowClosing",
134 MarkupsActions.class, "quit", context));
136 invokeOnVariableChanged(context, FILE,
137 SubtitlesAdjusterActions.class, "updateMainFrameTitle", context);
138 invokeOnVariableChanged(context, FILE_MODIFIED,
139 SubtitlesAdjusterActions.class, "updateMainFrameTitle", context);
141 Translator.getDefaultTranslator().addListener(newListener(Translator.Listener.class, "localeChanged",
142 SwingTools.class, "packAndUpdateMinimumSize", result));
144 return packAndCenter(result);
149 * @param context
150 * <br>Not null
151 * <br>Shared
152 * @return
153 * <br>Not null
154 * <br>New
156 public static final JMenuBar newMenuBar(final Context context) {
157 checkAWT();
159 if (MacAdapterTools.isMacOSX()) {
160 MacAdapterTools.setUseScreenMenuBar(true);
163 return menuBar(
164 menu("Application",
165 newAboutMenuItem(context),
166 null,
167 newPreferencesMenuItem(context),
168 null,
169 newQuitMenuItem(context)
171 menu("File",
172 newNewMenuItem(context),
173 null,
174 newOpenMenuItem(context),
175 null,
176 newSaveMenuItem(context),
177 newSaveAsMenuItem(context)
179 menu("Edit",
180 newUndoMenuItem(context),
181 newRedoMenuItem(context),
182 null,
183 newCopyMenuItem(context),
184 newCutMenuItem(context),
185 newPasteMenuItem(context)
187 menu("Node",
188 newAppendNewNodeMenuItem(context),
189 null,
190 newMoveUpMenuItem(context),
191 newMoveDownMenuItem(context)
193 menu("Help",
194 newManualMenuItem(context)
200 * @param context
201 * <br>Not null
202 * <br>Shared
203 * @return
204 * <br>Not null
205 * <br>New
207 public static final JMenuItem newAboutMenuItem(final Context context) {
208 checkAWT();
210 return SubtitlesAdjusterComponents.newAboutMenuItem(context);
215 * @param context
216 * <br>Not null
217 * <br>Shared
218 * @return
219 * <br>Not null
220 * <br>New
222 public static final JMenuItem newPreferencesMenuItem(final Context context) {
223 checkAWT();
225 return SubtitlesAdjusterComponents.newPreferencesMenuItem(context);
230 * @param context
231 * <br>Not null
232 * <br>Shared
233 * @return
234 * <br>Not null
235 * <br>New
237 public static final JMenuItem newQuitMenuItem(final Context context) {
238 checkAWT();
240 if (MacAdapterTools.isMacOSX() && MacAdapterTools.getUseScreenMenuBar()) {
241 if (registerMacOSXApplicationListener("handleQuit",
242 MarkupsActions.class, "quit", context)) {
243 return null;
247 return item("Quit", getKeyStroke(META + " Q"),
248 MarkupsActions.class, "quit", context);
253 * @param context
254 * <br>Not null
255 * <br>Shared
256 * @return
257 * <br>Not null
258 * <br>New
260 public static final JMenuItem newNewMenuItem(final Context context) {
261 return item("New", getKeyStroke(META + " N"),
262 MarkupsActions.class, "newFile", context);
267 * @param context
268 * <br>Not null
269 * <br>Shared
270 * @return
271 * <br>Not null
272 * <br>New
274 public static final JMenuItem newOpenMenuItem(final Context context) {
275 return item("Open...", getKeyStroke(META + " O"),
276 MarkupsActions.class, "open", context);
281 * @param context
282 * <br>Not null
283 * <br>Shared
284 * @return
285 * <br>Not null
286 * <br>New
288 public static final JMenuItem newSaveMenuItem(final Context context) {
289 final JMenuItem result = item("Save", getKeyStroke(META + " S"),
290 MarkupsActions.class, "save", context);
291 final Variable<Boolean> fileModifiedVariable = context.getVariable(FILE_MODIFIED);
293 fileModifiedVariable.addListener(new Variable.Listener<Boolean>() {
295 @Override
296 public final void valueChanged(final ValueChangedEvent<Boolean, ?> event) {
297 result.setEnabled(event.getNewValue());
302 return result;
307 * @param context
308 * <br>Not null
309 * <br>Shared
310 * @return
311 * <br>Not null
312 * <br>New
314 public static final JMenuItem newSaveAsMenuItem(final Context context) {
315 final JMenuItem result = item("Save As...", getKeyStroke(META + " shift S"),
316 MarkupsActions.class, "saveAs", context);
318 return result;
323 * @param context
324 * <br>Not null
325 * <br>Shared
326 * @return
327 * <br>Not null
328 * <br>New
330 public static final JMenuItem newUndoMenuItem(final Context context) {
331 final JMenuItem result = item("Undo", getKeyStroke(META + " Z"),
332 MarkupsActions.class, "undo", context);
334 return result;
339 * @param context
340 * <br>Not null
341 * <br>Shared
342 * @return
343 * <br>Not null
344 * <br>New
346 public static final JMenuItem newRedoMenuItem(final Context context) {
347 final JMenuItem result = item("Redo", getKeyStroke(META + " Y"),
348 MarkupsActions.class, "redo", context);
350 return result;
355 * @param context
356 * <br>Not null
357 * <br>Shared
358 * @return
359 * <br>Not null
360 * <br>New
362 public static final JMenuItem newCopyMenuItem(final Context context) {
363 final JMenuItem result = item("Copy", getKeyStroke(META + " C"),
364 MarkupsActions.class, "copy", context);
366 return result;
371 * @param context
372 * <br>Not null
373 * <br>Shared
374 * @return
375 * <br>Not null
376 * <br>New
378 public static final JMenuItem newCutMenuItem(final Context context) {
379 final JMenuItem result = item("Cut", getKeyStroke(META + " X"),
380 MarkupsActions.class, "cut", context);
382 return result;
387 * @param context
388 * <br>Not null
389 * <br>Shared
390 * @return
391 * <br>Not null
392 * <br>New
394 public static final JMenuItem newPasteMenuItem(final Context context) {
395 final JMenuItem result = item("Paste", getKeyStroke(META + " V"),
396 MarkupsActions.class, "paste", context);
398 return result;
403 * @param context
404 * <br>Not null
405 * <br>Shared
406 * @return
407 * <br>Not null
408 * <br>New
410 public static final JMenuItem newAppendNewNodeMenuItem(final Context context) {
411 final JMenuItem result = item("Append New Node",
412 MarkupsActions.class, "appendNewNode", context);
414 return result;
419 * @param context
420 * <br>Not null
421 * <br>Shared
422 * @return
423 * <br>Not null
424 * <br>New
426 public static final JMenuItem newMoveUpMenuItem(final Context context) {
427 final JMenuItem result = item("Move Up",
428 MarkupsActions.class, "moveUp", context);
430 return result;
435 * @param context
436 * <br>Not null
437 * <br>Shared
438 * @return
439 * <br>Not null
440 * <br>New
442 public static final JMenuItem newMoveDownMenuItem(final Context context) {
443 final JMenuItem result = item("Move Down",
444 MarkupsActions.class, "moveDown", context);
446 return result;
451 * @param context
452 * <br>Not null
453 * <br>Shared
454 * @return
455 * <br>Not null
456 * <br>New
458 public static final JMenuItem newManualMenuItem(final Context context) {
459 return item(
460 "Manual", getKeyStroke("F1"),
461 SubtitlesAdjusterActions.class, "showManual", context);
466 * @param context
467 * <br>Not null
468 * @return
469 * <br>Not null
470 * <br>New
472 public static final JPanel newMainPanel(final Context context) {
473 final JPanel result = new JPanel(new BorderLayout());
475 result.add(newMainSplitPane(context));
477 new DropTarget(result, new DropTargetAdapter() {
479 @Override
480 public final void dragEnter(final DropTargetDragEvent event) {
481 result.setBorder(BorderFactory.createLineBorder(Color.GREEN, 2));
484 @Override
485 public final void dragExit(final DropTargetEvent event) {
486 result.setBorder(null);
489 @Override
490 public final void drop(final DropTargetDropEvent event) {
491 result.setBorder(null);
493 final List<File> files = SwingTools.getFiles(event);
495 if (!files.isEmpty() && MarkupsActions.confirm(context)) {
496 MarkupsActions.open(context, files.get(0));
502 final EventListener domListener = new EventListener() {
504 @Override
505 public final void handleEvent(final Event event) {
506 result.repaint();
511 addListener(context, DOM, new AbstractDOMListenerReattacher(domListener) {
512 // Deliberately left empty
515 return result;
520 * @param context
521 * <br>Not null
522 * @return
523 * <br>Not null
524 * <br>New
526 public static final JSplitPane newMainSplitPane(final Context context) {
527 return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
528 scrollable(newDOMTreeView(context)),
529 newContextualSplitPane(context));
534 * @param context
535 * <br>Not null
536 * @return
537 * <br>Not null
538 * <br>New
540 public static final JSplitPane newContextualSplitPane(final Context context) {
541 return new JSplitPane(JSplitPane.VERTICAL_SPLIT,
542 newNodePanel(context),
543 newXPathPanel(context));
548 * @param context
549 * <br>Not null
550 * @return
551 * <br>Not null
552 * <br>New
554 public static final JPanel newNodePanel(final Context context) {
555 final JPanel result = new JPanel(new BorderLayout());
557 result.add(newNodeNamePanel(context), BorderLayout.NORTH);
558 result.add(newNodeValuePanel(context), BorderLayout.CENTER);
560 return result;
565 * @param context
566 * <br>Not null
567 * @return
568 * <br>Not null
569 * <br>New
571 public static final JPanel newNodeNamePanel(final Context context) {
572 return newTitledPanel("Name", newNodeNameTextField(context));
577 * @param context
578 * <br>Not null
579 * @return
580 * <br>Not null
581 * <br>New
583 public static final JTextField newNodeNameTextField(final Context context) {
584 final JTextField result = new JTextField();
585 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
586 final EventListener nodeListener = new EventListener() {
588 @Override
589 public final void handleEvent(final Event event) {
590 final Node node = selectedNodeVariable.getValue();
592 result.setText(node == null ? "" : node.getNodeName());
597 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
599 @Override
600 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
601 if (event.getOldValue() != null) {
602 removeDOMEventListener(event.getOldValue(), nodeListener);
605 final Node node = event.getNewValue();
607 if (node != null) {
608 addDOMEventListener(node, nodeListener);
611 result.setEditable(node != null && set(Node.ATTRIBUTE_NODE,
612 Node.ELEMENT_NODE).contains(node.getNodeType()));
614 result.setText(node == null ? "" : node.getNodeName());
619 result.getDocument().addDocumentListener(new AbstractDocumentHandler() {
621 @Override
622 protected final void eventReceived(final DocumentEvent event) {
623 SwingUtilities.invokeLater(new Runnable() {
625 @Override
626 public final void run() {
627 final Node node = selectedNodeVariable.getValue();
628 final String text = result.getText();
630 if (node == null || node.getNodeName().equals(text)) {
631 return;
634 try {
635 rename(
636 node,
637 node.getNamespaceURI(),
638 node.getNamespaceURI() == null ? text : node.getPrefix() + ":" + text);
639 } catch (final Exception exception) {
640 exception.printStackTrace();
641 result.setText(node.getNodeName());
650 return result;
655 * @param context
656 * <br>Not null
657 * @return
658 * <br>Not null
659 * <br>New
661 public static final JPanel newNodeValuePanel(final Context context) {
662 return newTitledPanel("Value", scrollable(newNodeValueTextArea(context)));
667 * @param context
668 * <br>Not null
669 * @return
670 * <br>Not null
671 * <br>New
673 public static final JTextArea newNodeValueTextArea(final Context context) {
674 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
675 final JTextArea result = new JTextArea();
677 final EventListener nodeListener = new EventListener() {
679 @Override
680 public final void handleEvent(final Event event) {
681 final Node node = selectedNodeVariable.getValue();
683 if (event.getTarget() == node) {
684 result.setText(node == null ? "" : node.getNodeValue());
690 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
692 @Override
693 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
694 if (event.getOldValue() != null) {
695 removeDOMEventListener(event.getOldValue(), nodeListener);
698 final Node node = event.getNewValue();
700 if (node != null) {
701 addDOMEventListener(node, nodeListener);
704 result.setEditable(node != null && set(Node.ATTRIBUTE_NODE, Node.CDATA_SECTION_NODE, Node.COMMENT_NODE,
705 Node.PROCESSING_INSTRUCTION_NODE, Node.TEXT_NODE).contains(node.getNodeType()));
707 result.setText(node == null ? "" : node.getNodeValue());
712 result.getDocument().addDocumentListener(new AbstractDocumentHandler() {
714 @Override
715 protected final void eventReceived(final DocumentEvent event) {
716 SwingUtilities.invokeLater(new Runnable() {
718 @Override
719 public final void run() {
720 final Node node = selectedNodeVariable.getValue();
721 final String text = result.getText();
723 if (node == null || emptyIfNull(node.getNodeValue()).equals(emptyIfNull(text))) {
724 return;
727 try {
728 node.setNodeValue(text);
729 } catch (final Exception exception) {
730 exception.printStackTrace();
731 result.setText(node.getNodeValue());
740 return result;
745 * @param context
746 * <br>Not null
747 * @return
748 * <br>Not null
749 * <br>New
751 public static final JPanel newXPathPanel(final Context context) {
752 final JPanel result = new JPanel(new BorderLayout());
754 result.setBorder(BorderFactory.createTitledBorder(
755 BorderFactory.createEtchedBorder(),
756 "XPath",
757 TitledBorder.CENTER,
758 TitledBorder.TOP));
760 result.add(newTitledPanel("Context", newContextTextField(context)), BorderLayout.NORTH);
761 result.add(newXPathTabbedPane(context), BorderLayout.CENTER);
763 return result;
768 * @param context
769 * <br>Not null
770 * @return
771 * <br>Not null
772 * <br>New
774 public static final JTextField newContextTextField(final Context context) {
775 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
776 final JTextField result = new JTextField();
778 final EventListener nodeListener = new EventListener() {
780 @Override
781 public final void handleEvent(final Event event) {
782 final Node node = selectedNodeVariable.getValue();
784 if (event.getTarget() == node) {
785 result.setText(node == null ? "" : getIdentifyingXPath(node));
791 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
793 @Override
794 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
795 if (event.getOldValue() != null) {
796 removeDOMEventListener(event.getOldValue(), nodeListener);
799 final Node node = event.getNewValue();
801 if (node != null) {
802 addDOMEventListener(node, nodeListener);
805 result.setText(node == null ? "" : getIdentifyingXPath(node));
810 result.setEditable(false);
812 return result;
817 * @param context
818 * <br>Not null
819 * @return
820 * <br>Not null
821 * <br>New
823 public static final JTabbedPane newXPathTabbedPane(final Context context) {
824 final JTabbedPane result = new JTabbedPane();
826 result.add("List", newXPathListSplitPane(context));
827 result.add("Create", newQuasiXPathPanel(context));
829 return result;
834 * @param context
835 * <br>Not null
836 * @return
837 * <br>Not null
838 * <br>New
840 public static final JSplitPane newXPathListSplitPane(final Context context) {
841 return new JSplitPane(JSplitPane.VERTICAL_SPLIT,
842 newTitledPanel("XPath Expression", scrollable(newXPathExpressionTextArea(context))),
843 newTitledPanel("Matches", scrollable(newXPathList(context))));
848 * @param context
849 * <br>Not null
850 * @return
851 * <br>Not null
852 * <br>New
854 public static final JTextArea newXPathExpressionTextArea(final Context context) {
855 final JTextArea result = new JTextArea();
857 updateVariableOnTextChanged(context, XPATH_EXPRESSION, result);
859 highlightBackgroundOnError(context, XPATH_ERROR, result);
861 return result;
866 * @param context
867 * <br>Not null
868 * @return
869 * <br>Not null
870 * <br>New
872 public static final JList newXPathList(final Context context) {
873 final DefaultListModel model = new DefaultListModel();
874 final JList result = new JList(model);
876 addListener(context, XPATH_RESULT, new Variable.Listener<NodeList>() {
878 @Override
879 public final void valueChanged(final ValueChangedEvent<NodeList, ?> event) {
880 model.clear();
882 if (event.getNewValue() != null) {
883 for (final Node node : toList(event.getNewValue())) {
884 model.addElement(node);
891 return result;
896 * @param context
897 * <br>Not null
898 * @return
899 * <br>Not null
900 * <br>New
902 public static final JPanel newQuasiXPathPanel(final Context context) {
903 final JPanel result = new JPanel(new BorderLayout());
905 result.add(newTitledPanel("Quasi-XPath Expression", newQuasiXPathExpressionTextArea(context)), BorderLayout.CENTER);
906 result.add(newQuasiXPathCreateButton(context), BorderLayout.SOUTH);
908 return result;
913 * @param context
914 * <br>Not null
915 * @return
916 * <br>Not null
917 * <br>New
919 public static final JTextArea newQuasiXPathExpressionTextArea(final Context context) {
920 final JTextArea result = new JTextArea();
922 updateVariableOnTextChanged(context, QUASI_XPATH_EXPRESSION, result);
924 highlightBackgroundOnError(context, QUASI_XPATH_ERROR, result);
926 return result;
931 * @param context
932 * <br>Not null
933 * @return
934 * <br>Not null
935 * <br>New
937 public static final JButton newQuasiXPathCreateButton(final Context context) {
938 final JButton result = translate(new JButton("Create Node"));
940 result.addActionListener(SwingTools.action(
941 MarkupsActions.class, "evaluateQuasiXPathExpression", context));
943 return result;
948 * @param context
949 * <br>Not null
950 * @return
951 * <br>Not null
952 * <br>New
954 public static final JTree newDOMTreeView(final Context context) {
955 final Variable<Node> domVariable = context.getVariable(DOM);
956 final JTree result = new JTree(new DOMTreeModel(domVariable.getValue()));
958 domVariable.addListener(new Variable.Listener<Node>() {
960 @Override
961 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
962 result.setModel(new DOMTreeModel(event.getNewValue()));
967 result.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
969 @Override
970 public final void valueChanged(final TreeSelectionEvent event) {
971 if (event.getNewLeadSelectionPath() == null) {
972 context.set(SELECTED_NODE, null);
974 return;
977 final DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) event.getNewLeadSelectionPath().getLastPathComponent();
979 if (selectedTreeNode != null) {
980 context.set(SELECTED_NODE, (Node) selectedTreeNode.getUserObject());
986 addListener(context, SELECTED_NODE, new Variable.Listener<Node>() {
988 @Override
989 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
990 final DOMTreeModel treeModel = (DOMTreeModel) result.getModel();
991 final DOMTreeModel.XMLTreeNode treeNode = treeModel.get(event.getNewValue());
993 if (treeNode != null) {
994 result.setSelectionPath(new TreePath(treeModel.getPathToRoot(treeNode)));
995 } else {
996 result.setSelectionPath(null);
1002 return result;
1007 * @author codistmonk (creation 2010-07-04)
1009 public static final class DOMTreeModel extends DefaultTreeModel {
1011 private final WeakHashMap<Node, XMLTreeNode> xmlTreeNodes;
1015 * @param domNode
1016 * <br>Not null
1017 * <br>Shared
1019 public DOMTreeModel(final Node domNode) {
1020 super(new DefaultMutableTreeNode());
1021 this.xmlTreeNodes = new WeakHashMap<Node, XMLTreeNode>();
1023 this.setRoot(this.new XMLTreeNode(domNode));
1028 * @return
1029 * <br>Not null
1030 * <br>Shared
1032 public final Node getDOMNode() {
1033 return (Node) ((DefaultMutableTreeNode) this.getRoot()).getUserObject();
1038 * @param domNode
1039 * <br>Not null
1040 * <br>Shared
1041 * @param xmlTreeNode
1042 * <br>Not null
1043 * <br>Shared
1045 final void put(final Node domNode, final XMLTreeNode xmlTreeNode) {
1046 this.xmlTreeNodes.put(domNode, xmlTreeNode);
1051 * @param domNode
1052 * <br>Not null
1053 * @return
1054 * <br>Maybe null
1055 * <br>Shared
1057 final XMLTreeNode get(final Node domNode) {
1058 return this.xmlTreeNodes.get(domNode);
1061 private static final long serialVersionUID = 4264388285566053331L;
1065 * @author codistmonk (creation 2010-07-10)
1067 public final class XMLTreeNode extends DefaultMutableTreeNode {
1071 * @param domNode
1072 * <br>Not null
1073 * <br>Shared
1075 public XMLTreeNode(final Node domNode) {
1076 super(domNode);
1078 this.getTreeModel().put(domNode, this);
1080 addDOMEventListener(domNode, new EventListener() {
1082 @Override
1083 public final void handleEvent(final Event event) {
1084 // debugPrint("\ntype", event.getType(), "\ntarget", event.getTarget());
1085 if (event.getTarget() == domNode) {
1086 if (DOM_EVENT_NODE_REMOVED.equals(event.getType())) {
1087 XMLTreeNode.this.getTreeModel().removeNodeFromParent(XMLTreeNode.this);
1089 if (DOM_EVENT_NODE_INSERTED.equals(event.getType())) {
1090 final Node domParent = getNode(domNode, "..");
1091 final XMLTreeNode parent = XMLTreeNode.this.getTreeModel().get(domParent);
1092 final List<Node> siblings = getChildren(domParent);
1094 debugPrint(parent);
1095 debugPrint(siblings);
1096 debugPrint(siblings.indexOf(domNode));
1098 XMLTreeNode.this.getTreeModel().insertNodeInto(XMLTreeNode.this, parent, siblings.indexOf(domNode));
1100 // debugPrint(event);
1101 // debugPrint("type", event.getType());
1102 // debugPrint("target", event.getTarget());
1103 // debugPrint("currentTarget", event.getCurrentTarget());
1105 // if (event instanceof MutationEvent) {
1106 // debugPrint("relatedNode", ((MutationEvent) event).getRelatedNode());
1107 // debugPrint("attrChange", ((MutationEvent) event).getAttrChange());
1108 // debugPrint("attrName", ((MutationEvent) event).getAttrName());
1109 // debugPrint("prevValue", ((MutationEvent) event).getPrevValue());
1110 // debugPrint("newValue", ((MutationEvent) event).getNewValue());
1111 // }
1112 // TODO
1118 for (final Node domChild : getChildren(domNode)) {
1119 this.add(new XMLTreeNode(domChild));
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);