001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.widgets;
003
004import java.awt.KeyboardFocusManager;
005import java.awt.event.FocusEvent;
006import java.awt.event.FocusListener;
007
008import javax.swing.JTextArea;
009import javax.swing.text.Document;
010
011import org.openstreetmap.josm.gui.MainApplication;
012import org.openstreetmap.josm.gui.MapFrame;
013import org.openstreetmap.josm.tools.Destroyable;
014
015/**
016 * Subclass of {@link JTextArea} that adds a "native" context menu (cut/copy/paste/select all).
017 * @since 5886
018 */
019public class JosmTextArea extends JTextArea implements Destroyable, FocusListener {
020
021    private final PopupMenuLauncher launcher;
022
023    /**
024     * Constructs a new {@code JosmTextArea}. A default model is set, the initial string
025     * is null, and rows/columns are set to 0.
026     */
027    public JosmTextArea() {
028        this(null, null, 0, 0);
029    }
030
031    /**
032     * Constructs a new {@code JosmTextArea} with the specified text displayed.
033     * A default model is created and rows/columns are set to 0.
034     *
035     * @param text the text to be displayed, or null
036     */
037    public JosmTextArea(String text) {
038        this(null, text, 0, 0);
039    }
040
041    /**
042     * Constructs a new {@code JosmTextArea} with the given document model, and defaults
043     * for all of the other arguments (null, 0, 0).
044     *
045     * @param doc  the model to use
046     */
047    public JosmTextArea(Document doc) {
048        this(doc, null, 0, 0);
049    }
050
051    /**
052     * Constructs a new empty {@code JosmTextArea} with the specified number of
053     * rows and columns. A default model is created, and the initial
054     * string is null.
055     *
056     * @param rows the number of rows >= 0
057     * @param columns the number of columns >= 0
058     * @throws IllegalArgumentException if the rows or columns
059     *  arguments are negative.
060     */
061    public JosmTextArea(int rows, int columns) {
062        this(null, null, rows, columns);
063    }
064
065    /**
066     * Constructs a new {@code JosmTextArea} with the specified text and number
067     * of rows and columns. A default model is created.
068     *
069     * @param text the text to be displayed, or null
070     * @param rows the number of rows >= 0
071     * @param columns the number of columns >= 0
072     * @throws IllegalArgumentException if the rows or columns
073     *  arguments are negative.
074     */
075    public JosmTextArea(String text, int rows, int columns) {
076        this(null, text, rows, columns);
077    }
078
079    /**
080     * Constructs a new {@code JosmTextArea} with the specified number of rows
081     * and columns, and the given model.  All of the constructors
082     * feed through this constructor.
083     *
084     * @param doc the model to use, or create a default one if null
085     * @param text the text to be displayed, null if none
086     * @param rows the number of rows >= 0
087     * @param columns the number of columns >= 0
088     * @throws IllegalArgumentException if the rows or columns
089     *  arguments are negative.
090     */
091    public JosmTextArea(Document doc, String text, int rows, int columns) {
092        super(doc, text, rows, columns);
093        launcher = TextContextualPopupMenu.enableMenuFor(this, true);
094        addFocusListener(this);
095    }
096
097    /**
098     * Restore default behaviour of focus transfer with TAB, overridden by {@link JTextArea}.
099     * @return {@code this}
100     * @since 11308
101     */
102    public JosmTextArea transferFocusOnTab() {
103        // http://stackoverflow.com/a/525867/2257172
104        setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
105        setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
106        return this;
107    }
108
109    @Override
110    public void focusGained(FocusEvent e) {
111        MapFrame map = MainApplication.getMap();
112        if (map != null) {
113            map.keyDetector.setEnabled(false);
114        }
115    }
116
117    @Override
118    public void focusLost(FocusEvent e) {
119        MapFrame map = MainApplication.getMap();
120        if (map != null) {
121            map.keyDetector.setEnabled(true);
122        }
123    }
124
125    @Override
126    public void destroy() {
127        removeFocusListener(this);
128        TextContextualPopupMenu.disableMenuFor(this, launcher);
129    }
130}