[Markups]
[aprog.git] / Markups / src / net / sourceforge / aprog / markups / Components.java
blob40a7733bffb15d279be7ac00ed9fac8582d035a5
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.markups.Constants.Variables.*;
30 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.*;
31 import static net.sourceforge.aprog.subtitlesadjuster.SubtitlesAdjusterTools.menu;
32 import static net.sourceforge.aprog.swing.SwingTools.checkAWT;
33 import static net.sourceforge.aprog.swing.SwingTools.menuBar;
34 import static net.sourceforge.aprog.swing.SwingTools.packAndCenter;
35 import static net.sourceforge.aprog.swing.SwingTools.scrollable;
36 import static net.sourceforge.aprog.tools.Tools.*;
38 import java.awt.BorderLayout;
39 import java.awt.CardLayout;
40 import java.awt.Color;
41 import java.awt.dnd.DropTarget;
42 import java.awt.dnd.DropTargetAdapter;
43 import java.awt.dnd.DropTargetDragEvent;
44 import java.awt.dnd.DropTargetDropEvent;
45 import java.awt.dnd.DropTargetEvent;
46 import java.awt.event.WindowListener;
47 import java.io.ByteArrayOutputStream;
48 import java.io.File;
49 import java.util.List;
51 import javax.swing.BorderFactory;
52 import javax.swing.ButtonGroup;
53 import javax.swing.JFrame;
54 import javax.swing.JMenuBar;
55 import javax.swing.JMenuItem;
56 import javax.swing.JPanel;
57 import javax.swing.JRadioButtonMenuItem;
58 import javax.swing.JTextPane;
59 import javax.swing.JTree;
60 import javax.swing.SwingUtilities;
61 import javax.swing.event.DocumentEvent;
62 import javax.swing.event.DocumentListener;
63 import javax.swing.tree.DefaultMutableTreeNode;
64 import javax.swing.tree.DefaultTreeModel;
66 import net.sourceforge.aprog.context.Context;
67 import net.sourceforge.aprog.events.Variable;
68 import net.sourceforge.aprog.events.Variable.ValueChangedEvent;
69 import net.sourceforge.aprog.i18n.Messages;
70 import net.sourceforge.aprog.i18n.Translator;
71 import net.sourceforge.aprog.swing.SwingTools;
72 import net.sourceforge.aprog.tools.IllegalInstantiationException;
73 import net.sourceforge.aprog.xml.XMLTools;
74 import net.sourceforge.jmacadapter.MacAdapterTools;
76 import org.w3c.dom.NamedNodeMap;
77 import org.w3c.dom.Node;
79 /**
81 * @author codistmonk (creation 2010-07-03)
83 public final class Components {
85 /**
86 * @throws IllegalInstantiationException To prevent instantiation
88 private Components() {
89 throw new IllegalInstantiationException();
92 /**
94 * @param context
95 * <br>Not null
96 * <br>Input-output
97 * @return
98 * <br>Not null
99 * <br>New
101 public static final JFrame newMainFrame(final Context context) {
102 final JFrame result = new JFrame();
104 context.set(MAIN_FRAME, result);
106 result.setJMenuBar(newMenuBar(context));
107 result.add(newMainPanel(context));
109 result.addWindowListener(newListener(WindowListener.class, "windowClosing",
110 Actions.class, "quit", context));
112 invokeOnVariableChanged(context, FILE,
113 net.sourceforge.aprog.subtitlesadjuster.Actions.class, "updateMainFrameTitle", context);
114 invokeOnVariableChanged(context, FILE_MODIFIED,
115 net.sourceforge.aprog.subtitlesadjuster.Actions.class, "updateMainFrameTitle", context);
117 Translator.getDefaultTranslator().addListener(newListener(Translator.Listener.class, "localeChanged",
118 SwingTools.class, "packAndUpdateMinimumSize", result));
120 return packAndCenter(result);
125 * @param context
126 * <br>Not null
127 * <br>Shared
128 * @return
129 * <br>Not null
130 * <br>New
132 public static final JMenuBar newMenuBar(final Context context) {
133 checkAWT();
135 if (MacAdapterTools.isMacOSX()) {
136 MacAdapterTools.setUseScreenMenuBar(true);
139 return menuBar(
140 menu("Application",
141 newAboutMenuItem(context),
142 null,
143 newPreferencesMenuItem(context),
144 null,
145 newQuitMenuItem(context)
147 menu("File",
148 newNewMenuItem(context),
149 null,
150 newOpenMenuItem(context),
151 null,
152 newSaveMenuItem(context),
153 newSaveAsMenuItem(context)
155 menu("Edit",
156 newUndoMenuItem(context),
157 newRedoMenuItem(context),
158 null,
159 newCopyMenuItem(context),
160 newCutMenuItem(context),
161 newPasteMenuItem(context)
163 menu("View",
164 newTreeMenuItem(context),
165 newTextMenuItem(context)
167 menu("Help",
168 newManualMenuItem(context)
174 * @param context
175 * <br>Not null
176 * <br>Shared
177 * @return
178 * <br>Not null
179 * <br>New
181 public static final JMenuItem newAboutMenuItem(final Context context) {
182 checkAWT();
184 return net.sourceforge.aprog.subtitlesadjuster.Components.newAboutMenuItem(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 newPreferencesMenuItem(final Context context) {
197 checkAWT();
199 return net.sourceforge.aprog.subtitlesadjuster.Components.newPreferencesMenuItem(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 newQuitMenuItem(final Context context) {
212 checkAWT();
214 if (MacAdapterTools.isMacOSX() && MacAdapterTools.getUseScreenMenuBar()) {
215 if (registerMacOSXApplicationListener("handleQuit",
216 Actions.class, "quit", context)) {
217 return null;
221 return item("Quit", getKeyStroke(META + " Q"),
222 Actions.class, "quit", context);
227 * @param context
228 * <br>Not null
229 * <br>Shared
230 * @return
231 * <br>Not null
232 * <br>New
234 public static final JMenuItem newNewMenuItem(final Context context) {
235 return item("New", getKeyStroke(META + " N"),
236 Actions.class, "newFile", context);
241 * @param context
242 * <br>Not null
243 * <br>Shared
244 * @return
245 * <br>Not null
246 * <br>New
248 public static final JMenuItem newOpenMenuItem(final Context context) {
249 return item("Open...", getKeyStroke(META + " O"),
250 Actions.class, "open", context);
255 * @param context
256 * <br>Not null
257 * <br>Shared
258 * @return
259 * <br>Not null
260 * <br>New
262 public static final JMenuItem newSaveMenuItem(final Context context) {
263 final JMenuItem result = item("Save", getKeyStroke(META + " S"),
264 Actions.class, "save", context);
265 final Variable<Boolean> fileModifiedVariable = context.getVariable(FILE_MODIFIED);
267 fileModifiedVariable.addListener(new Variable.Listener<Boolean>() {
269 @Override
270 public final void valueChanged(final ValueChangedEvent<Boolean, ?> event) {
271 result.setEnabled(event.getNewValue());
276 return result;
281 * @param context
282 * <br>Not null
283 * <br>Shared
284 * @return
285 * <br>Not null
286 * <br>New
288 public static final JMenuItem newSaveAsMenuItem(final Context context) {
289 final JMenuItem result = item("Save As...", getKeyStroke(META + " shift S"),
290 Actions.class, "saveAs", context);
292 return result;
297 * @param context
298 * <br>Not null
299 * <br>Shared
300 * @return
301 * <br>Not null
302 * <br>New
304 public static final JMenuItem newUndoMenuItem(final Context context) {
305 final JMenuItem result = item("Undo", getKeyStroke(META + " Z"),
306 Actions.class, "undo", context);
308 return result;
313 * @param context
314 * <br>Not null
315 * <br>Shared
316 * @return
317 * <br>Not null
318 * <br>New
320 public static final JMenuItem newRedoMenuItem(final Context context) {
321 final JMenuItem result = item("Redo", getKeyStroke(META + " Y"),
322 Actions.class, "redo", context);
324 return result;
329 * @param context
330 * <br>Not null
331 * <br>Shared
332 * @return
333 * <br>Not null
334 * <br>New
336 public static final JMenuItem newCopyMenuItem(final Context context) {
337 final JMenuItem result = item("Copy", getKeyStroke(META + " C"),
338 Actions.class, "copy", context);
340 return result;
345 * @param context
346 * <br>Not null
347 * <br>Shared
348 * @return
349 * <br>Not null
350 * <br>New
352 public static final JMenuItem newCutMenuItem(final Context context) {
353 final JMenuItem result = item("Cut", getKeyStroke(META + " X"),
354 Actions.class, "cut", context);
356 return result;
361 * @param context
362 * <br>Not null
363 * <br>Shared
364 * @return
365 * <br>Not null
366 * <br>New
368 public static final JMenuItem newPasteMenuItem(final Context context) {
369 final JMenuItem result = item("Paste", getKeyStroke(META + " V"),
370 Actions.class, "paste", context);
372 return result;
377 * @param context
378 * <br>Not null
379 * <br>Shared
380 * @return
381 * <br>Not null
382 * <br>New
384 public static final JMenuItem newTreeMenuItem(final Context context) {
385 final JMenuItem result = Messages.translate(new JRadioButtonMenuItem(
386 SwingTools.action(Actions.class, "tree", context)
387 .setName("Tree")));
389 getOrCreateButtonGroup(context, VIEW_RADIO_GROUP).add(result);
390 getOrCreateButtonGroup(context, VIEW_RADIO_GROUP).setSelected(result.getModel(), true);
392 return result;
397 * @param context
398 * <br>Not null
399 * <br>Shared
400 * @return
401 * <br>Not null
402 * <br>New
404 public static final JMenuItem newTextMenuItem(final Context context) {
405 final JMenuItem result = Messages.translate(new JRadioButtonMenuItem(
406 SwingTools.action(Actions.class, "text", context)
407 .setName("Text")));
409 getOrCreateButtonGroup(context, VIEW_RADIO_GROUP).add(result);
411 return result;
416 * @param context
417 * <br>Not null
418 * <br>Shared
419 * @return
420 * <br>Not null
421 * <br>New
423 public static final JMenuItem newManualMenuItem(final Context context) {
424 return item(
425 "Manual", getKeyStroke("F1"),
426 net.sourceforge.aprog.subtitlesadjuster.Actions.class, "showManual", context);
431 * @param context
432 * <br>Not null
433 * @return
434 * <br>Not null
435 * <br>New
437 public static final JPanel newMainPanel(final Context context) {
438 final JPanel result = new JPanel(new BorderLayout());
440 result.add(newLayeredViews(context));
442 new DropTarget(result, new DropTargetAdapter() {
444 @Override
445 public final void dragEnter(final DropTargetDragEvent event) {
446 result.setBorder(BorderFactory.createLineBorder(Color.GREEN, 2));
449 @Override
450 public final void dragExit(final DropTargetEvent event) {
451 result.setBorder(null);
454 @Override
455 public final void drop(final DropTargetDropEvent event) {
456 result.setBorder(null);
458 final List<File> files = SwingTools.getFiles(event);
460 if (!files.isEmpty() && Actions.confirm(context)) {
461 context.set(FILE, files.get(0));
467 return result;
472 * @param context
473 * <br>Not null
474 * @return
475 * <br>Not null
476 * <br>New
478 public static final JPanel newLayeredViews(final Context context) {
479 final JPanel views = new JPanel(new CardLayout());
481 views.add(scrollable(newDOMTreeView(context)), Constants.VIEW_MODE_TREE);
482 views.add(scrollable(newDOMTextView(context)), Constants.VIEW_MODE_TEXT);
484 getOrCreateViewModeVariable(context).addListener(new Variable.Listener<String>() {
486 @Override
487 public final void valueChanged(final ValueChangedEvent<String, ?> event) {
488 ((CardLayout) views.getLayout()).show(views, event.getNewValue());
493 return views;
498 * @param context
499 * <br>Not null
500 * @return
501 * <br>Not null
502 * <br>New
504 public static final JTextPane newDOMTextView(final Context context) {
505 final Variable<Node> domVariable = context.getVariable(DOM);
506 final JTextPane result = new JTextPane();
508 result.getDocument().addDocumentListener(new DocumentListener() {
510 private final int[] newCaretPosition = new int[1];
512 @Override
513 public final void insertUpdate(final DocumentEvent event) {
514 this.newCaretPosition[0] = result.getCaretPosition() + 1;
515 this.updateContext();
518 @Override
519 public final void removeUpdate(final DocumentEvent event) {
520 this.newCaretPosition[0] = result.getCaretPosition() - 1;
521 this.updateContext();
524 @Override
525 public final void changedUpdate(final DocumentEvent event) {
526 this.newCaretPosition[0] = result.getCaretPosition();
527 this.updateContext();
530 private final void updateContext() {
531 final int caretPosition = this.newCaretPosition[0];
533 SwingUtilities.invokeLater(new Runnable() {
535 @Override
536 public void run() {
537 debugPrint(result.getText());
538 if (!asString(domVariable.getValue()).equals(result.getText())) {
539 context.set(FILE_MODIFIED, true);
540 try {
541 domVariable.setValue(XMLTools.parse(result.getText()));
542 result.setCaretPosition(caretPosition);
543 } catch (final Exception exception) {
544 net.sourceforge.aprog.subtitlesadjuster.Actions.showErrorMessage(context, exception);
554 domVariable.addListener(new Variable.Listener<Node>() {
556 @Override
557 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
558 result.setText(asString(domVariable.getValue()));
563 result.setText(asString(domVariable.getValue()));
565 return result;
570 * @param node
571 * <br>Not null
572 * @return
573 * <br>Not null
574 * <br>New
576 public static final String asString(final Node node) {
577 final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
579 XMLTools.write(node, buffer, 0);
581 return buffer.toString();
586 * @param context
587 * <br>Not null
588 * @return
589 * <br>Not null
590 * <br>New
592 public static final JTree newDOMTreeView(final Context context) {
593 final Variable<Node> domVariable = context.getVariable(DOM);
594 final JTree result = new JTree(new DOMTreeModel(domVariable.getValue()));
597 domVariable.addListener(new Variable.Listener<Node>() {
599 @Override
600 public final void valueChanged(final ValueChangedEvent<Node, ?> event) {
601 final DOMTreeModel treeModel = new DOMTreeModel(event.getNewValue());
603 result.setModel(treeModel);
605 context.set(TREE_MODEL, treeModel);
610 return result;
615 * @param context
616 * <br>Not null
617 * <br>Input-output
618 * @param variableName
619 * <br>Not null
620 * @return
621 * <br>Not null
622 * <br>Maybe new
624 public static final ButtonGroup getOrCreateButtonGroup(final Context context, final String variableName) {
625 ButtonGroup result = context.get(variableName);
627 if (result == null) {
628 context.set(variableName, result = new ButtonGroup());
631 return result;
636 * @param context
637 * <br>Not null
638 * <br>Input-output
639 * @return
640 * <br>Not null
641 * <br>Maybe new
643 public static final Variable<String> getOrCreateViewModeVariable(final Context context) {
644 Variable<String> result = context.getVariable(VIEW_MODE);
646 if (result == null) {
647 context.set(VIEW_MODE, Constants.VIEW_MODE_TREE);
648 result = context.getVariable(VIEW_MODE);
651 return result;
656 * @author codistmonk (creation 2010-07-04)
658 public static final class DOMTreeModel extends DefaultTreeModel {
662 * @param domNode
663 * <br>Not null
664 * <br>Shared
666 public DOMTreeModel(final Node domNode) {
667 super(newTreeNode(domNode));
672 * @return
673 * <br>Not null
674 * <br>Shared
676 public final Node getDOMNode() {
677 return (Node) ((DefaultMutableTreeNode) this.getRoot()).getUserObject();
680 private static final long serialVersionUID = 4264388285566053331L;
684 * @param domNode
685 * <br>Not null
686 * <br>Shared
687 * @return
688 * <br>Not null
689 * <br>New
691 public static final DefaultMutableTreeNode newTreeNode(final Node domNode) {
692 final DefaultMutableTreeNode result = new DefaultMutableTreeNode(domNode) {
694 @Override
695 public final String toString() {
696 switch (domNode.getNodeType()) {
697 case Node.ATTRIBUTE_NODE:
698 return domNode.getNodeName() +
699 (domNode.getNodeValue() == null ? "" : "=\"" + domNode.getNodeValue() + "\"");
700 default:
701 return domNode.getNodeName() +
702 (domNode.getNodeValue() == null ? "" : "[" + domNode.getNodeValue() + "]");
706 private static final long serialVersionUID = 8090552131823122052L;
709 final NamedNodeMap attributes = domNode.getAttributes();
711 if (attributes != null) {
712 for (int i = 0; i < attributes.getLength(); ++i) {
713 result.add(newTreeNode(attributes.item(i)));
717 if (domNode.getNodeType() != Node.ATTRIBUTE_NODE) {
718 for (final Node domChild : XMLTools.toList(domNode.getChildNodes())) {
719 result.add(newTreeNode(domChild));
724 return result;