123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- /*
- * Copyright (c) 2006-2011 Chris Smith, Shane Mc Cormack, Gregory Holmes
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
- package com.dmdirc.addons.ui_swing.textpane;
-
- import com.dmdirc.addons.ui_swing.SwingController;
- import com.dmdirc.addons.ui_swing.UIUtilities;
- import com.dmdirc.interfaces.ConfigChangeListener;
- import com.dmdirc.ui.interfaces.Window;
- import com.dmdirc.ui.messages.IRCDocument;
- import com.dmdirc.ui.messages.IRCDocumentListener;
- import com.dmdirc.ui.messages.LinePosition;
- import com.dmdirc.ui.messages.Styliser;
-
- import java.awt.Color;
- import java.awt.Point;
- import java.awt.Toolkit;
- import java.awt.datatransfer.StringSelection;
- import java.awt.event.AdjustmentEvent;
- import java.awt.event.AdjustmentListener;
- import java.awt.event.MouseEvent;
- import java.awt.event.MouseMotionAdapter;
- import java.awt.event.MouseMotionListener;
- import java.awt.event.MouseWheelEvent;
- import java.awt.event.MouseWheelListener;
-
- import javax.swing.BoundedRangeModel;
- import javax.swing.JComponent;
- import javax.swing.JLabel;
- import javax.swing.JScrollBar;
- import javax.swing.SwingConstants;
-
- import net.miginfocom.swing.MigLayout;
-
- /**
- * Styled, scrollable text pane.
- */
- public final class TextPane extends JComponent implements MouseWheelListener,
- AdjustmentListener, IRCDocumentListener, ConfigChangeListener {
-
- /**
- * A version number for this class. It should be changed whenever the class
- * structure is changed (or anything else that would prevent serialized
- * objects being unserialized with the new class).
- */
- private static final long serialVersionUID = 5;
- /** Scrollbar model. */
- private final BoundedRangeModel scrollModel;
- /** Canvas object, used to draw text. */
- private final TextPaneCanvas canvas;
- /** IRCDocument. */
- private final IRCDocument document;
- /** Parent Frame. */
- private final Window frame;
- /** Indicator to show whether new lines have been added. */
- private final JLabel newLineIndicator;
- /** Last seen line. */
- private int lastSeenLine = 0;
- /** Show new line notifications. */
- private boolean showNotification;
-
- /**
- * Creates a new instance of TextPane.
- *
- * @param frame Parent Frame
- */
- public TextPane(final Window frame) {
- super();
- this.frame = frame;
-
- setUI(new TextPaneUI());
- document = frame.getContainer().getDocument();
- newLineIndicator = new JLabel("", SwingConstants.CENTER);
- newLineIndicator.setBackground(Color.RED);
- newLineIndicator.setForeground(Color.WHITE);
- newLineIndicator.setOpaque(true);
- newLineIndicator.setVisible(false);
-
- setLayout(new MigLayout("fill, hidemode 3"));
- canvas = new TextPaneCanvas(this, document);
- add(canvas, "dock center");
- add(newLineIndicator, "dock south, center, grow");
- scrollModel = new TextPaneBoundedRangeModel();
- scrollModel.setExtent(0);
- final JScrollBar scrollBar = new JScrollBar(JScrollBar.VERTICAL);
- scrollBar.setModel(scrollModel);
- add(scrollBar, "dock east");
- scrollBar.addAdjustmentListener(this);
- scrollBar.addAdjustmentListener(canvas);
- frame.getContainer().getConfigManager().addChangeListener(
- ((SwingController) frame.getController()).getDomain(),
- "textpanelinenotification", this);
- configChanged("", "textpanelinenotification");
-
- addMouseWheelListener(this);
- document.addIRCDocumentListener(this);
- setAutoscrolls(true);
-
- final MouseMotionListener doScrollRectToVisible
- = new MouseMotionAdapter() {
-
- /** {@inheritDoc} */
- @Override
- public void mouseDragged(final MouseEvent e) {
- if (e.getXOnScreen() > getLocationOnScreen().getX()
- && e.getXOnScreen() < (getLocationOnScreen().getX()
- + getWidth()) && e.getModifiersEx()
- == MouseEvent.BUTTON1_DOWN_MASK) {
- if (getLocationOnScreen().getY() > e.getYOnScreen()) {
- scrollModel.setValue(scrollBar.getValue() - 1);
- } else if (getLocationOnScreen().getY() + getHeight()
- < e.getYOnScreen()) {
- scrollModel.setValue(scrollBar.getValue() + 1);
- }
- canvas.highlightEvent(MouseEventType.DRAG, e);
- }
- }
- };
- addMouseMotionListener(doScrollRectToVisible);
-
- setScrollBarMax(document.getNumLines(), document.getNumLines() - 1);
- }
-
- /** {@inheritDoc} */
- @Override
- public void updateUI() {
- setUI(new TextPaneUI());
- }
-
- /**
- * Returns the last visible line in the textpane.
- *
- * @return Last visible line index
- */
- public int getLastVisibleLine() {
- return scrollModel.getValue();
- }
-
- /**
- * Sets the scrollbar's maximum position. If the current position is
- * within <code>linesAllowed</code> of the end of the document, the
- * scrollbar's current position is set to the end of the document.
- *
- * @param lines Current number of lines
- * @param linesAllowed The number of lines allowed below the
- * current position
- * @since 0.6
- */
- protected void setScrollBarMax(final int lines, final int linesAllowed) {
- final int currentLine = scrollModel.getValue();
- final int allowedDeviation = lines - 1 - linesAllowed;
- boolean setToMax = currentLine == allowedDeviation;
-
- if (allowedDeviation <= 0) {
- setToMax = true;
- }
-
- if (lines <= 1) {
- scrollModel.setRangeProperties(lines, 0, lines, lines, false);
- } else if (setToMax) {
- scrollModel.setRangeProperties(lines - 1, 0, 0, lines - 1, false);
- } else {
- scrollModel.setRangeProperties(scrollModel.getValue(), 0, 0,
- lines - 1, false);
- }
- }
-
- /**
- * Sets the new position for the scrollbar and the associated position
- * to render the text from.
- * @param position new position of the scrollbar
- */
- public void setScrollBarPosition(final int position) {
- scrollModel.setValue(position);
- }
-
- /**
- * {@inheritDoc}
- *
- * @param e Mouse wheel event
- */
- @Override
- public void adjustmentValueChanged(final AdjustmentEvent e) {
- if (showNotification && e.getValue() >= scrollModel.getMaximum()) {
- newLineIndicator.setVisible(false);
- }
-
- lastSeenLine = Math.max(lastSeenLine, e.getValue());
-
- final int lines = scrollModel.getMaximum() - lastSeenLine;
- newLineIndicator.setText("↓ " + lines + " new line"
- + (lines == 1 ? "" : "s") + " ↓");
- }
-
- /**
- * {@inheritDoc}
- *
- * @param e Mouse wheel event
- */
- @Override
- public void mouseWheelMoved(final MouseWheelEvent e) {
- if (e.getWheelRotation() > 0) {
- scrollModel.setValue(scrollModel.getValue() + e.getScrollAmount());
- } else {
- scrollModel.setValue(scrollModel.getValue() - e.getScrollAmount());
- }
- }
-
- /**
- *
- * Returns the line information from a mouse click inside the textpane.
- *
- * @param point mouse position
- *
- * @return line number, line part, position in whole line
- */
- public LineInfo getClickPosition(final Point point) {
- return canvas.getClickPosition(point, true);
- }
-
- /**
- *
- * Returns the line information from a mouse click inside the textpane.
- *
- * @param point mouse position
- * @param selection Are we selecting text?
- *
- * @return line number, line part, position in whole line
- *
- * @since 0.6.3
- */
- public LineInfo getClickPosition(final Point point,
- final boolean selection) {
- return canvas.getClickPosition(point, selection);
- }
-
- /**
- * Returns the selected text.
- *
- * @return Selected text
- */
- public String getSelectedText() {
- return getSelectedText(false);
- }
-
- /**
- * Returns the selected text.
- *
- * @return Selected text
- */
- public String getStyledSelectedText() {
- return getSelectedText(true);
- }
-
- /**
- * Returns the selected text.
- *
- * @param styled Return styled text?
- *
- * @return Selected text
- */
- public String getSelectedText(final boolean styled) {
- final StringBuffer selectedText = new StringBuffer();
- final LinePosition selectedRange = canvas.getSelectedRange();
-
- if (selectedRange.getStartLine() == -1) {
- return null;
- }
-
- for (int i = selectedRange.getStartLine(); i
- <= selectedRange.getEndLine(); i++) {
- if (i != selectedRange.getStartLine()) {
- selectedText.append('\n');
- }
- if (scrollModel.getMaximum() < i) {
- return selectedText.toString();
- }
- final String line;
- if (styled) {
- line = document.getLine(i).getStyledText();
- } else {
- line = document.getLine(i).getText();
- }
- if (!line.isEmpty()) {
- if (selectedRange.getEndLine()
- == selectedRange.getStartLine()) {
- //loop through range
- if (selectedRange.getStartPos() != -1 && selectedRange.
- getEndPos() != -1) {
- selectedText.append(getText(line,
- selectedRange.getStartPos(),
- selectedRange.getEndPos(), styled));
- }
- } else if (i == selectedRange.getStartLine()) {
- //loop from start of range to the end
- if (selectedRange.getStartPos() != -1) {
- selectedText.append(getText(line,
- selectedRange.getStartPos(),
- Styliser.stipControlCodes(line).length(), styled));
- }
- } else if (i == selectedRange.getEndLine()) {
- //loop from start to end of range
- if (selectedRange.getEndPos() != -1) {
- selectedText.append(getText(line, 0, selectedRange
- .getEndPos(), styled));
- }
- } else {
- //loop the whole line
- selectedText.append(getText(line, 0, line.length(),
- styled));
- }
- }
- }
-
- return selectedText.toString();
- }
-
- /**
- * Gets a range of text (styled or unstyled) from the given text.
- *
- * @param text Text to extract text from
- * @param start Start index
- * @param end End index
- * @param styled Styled text?
- *
- * @return Requested text range as a String
- */
- private String getText(final String text, final int start, final int end,
- final boolean styled) {
- if (styled) {
- return Styliser.getStyledText(text, start, end);
- } else {
- return text.substring(start, end);
- }
- }
-
- /**
- * Returns the selected range.
- *
- * @return selected range
- */
- public LinePosition getSelectedRange() {
- return canvas.getSelectedRange();
- }
-
- /**
- * Returns whether there is a selected range.
- *
- * @return true iif there is a selected range
- */
- public boolean hasSelectedRange() {
- final LinePosition selectedRange = canvas.getSelectedRange();
- return !(selectedRange.getStartLine() == selectedRange.getEndLine()
- && selectedRange.getStartPos() == selectedRange.getEndPos());
- }
-
- /**
- * Selects the specified region of text.
- *
- * @param position Line position
- */
- public void setSelectedTexT(final LinePosition position) {
- canvas.setSelectedRange(position);
- }
-
- /**
- * Returns the type of text this click represents.
- *
- * @param lineInfo Line info of click.
- *
- * @return Click type for specified position
- */
- public ClickTypeValue getClickType(final LineInfo lineInfo) {
- return canvas.getClickType(lineInfo);
- }
-
- /**
- * Returns the surrouding word at the specified position.
- *
- * @param lineNumber Line number to get word from
- * @param index Position to get surrounding word
- *
- * @return Surrounding word
- */
- public String getWordAtIndex(final int lineNumber, final int index) {
- if (lineNumber == -1) {
- return "";
- }
- final int[] indexes = canvas.getSurroundingWordIndexes(document
- .getLine(lineNumber).getText(), index);
- return document.getLine(lineNumber).getText().substring(indexes[0],
- indexes[1]);
- }
-
- /** Adds the selected text to the clipboard. */
- public void copy() {
- copy(false);
- }
-
- /**
- * Adds the selected text to the clipboard.
- *
- * @param copyControlCharacters Should we copy control codes, or strip them?
- */
- public void copy(final boolean copyControlCharacters) {
- if (getSelectedText() != null && !getSelectedText().isEmpty()) {
- Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
- new StringSelection(getSelectedText(
- copyControlCharacters)), null);
- }
- }
-
- /** Clears the textpane. */
- public void clear() {
- UIUtilities.invokeLater(new Runnable() {
-
- /** {@inheritDoc}. */
- @Override
- public void run() {
- document.clear();
- }
- });
- }
-
- /** Clears the selection. */
- public void clearSelection() {
- canvas.clearSelection();
- }
-
- /** Scrolls one page up in the textpane. */
- public void pageDown() {
- scrollModel.setValue(scrollModel.getValue() + 10);
- }
-
- /** Scrolls one page down in the textpane. */
- public void pageUp() {
- scrollModel.setValue(scrollModel.getValue() - 10);
- }
-
- /** Scrolls to the beginning of the TextPane. */
- public void goToHome() {
- scrollModel.setValue(0);
- }
-
- /** Scrolls to the end of the TextPane. */
- public void goToEnd() {
- scrollModel.setValue(scrollModel.getMaximum());
- }
-
- /** {@inheritDoc}. */
- @Override
- @Deprecated
- public void lineAdded(final int line, final int size) {
- // DO NOTHING, MWAHAHAHAHAHA
- }
-
- /** {@inheritDoc}. */
- @Override
- public void trimmed(final int newSize, final int numTrimmed) {
- UIUtilities.invokeLater(new Runnable() {
-
- /** {@inheritDoc}. */
- @Override
- public void run() {
- final LinePosition selectedRange = getSelectedRange();
- selectedRange.setStartLine(selectedRange.getStartLine()
- - numTrimmed);
- selectedRange.setEndLine(selectedRange.getEndLine()
- - numTrimmed);
- if (selectedRange.getStartLine() < 0) {
- selectedRange.setStartLine(0);
- }
- if (selectedRange.getEndLine() < 0) {
- selectedRange.setEndLine(0);
- }
- setSelectedTexT(selectedRange);
- setScrollBarMax(newSize, 1);
- }
- });
- }
-
- /** {@inheritDoc}. */
- @Override
- public void cleared() {
- UIUtilities.invokeLater(new Runnable() {
-
- /** {@inheritDoc}. */
- @Override
- public void run() {
- scrollModel.setMaximum(0);
- scrollModel.setValue(0);
- canvas.recalc();
- }
- });
- }
-
- /** {@inheritDoc}. */
- @Override
- public void linesAdded(final int line, final int length, final int size) {
- UIUtilities.invokeLater(new Runnable() {
-
- /** {@inheritDoc}. */
- @Override
- public void run() {
- if (showNotification && scrollModel.getValue() != line) {
- newLineIndicator.setVisible(true);
- }
-
- setScrollBarMax(size, length);
- }
- });
- }
-
- /** {@inheritDoc}. */
- @Override
- public void repaintNeeded() {
- UIUtilities.invokeLater(new Runnable() {
-
- /** {@inheritDoc}. */
- @Override
- public void run() {
- canvas.recalc();
- }
- });
- }
-
- /**
- * Retrieves this textpane's IRCDocument.
- *
- * @return This textpane's IRC document
- */
- public IRCDocument getDocument() {
- return document;
- }
-
- /**
- * Retrives the parent window for this textpane.
- *
- * @return Parent window
- */
- public Window getWindow() {
- return frame;
- }
-
- /**
- * Adds a textpane listener to this textpane.
- *
- * @param listener Listener to add
- */
- public void addTextPaneListener(final TextPaneListener listener) {
- canvas.addTextPaneListener(listener);
- }
-
- /**
- * Removes a textpane listener from this textpane.
- *
- * @param listener Listener to remove
- */
- public void removeTextPaneListener(final TextPaneListener listener) {
- canvas.removeTextPaneListener(listener);
- }
-
- /** {@inheritDoc} */
- @Override
- public void configChanged(final String domain, final String key) {
- showNotification = frame.getContainer().getConfigManager()
- .getOptionBool(((SwingController) frame.getController())
- .getDomain(), "textpanelinenotification");
- if (!showNotification) {
- UIUtilities.invokeLater(new Runnable() {
-
- /** {@inheritDoc}. */
- @Override
- public void run() {
- newLineIndicator.setVisible(false);
- }
- });
- }
- }
- }
|