[Markups]
[aprog.git] / Markups / src / net / sourceforge / aprog / markups / MarkupsComponents.java
blob6e0dddda3d309d23254e3d078052dbdaa2f1781b
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;
28 import static net.sourceforge.aprog.i18n.Messages.translate;
29 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.DOM;
30 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.FILE;
31 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.FILE_MODIFIED;
32 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.MAIN_FRAME;
33 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.QUASI_XPATH_ERROR;
34 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.QUASI_XPATH_EXPRESSION;
35 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.SELECTED_NODE;
36 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.XPATH_ERROR;
37 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.XPATH_EXPRESSION;
38 import static net.sourceforge.aprog.markups.MarkupsConstants.Variables.XPATH_RESULT;
39 import static net.sourceforge.aprog.markups.MarkupsTools.addListener;
40 import static net.sourceforge.aprog.markups.MarkupsTools.getChildren;
41 import static net.sourceforge.aprog.markups.MarkupsTools.getIdentifyingXPath;
42 import static net.sourceforge.aprog.markups.MarkupsTools.highlightBackgroundOnError;
43 import static net.sourceforge.aprog.markups.MarkupsTools.newTitledPanel;
44 import static net.sourceforge.aprog.markups.MarkupsTools.updateVariableOnTextChanged;
45 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.META;
46 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.invokeOnVariableChanged;
47 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.item;
48 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.menu;
49 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.newListener;
50 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.registerMacOSXApplicationListener;
51 import static net.sourceforge.aprog.swing.SwingTools.checkAWT;
52 import static net.sourceforge.aprog.swing.SwingTools.menuBar;
53 import static net.sourceforge.aprog.swing.SwingTools.packAndCenter;
54 import static net.sourceforge.aprog.swing.SwingTools.scrollable;
55 import static net.sourceforge.aprog.tools.Tools.debugPrint;
56 import static net.sourceforge.aprog.tools.Tools.emptyIfNull;
57 import static net.sourceforge.aprog.tools.Tools.set;
58 import static net.sourceforge.aprog.xml.XMLTools.DOM_EVENT_NODE_INSERTED;
59 import static net.sourceforge.aprog.xml.XMLTools.DOM_EVENT_NODE_REMOVED;
60 import static net.sourceforge.aprog.xml.XMLTools.addDOMEventListener;
61 import static net.sourceforge.aprog.xml.XMLTools.getNode;
62 import static net.sourceforge.aprog.xml.XMLTools.removeDOMEventListener;
63 import static net.sourceforge.aprog.xml.XMLTools.rename;
64 import static net.sourceforge.aprog.xml.XMLTools.toList;
66 import java.awt.BorderLayout;
67 import java.awt.Color;
68 import java.awt.dnd.DropTarget;
69 import java.awt.dnd.DropTargetAdapter;
70 import java.awt.dnd.DropTargetDragEvent;
71 import java.awt.dnd.DropTargetDropEvent;
72 import java.awt.dnd.DropTargetEvent;
73 import java.awt.event.WindowListener;
74 import java.io.File;
75 import java.util.List;
76 import java.util.WeakHashMap;
78 import javax.swing.BorderFactory;
79 import javax.swing.DefaultListModel;
80 import javax.swing.JButton;
81 import javax.swing.JFrame;
82 import javax.swing.JList;
83 import javax.swing.JMenuBar;
84 import javax.swing.JMenuItem;
85 import javax.swing.JPanel;
86 import javax.swing.JSplitPane;
87 import javax.swing.JTabbedPane;
88 import javax.swing.JTextArea;
89 import javax.swing.JTextField;
90 import javax.swing.JTree;
91 import javax.swing.SwingUtilities;
92 import javax.swing.WindowConstants;
93 import javax.swing.border.TitledBorder;
94 import javax.swing.event.DocumentEvent;
95 import javax.swing.event.DocumentListener;
96 import javax.swing.event.TreeSelectionEvent;
97 import javax.swing.event.TreeSelectionListener;
98 import javax.swing.tree.DefaultMutableTreeNode;
99 import javax.swing.tree.DefaultTreeModel;
100 import javax.swing.tree.TreePath;
102 import net.sourceforge.aprog.af.MacOSXTools;
103 import net.sourceforge.aprog.context.Context;
104 import net.sourceforge.aprog.events.Variable;
105 import net.sourceforge.aprog.events.Variable.ValueChangedEvent;
106 import net.sourceforge.aprog.i18n.Translator;
107 import net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterActions;
108 import net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterComponents;
109 import net.sourceforge.aprog.swing.SwingTools;
110 import net.sourceforge.aprog.tools.IllegalInstantiationException;
112 import org.w3c.dom.Node;
113 import org.w3c.dom.NodeList;
114 import org.w3c.dom.events.Event;
115 import org.w3c.dom.events.EventListener;
119 * @author codistmonk (creation 2010-07-03)
121 public final class MarkupsComponents {
124 * @throws IllegalInstantiationException To prevent instantiation
126 private MarkupsComponents() {
127 throw new IllegalInstantiationException();
132 * @param context
133 * <br>Not null
134 * <br>Input-output
135 * @return
136 * <br>Not null
137 * <br>New
139 public static final JFrame newMainFrame(final Context context) {
140 final JFrame result = new JFrame();
142 context.set(MAIN_FRAME, result);
144 result.setJMenuBar(newMenuBar(context));
145 result.add(newMainPanel(context));
146 result.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
148 result.addWindowListener(newListener(WindowListener.class, "windowClosing",
149 MarkupsActions.class, "quit", context));
151 invokeOnVariableChanged(context, FILE,
152 SubtitlesAdjusterActions.class, "updateMainFrameTitle", context);
153 invokeOnVariableChanged(context, FILE_MODIFIED,
154 SubtitlesAdjusterActions.class, "updateMainFrameTitle", context);
156 Translator.getDefaultTranslator().addListener(newListener(Translator.Listener.class, "localeChanged",
157 SwingTools.class, "packAndUpdateMinimumSize", result));
159 return packAndCenter(result);
164 * @param context
165 * <br>Not null
166 * <br>Shared
167 * @return
168 * <br>Not null
169 * <br>New
171 public static final JMenuBar newMenuBar(final Context context) {
172 checkAWT();
174 if (MacOSXTools.MAC_OS_X) {
175 MacOSXTools.setUseScreenMenuBar(true);
178 return menuBar(
179 menu("Application",
180 newAboutMenuItem(context),
181 null,
182 newPreferencesMenuItem(context),
183 null,
184 newQuitMenuItem(context)
186 menu("File",
187 newNewMenuItem(context),
188 null,
189 newOpenMenuItem(context),
190 null,
191 newSaveMenuItem(context),
192 newSaveAsMenuItem(context)
194 menu("Edit",
195 newUndoMenuItem(context),
196 newRedoMenuItem(context),
197 null,
198 newCopyMenuItem(context),
199 newCutMenuItem(context),
200 newPasteMenuItem(context)
202 menu("Node",
203 newAppendNewNodeMenuItem(context),
204 null,
205 newMoveUpMenuItem(context),
206 newMoveDownMenuItem(context)
208 menu("Help",
209 newManualMenuItem(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 newAboutMenuItem(final Context context) {
223 checkAWT();
225 return SubtitlesAdjusterComponents.newAboutMenuItem(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 newPreferencesMenuItem(final Context context) {
238 checkAWT();
240 return SubtitlesAdjusterComponents.newPreferencesMenuItem(context);
245 * @param context
246 * <br>Not null
247 * <br>Shared
248 * @return
249 * <br>Not null
250 * <br>New
252 public static final JMenuItem newQuitMenuItem(final Context context) {
253 checkAWT();
255 if (MacOSXTools.MAC_OS_X && MacOSXTools.getUseScreenMenuBar()) {
256 if (registerMacOSXApplicationListener("handleQuit",
257 MarkupsActions.class, "quit", context)) {
258 return null;
262 return item("Quit", getKeyStroke(META + " Q"),
263 MarkupsActions.class, "quit", context);
268 * @param context
269 * <br>Not null
270 * <br>Shared
271 * @return
272 * <br>Not null
273 * <br>New
275 public static final JMenuItem newNewMenuItem(final Context context) {
276 return item("New", getKeyStroke(META + " N"),
277 MarkupsActions.class, "newFile", context);
282 * @param context
283 * <br>Not null
284 * <br>Shared
285 * @return
286 * <br>Not null
287 * <br>New
289 public static final JMenuItem newOpenMenuItem(final Context context) {
290 return item("Open...", getKeyStroke(META + " O"),
291 MarkupsActions.class, "open", context);
296 * @param context
297 * <br>Not null
298 * <br>Shared
299 * @return
300 * <br>Not null
301 * <br>New
303 public static final JMenuItem newSaveMenuItem(final Context context) {
304 final JMenuItem result = item("Save", getKeyStroke(META + " S"),
305 MarkupsActions.class, "save", context);
306 final Variable<Boolean> fileModifiedVariable = context.getVariable(FILE_MODIFIED);
308 fileModifiedVariable.addListener(new Variable.Listener<Boolean>() {
310 @Override
311 public final void valueChanged(final ValueChangedEvent<Boolean, ?> event) {
312 result.setEnabled(event.getNewValue());
317 return result;
322 * @param context
323 * <br>Not null
324 * <br>Shared
325 * @return
326 * <br>Not null
327 * <br>New
329 public static final JMenuItem newSaveAsMenuItem(final Context context) {
330 final JMenuItem result = item("Save As...", getKeyStroke(META + " shift S"),
331 MarkupsActions.class, "saveAs", context);
333 return result;
338 * @param context
339 * <br>Not null
340 * <br>Shared
341 * @return
342 * <br>Not null
343 * <br>New
345 public static final JMenuItem newUndoMenuItem(final Context context) {
346 final JMenuItem result = item("Undo", getKeyStroke(META + " Z"),
347 MarkupsActions.class, "undo", context);
349 return result;
354 * @param context
355 * <br>Not null
356 * <br>Shared
357 * @return
358 * <br>Not null
359 * <br>New
361 public static final JMenuItem newRedoMenuItem(final Context context) {
362 final JMenuItem result = item("Redo", getKeyStroke(META + " Y"),
363 MarkupsActions.class, "redo", context);
365 return result;
370 * @param context
371 * <br>Not null
372 * <br>Shared
373 * @return
374 * <br>Not null
375 * <br>New
377 public static final JMenuItem newCopyMenuItem(final Context context) {
378 final JMenuItem result = item("Copy", getKeyStroke(META + " C"),
379 MarkupsActions.class, "copy", context);
381 return result;
386 * @param context
387 * <br>Not null
388 * <br>Shared
389 * @return
390 * <br>Not null
391 * <br>New
393 public static final JMenuItem newCutMenuItem(final Context context) {
394 final JMenuItem result = item("Cut", getKeyStroke(META + " X"),
395 MarkupsActions.class, "cut", context);
397 return result;
402 * @param context
403 * <br>Not null
404 * <br>Shared
405 * @return
406 * <br>Not null
407 * <br>New
409 public static final JMenuItem newPasteMenuItem(final Context context) {
410 final JMenuItem result = item("Paste", getKeyStroke(META + " V"),
411 MarkupsActions.class, "paste", context);
413 return result;
418 * @param context
419 * <br>Not null
420 * <br>Shared
421 * @return
422 * <br>Not null
423 * <br>New
425 public static final JMenuItem newAppendNewNodeMenuItem(final Context context) {
426 final JMenuItem result = item("Append New Node",
427 MarkupsActions.class, "appendNewNode", context);
429 return result;
434 * @param context
435 * <br>Not null
436 * <br>Shared
437 * @return
438 * <br>Not null
439 * <br>New
441 public static final JMenuItem newMoveUpMenuItem(final Context context) {
442 final JMenuItem result = item("Move Up",
443 MarkupsActions.class, "moveUp", context);
445 return result;
450 * @param context
451 * <br>Not null
452 * <br>Shared
453 * @return
454 * <br>Not null
455 * <br>New
457 public static final JMenuItem newMoveDownMenuItem(final Context context) {
458 final JMenuItem result = item("Move Down",
459 MarkupsActions.class, "moveDown", context);
461 return result;
466 * @param context
467 * <br>Not null
468 * <br>Shared
469 * @return
470 * <br>Not null
471 * <br>New
473 public static final JMenuItem newManualMenuItem(final Context context) {
474 return item(
475 "Manual", getKeyStroke("F1"),
476 SubtitlesAdjusterActions.class, "showManual", context);
481 * @param context
482 * <br>Not null
483 * @return
484 * <br>Not null
485 * <br>New
487 public static final JPanel newMainPanel(final Context context) {
488 final JPanel result = new JPanel(new BorderLayout());
490 result.add(newMainSplitPane(context));
492 new DropTarget(result, new DropTargetAdapter() {
494 @Override
495 public final void dragEnter(final DropTargetDragEvent event) {
496 result.setBorder(BorderFactory.createLineBorder(Color.GREEN, 2));
499 @Override
500 public final void dragExit(final DropTargetEvent event) {
501 result.setBorder(null);
504 @Override
505 public final void drop(final DropTargetDropEvent event) {
506 result.setBorder(null);
508 final List<File> files = SwingTools.getFiles(event);
510 if (!files.isEmpty() && MarkupsActions.confirm(context)) {
511 MarkupsActions.open(context, files.get(0));
517 final EventListener domListener = new EventListener() {
519 @Override
520 public final void handleEvent(final Event event) {
521 result.repaint();
526 addListener(context, DOM, new AbstractDOMListenerReattacher(domListener) {
527 // Deliberately left empty
530 return result;
535 * @param context
536 * <br>Not null
537 * @return
538 * <br>Not null
539 * <br>New
541 public static final JSplitPane newMainSplitPane(final Context context) {
542 return new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
543 scrollable(newDOMTreeView(context)),
544 newContextualSplitPane(context));
549 * @param context
550 * <br>Not null
551 * @return
552 * <br>Not null
553 * <br>New
555 public static final JSplitPane newContextualSplitPane(final Context context) {
556 return new JSplitPane(JSplitPane.VERTICAL_SPLIT,
557 newNodePanel(context),
558 newXPathPanel(context));
563 * @param context
564 * <br>Not null
565 * @return
566 * <br>Not null
567 * <br>New
569 public static final JPanel newNodePanel(final Context context) {
570 final JPanel result = new JPanel(new BorderLayout());
572 result.add(newNodeNamePanel(context), BorderLayout.NORTH);
573 result.add(newNodeValuePanel(context), BorderLayout.CENTER);
575 return result;
580 * @param context
581 * <br>Not null
582 * @return
583 * <br>Not null
584 * <br>New
586 public static final JPanel newNodeNamePanel(final Context context) {
587 return newTitledPanel("Name", newNodeNameTextField(context));
592 * @param context
593 * <br>Not null
594 * @return
595 * <br>Not null
596 * <br>New
598 public static final JTextField newNodeNameTextField(final Context context) {
599 final JTextField result = new JTextField();
600 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
601 final EventListener nodeListener = new EventListener() {
603 @Override
604 public final void handleEvent(final Event event) {
605 final Node node = selectedNodeVariable.getValue();
607 result.setText(node == null ? "" : node.getNodeName());
612 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
614 @Override
615 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
616 if (event.getOldValue() != null) {
617 removeDOMEventListener(event.getOldValue(), nodeListener);
620 final Node node = event.getNewValue();
622 if (node != null) {
623 addDOMEventListener(node, nodeListener);
626 result.setEditable(node != null && set(Node.ATTRIBUTE_NODE,
627 Node.ELEMENT_NODE).contains(node.getNodeType()));
629 result.setText(node == null ? "" : node.getNodeName());
634 result.getDocument().addDocumentListener(new AbstractDocumentHandler() {
636 @Override
637 protected final void eventReceived(final DocumentEvent event) {
638 SwingUtilities.invokeLater(new Runnable() {
640 @Override
641 public final void run() {
642 final Node node = selectedNodeVariable.getValue();
643 final String text = result.getText();
645 if (node == null || node.getNodeName().equals(text)) {
646 return;
649 try {
650 rename(
651 node,
652 node.getNamespaceURI(),
653 node.getNamespaceURI() == null ? text : node.getPrefix() + ":" + text);
654 } catch (final Exception exception) {
655 exception.printStackTrace();
656 result.setText(node.getNodeName());
665 return result;
670 * @param context
671 * <br>Not null
672 * @return
673 * <br>Not null
674 * <br>New
676 public static final JPanel newNodeValuePanel(final Context context) {
677 return newTitledPanel("Value", scrollable(newNodeValueTextArea(context)));
682 * @param context
683 * <br>Not null
684 * @return
685 * <br>Not null
686 * <br>New
688 public static final JTextArea newNodeValueTextArea(final Context context) {
689 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
690 final JTextArea result = new JTextArea();
692 final EventListener nodeListener = new EventListener() {
694 @Override
695 public final void handleEvent(final Event event) {
696 final Node node = selectedNodeVariable.getValue();
698 if (event.getTarget() == node) {
699 result.setText(node == null ? "" : node.getNodeValue());
705 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
707 @Override
708 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
709 if (event.getOldValue() != null) {
710 removeDOMEventListener(event.getOldValue(), nodeListener);
713 final Node node = event.getNewValue();
715 if (node != null) {
716 addDOMEventListener(node, nodeListener);
719 result.setEditable(node != null && set(Node.ATTRIBUTE_NODE, Node.CDATA_SECTION_NODE, Node.COMMENT_NODE,
720 Node.PROCESSING_INSTRUCTION_NODE, Node.TEXT_NODE).contains(node.getNodeType()));
722 result.setText(node == null ? "" : node.getNodeValue());
727 result.getDocument().addDocumentListener(new AbstractDocumentHandler() {
729 @Override
730 protected final void eventReceived(final DocumentEvent event) {
731 SwingUtilities.invokeLater(new Runnable() {
733 @Override
734 public final void run() {
735 final Node node = selectedNodeVariable.getValue();
736 final String text = result.getText();
738 if (node == null || emptyIfNull(node.getNodeValue()).equals(emptyIfNull(text))) {
739 return;
742 try {
743 node.setNodeValue(text);
744 } catch (final Exception exception) {
745 exception.printStackTrace();
746 result.setText(node.getNodeValue());
755 return result;
760 * @param context
761 * <br>Not null
762 * @return
763 * <br>Not null
764 * <br>New
766 public static final JPanel newXPathPanel(final Context context) {
767 final JPanel result = new JPanel(new BorderLayout());
769 result.setBorder(BorderFactory.createTitledBorder(
770 BorderFactory.createEtchedBorder(),
771 "XPath",
772 TitledBorder.CENTER,
773 TitledBorder.TOP));
775 result.add(newTitledPanel("Context", newContextTextField(context)), BorderLayout.NORTH);
776 result.add(newXPathTabbedPane(context), BorderLayout.CENTER);
778 return result;
783 * @param context
784 * <br>Not null
785 * @return
786 * <br>Not null
787 * <br>New
789 public static final JTextField newContextTextField(final Context context) {
790 final Variable<Node> selectedNodeVariable = context.getVariable(SELECTED_NODE);
791 final JTextField result = new JTextField();
793 final EventListener nodeListener = new EventListener() {
795 @Override
796 public final void handleEvent(final Event event) {
797 final Node node = selectedNodeVariable.getValue();
799 if (event.getTarget() == node) {
800 result.setText(node == null ? "" : getIdentifyingXPath(node));
806 selectedNodeVariable.addListener(new Variable.Listener<Node>() {
808 @Override
809 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
810 if (event.getOldValue() != null) {
811 removeDOMEventListener(event.getOldValue(), nodeListener);
814 final Node node = event.getNewValue();
816 if (node != null) {
817 addDOMEventListener(node, nodeListener);
820 result.setText(node == null ? "" : getIdentifyingXPath(node));
825 result.setEditable(false);
827 return result;
832 * @param context
833 * <br>Not null
834 * @return
835 * <br>Not null
836 * <br>New
838 public static final JTabbedPane newXPathTabbedPane(final Context context) {
839 final JTabbedPane result = new JTabbedPane();
841 result.add("List", newXPathListSplitPane(context));
842 result.add("Create", newQuasiXPathPanel(context));
844 return result;
849 * @param context
850 * <br>Not null
851 * @return
852 * <br>Not null
853 * <br>New
855 public static final JSplitPane newXPathListSplitPane(final Context context) {
856 return new JSplitPane(JSplitPane.VERTICAL_SPLIT,
857 newTitledPanel("XPath Expression", scrollable(newXPathExpressionTextArea(context))),
858 newTitledPanel("Matches", scrollable(newXPathList(context))));
863 * @param context
864 * <br>Not null
865 * @return
866 * <br>Not null
867 * <br>New
869 public static final JTextArea newXPathExpressionTextArea(final Context context) {
870 final JTextArea result = new JTextArea();
872 updateVariableOnTextChanged(context, XPATH_EXPRESSION, result);
874 highlightBackgroundOnError(context, XPATH_ERROR, result);
876 return result;
881 * @param context
882 * <br>Not null
883 * @return
884 * <br>Not null
885 * <br>New
887 public static final JList newXPathList(final Context context) {
888 final DefaultListModel model = new DefaultListModel();
889 final JList result = new JList(model);
891 addListener(context, XPATH_RESULT, new Variable.Listener<NodeList>() {
893 @Override
894 public final void valueChanged(final ValueChangedEvent<NodeList, ?> event) {
895 model.clear();
897 if (event.getNewValue() != null) {
898 for (final Node node : toList(event.getNewValue())) {
899 model.addElement(node);
906 return result;
911 * @param context
912 * <br>Not null
913 * @return
914 * <br>Not null
915 * <br>New
917 public static final JPanel newQuasiXPathPanel(final Context context) {
918 final JPanel result = new JPanel(new BorderLayout());
920 result.add(newTitledPanel("Quasi-XPath Expression", newQuasiXPathExpressionTextArea(context)), BorderLayout.CENTER);
921 result.add(newQuasiXPathCreateButton(context), BorderLayout.SOUTH);
923 return result;
928 * @param context
929 * <br>Not null
930 * @return
931 * <br>Not null
932 * <br>New
934 public static final JTextArea newQuasiXPathExpressionTextArea(final Context context) {
935 final JTextArea result = new JTextArea();
937 updateVariableOnTextChanged(context, QUASI_XPATH_EXPRESSION, result);
939 highlightBackgroundOnError(context, QUASI_XPATH_ERROR, result);
941 return result;
946 * @param context
947 * <br>Not null
948 * @return
949 * <br>Not null
950 * <br>New
952 public static final JButton newQuasiXPathCreateButton(final Context context) {
953 final JButton result = translate(new JButton("Create Node"));
955 result.addActionListener(SwingTools.action(
956 MarkupsActions.class, "evaluateQuasiXPathExpression", context));
958 return result;
963 * @param context
964 * <br>Not null
965 * @return
966 * <br>Not null
967 * <br>New
969 public static final JTree newDOMTreeView(final Context context) {
970 final Variable<Node> domVariable = context.getVariable(DOM);
971 final JTree result = new JTree(new DOMTreeModel(domVariable.getValue()));
973 domVariable.addListener(new Variable.Listener<Node>() {
975 @Override
976 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
977 result.setModel(new DOMTreeModel(event.getNewValue()));
982 result.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
984 @Override
985 public final void valueChanged(final TreeSelectionEvent event) {
986 if (event.getNewLeadSelectionPath() == null) {
987 context.set(SELECTED_NODE, null);
989 return;
992 final DefaultMutableTreeNode selectedTreeNode = (DefaultMutableTreeNode) event.getNewLeadSelectionPath().getLastPathComponent();
994 if (selectedTreeNode != null) {
995 context.set(SELECTED_NODE, (Node) selectedTreeNode.getUserObject());
1001 addListener(context, SELECTED_NODE, new Variable.Listener<Node>() {
1003 @Override
1004 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
1005 final DOMTreeModel treeModel = (DOMTreeModel) result.getModel();
1006 final DOMTreeModel.DOMTreeNode treeNode = treeModel.get(event.getNewValue());
1008 if (treeNode != null) {
1009 result.setSelectionPath(new TreePath(treeModel.getPathToRoot(treeNode)));
1010 } else {
1011 result.setSelectionPath(null);
1017 return result;
1022 * @author codistmonk (creation 2010-07-04)
1024 public static final class DOMTreeModel extends DefaultTreeModel {
1026 private final WeakHashMap<Node, DOMTreeNode> xmlTreeNodes;
1030 * @param domNode
1031 * <br>Not null
1032 * <br>Shared
1034 public DOMTreeModel(final Node domNode) {
1035 super(new DefaultMutableTreeNode());
1036 this.xmlTreeNodes = new WeakHashMap<Node, DOMTreeNode>();
1038 this.setRoot(this.new DOMTreeNode(domNode));
1043 * @return
1044 * <br>Not null
1045 * <br>Shared
1047 public final Node getDOMNode() {
1048 return (Node) ((DefaultMutableTreeNode) this.getRoot()).getUserObject();
1053 * @param domNode
1054 * <br>Not null
1055 * <br>Shared
1056 * @param xmlTreeNode
1057 * <br>Not null
1058 * <br>Shared
1060 final void put(final Node domNode, final DOMTreeNode xmlTreeNode) {
1061 this.xmlTreeNodes.put(domNode, xmlTreeNode);
1066 * @param domNode
1067 * <br>Not null
1068 * @return
1069 * <br>Maybe null
1070 * <br>Shared
1072 final DOMTreeNode get(final Node domNode) {
1073 return this.xmlTreeNodes.get(domNode);
1076 private static final long serialVersionUID = 4264388285566053331L;
1080 * @author codistmonk (creation 2010-07-10)
1082 public final class DOMTreeNode extends DefaultMutableTreeNode {
1086 * @param domNode
1087 * <br>Not null
1088 * <br>Shared
1090 public DOMTreeNode(final Node domNode) {
1091 super(domNode);
1093 this.getTreeModel().put(domNode, this);
1095 addDOMEventListener(domNode, new EventListener() {
1097 @Override
1098 public final void handleEvent(final Event event) {
1099 debugPrint("\ntype", event.getType(), "\ntarget", event.getTarget());
1100 if (event.getTarget() == domNode) {
1101 if (DOM_EVENT_NODE_REMOVED.equals(event.getType())) {
1102 DOMTreeNode.this.getTreeModel().removeNodeFromParent(DOMTreeNode.this);
1103 } else if (DOM_EVENT_NODE_INSERTED.equals(event.getType())) {
1104 final Node domParent = getNode(domNode, "..");
1105 final DOMTreeNode parent = DOMTreeNode.this.getTreeModel().get(domParent);
1106 final List<Node> siblings = getChildren(domParent);
1108 DOMTreeNode.this.getTreeModel().insertNodeInto(DOMTreeNode.this, parent, siblings.indexOf(domNode));
1109 } else {
1110 DOMTreeNode.this.refresh();
1117 for (final Node domChild : getChildren(domNode)) {
1118 this.add(new DOMTreeNode(domChild));
1122 final void refresh() {
1123 int i = 0;
1125 for (final Node domChild : getChildren(this.getDomNode())) {
1126 DOMTreeModel.this.insertNodeInto(this.getOrCreate(domChild), DOMTreeNode.this, i++);
1132 * @param domNode
1133 * <br>Not null
1134 * @return
1135 * <br>Not null
1136 * <br>Maybe new
1137 * <br>Shared
1139 private final DOMTreeNode getOrCreate(final Node domNode) {
1140 DOMTreeNode result = DOMTreeModel.this.get(domNode);
1142 if (result == null) {
1143 result = DOMTreeModel.this.new DOMTreeNode(domNode);
1146 return result;
1151 * @return
1152 * <br>Not null
1153 * <br>Shared
1155 public final Node getDomNode() {
1156 return (Node) this.getUserObject();
1161 * @return
1162 * <br>Not null
1163 * <br>Shared
1165 public final DOMTreeModel getTreeModel() {
1166 return DOMTreeModel.this;
1169 @Override
1170 public final String toString() {
1171 switch (this.getDomNode().getNodeType()) {
1172 case Node.ATTRIBUTE_NODE:
1173 return this.getDomNode().getNodeName() +
1174 (this.getDomNode().getNodeValue() == null ? "" : "=\"" + this.getDomNode().getNodeValue() + "\"");
1175 default:
1176 return this.getDomNode().getNodeName() +
1177 (this.getDomNode().getNodeValue() == null ? "" : "[" + this.getDomNode().getNodeValue() + "]");
1181 private static final long serialVersionUID = 8090552131823122052L;
1189 * @author codistmonk (creation 2010-07-07)
1191 public static abstract class AbstractDocumentHandler implements DocumentListener {
1193 @Override
1194 public final void insertUpdate(final DocumentEvent event) {
1195 this.doInsertUpdate(event);
1198 @Override
1199 public final void removeUpdate(final DocumentEvent event) {
1200 this.doRemoveUpdate(event);
1203 @Override
1204 public final void changedUpdate(final DocumentEvent event) {
1205 this.doChangedUpdate(event);
1210 * @param event
1211 * <br>Not null
1213 protected void doInsertUpdate(final DocumentEvent event) {
1214 this.eventReceived(event);
1219 * @param event
1220 * <br>Not null
1222 protected void doRemoveUpdate(final DocumentEvent event) {
1223 this.eventReceived(event);
1228 * @param event
1229 * <br>Not null
1231 protected void doChangedUpdate(final DocumentEvent event) {
1232 this.eventReceived(event);
1237 * @param event
1238 * <br>Not null
1240 protected abstract void eventReceived(DocumentEvent event);