4 * Copyright 2010 Codist Monk.
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
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
;
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
;
81 * @author codistmonk (creation 2010-07-03)
83 public final class Components
{
86 * @throws IllegalInstantiationException To prevent instantiation
88 private Components() {
89 throw new IllegalInstantiationException();
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
);
132 public static final JMenuBar
newMenuBar(final Context context
) {
135 if (MacAdapterTools
.isMacOSX()) {
136 MacAdapterTools
.setUseScreenMenuBar(true);
141 newAboutMenuItem(context
),
143 newPreferencesMenuItem(context
),
145 newQuitMenuItem(context
)
148 newNewMenuItem(context
),
150 newOpenMenuItem(context
),
152 newSaveMenuItem(context
),
153 newSaveAsMenuItem(context
)
156 newUndoMenuItem(context
),
157 newRedoMenuItem(context
),
159 newCopyMenuItem(context
),
160 newCutMenuItem(context
),
161 newPasteMenuItem(context
)
164 newTreeMenuItem(context
),
165 newTextMenuItem(context
)
168 newManualMenuItem(context
)
181 public static final JMenuItem
newAboutMenuItem(final Context context
) {
184 return net
.sourceforge
.aprog
.subtitlesadjuster
.Components
.newAboutMenuItem(context
);
196 public static final JMenuItem
newPreferencesMenuItem(final Context context
) {
199 return net
.sourceforge
.aprog
.subtitlesadjuster
.Components
.newPreferencesMenuItem(context
);
211 public static final JMenuItem
newQuitMenuItem(final Context context
) {
214 if (MacAdapterTools
.isMacOSX() && MacAdapterTools
.getUseScreenMenuBar()) {
215 if (registerMacOSXApplicationListener("handleQuit",
216 Actions
.class, "quit", context
)) {
221 return item("Quit", getKeyStroke(META
+ " Q"),
222 Actions
.class, "quit", context
);
234 public static final JMenuItem
newNewMenuItem(final Context context
) {
235 return item("New", getKeyStroke(META
+ " N"),
236 Actions
.class, "newFile", context
);
248 public static final JMenuItem
newOpenMenuItem(final Context context
) {
249 return item("Open...", getKeyStroke(META
+ " O"),
250 Actions
.class, "open", context
);
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
>() {
270 public final void valueChanged(final ValueChangedEvent
<Boolean
, ?
> event
) {
271 result
.setEnabled(event
.getNewValue());
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
);
304 public static final JMenuItem
newUndoMenuItem(final Context context
) {
305 final JMenuItem result
= item("Undo", getKeyStroke(META
+ " Z"),
306 Actions
.class, "undo", context
);
320 public static final JMenuItem
newRedoMenuItem(final Context context
) {
321 final JMenuItem result
= item("Redo", getKeyStroke(META
+ " Y"),
322 Actions
.class, "redo", context
);
336 public static final JMenuItem
newCopyMenuItem(final Context context
) {
337 final JMenuItem result
= item("Copy", getKeyStroke(META
+ " C"),
338 Actions
.class, "copy", context
);
352 public static final JMenuItem
newCutMenuItem(final Context context
) {
353 final JMenuItem result
= item("Cut", getKeyStroke(META
+ " X"),
354 Actions
.class, "cut", context
);
368 public static final JMenuItem
newPasteMenuItem(final Context context
) {
369 final JMenuItem result
= item("Paste", getKeyStroke(META
+ " V"),
370 Actions
.class, "paste", context
);
384 public static final JMenuItem
newTreeMenuItem(final Context context
) {
385 final JMenuItem result
= Messages
.translate(new JRadioButtonMenuItem(
386 SwingTools
.action(Actions
.class, "tree", context
)
389 getOrCreateButtonGroup(context
, VIEW_RADIO_GROUP
).add(result
);
390 getOrCreateButtonGroup(context
, VIEW_RADIO_GROUP
).setSelected(result
.getModel(), true);
404 public static final JMenuItem
newTextMenuItem(final Context context
) {
405 final JMenuItem result
= Messages
.translate(new JRadioButtonMenuItem(
406 SwingTools
.action(Actions
.class, "text", context
)
409 getOrCreateButtonGroup(context
, VIEW_RADIO_GROUP
).add(result
);
423 public static final JMenuItem
newManualMenuItem(final Context context
) {
425 "Manual", getKeyStroke("F1"),
426 net
.sourceforge
.aprog
.subtitlesadjuster
.Actions
.class, "showManual", context
);
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() {
445 public final void dragEnter(final DropTargetDragEvent event
) {
446 result
.setBorder(BorderFactory
.createLineBorder(Color
.GREEN
, 2));
450 public final void dragExit(final DropTargetEvent event
) {
451 result
.setBorder(null);
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));
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
>() {
487 public final void valueChanged(final ValueChangedEvent
<String
, ?
> event
) {
488 ((CardLayout
) views
.getLayout()).show(views
, event
.getNewValue());
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];
513 public final void insertUpdate(final DocumentEvent event
) {
514 this.newCaretPosition
[0] = result
.getCaretPosition() + 1;
515 this.updateContext();
519 public final void removeUpdate(final DocumentEvent event
) {
520 this.newCaretPosition
[0] = result
.getCaretPosition() - 1;
521 this.updateContext();
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() {
537 debugPrint(result
.getText());
538 if (!asString(domVariable
.getValue()).equals(result
.getText())) {
539 context
.set(FILE_MODIFIED
, true);
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
>() {
557 public final void valueChanged(final ValueChangedEvent
<Node
, ?
> event
) {
558 result
.setText(asString(domVariable
.getValue()));
563 result
.setText(asString(domVariable
.getValue()));
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();
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
>() {
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
);
618 * @param variableName
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());
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
);
656 * @author codistmonk (creation 2010-07-04)
658 public static final class DOMTreeModel
extends DefaultTreeModel
{
666 public DOMTreeModel(final Node domNode
) {
667 super(newTreeNode(domNode
));
676 public final Node
getDOMNode() {
677 return (Node
) ((DefaultMutableTreeNode
) this.getRoot()).getUserObject();
680 private static final long serialVersionUID
= 4264388285566053331L;
691 public static final DefaultMutableTreeNode
newTreeNode(final Node domNode
) {
692 final DefaultMutableTreeNode result
= new DefaultMutableTreeNode(domNode
) {
695 public final String
toString() {
696 switch (domNode
.getNodeType()) {
697 case Node
.ATTRIBUTE_NODE
:
698 return domNode
.getNodeName() +
699 (domNode
.getNodeValue() == null ?
"" : "=\"" + domNode
.getNodeValue() + "\"");
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
));