⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jedittextarea.java

📁 报表设计软件,很好的
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
package org.jfree.designer.text.jedit;

/*
 * JEditTextArea.java - jEdit's text component
 * Copyright (C) 1999 Slava Pestov
 *
 * You may use and modify this package for any purpose. Redistribution is
 * permitted, in both source and binary form, provided that this notice
 * remains intact in all source distributions of this package.
 */

import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.EventListenerList;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.Segment;
import javax.swing.text.Utilities;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoableEdit;

import org.jfree.designer.text.jedit.syntax.SyntaxDocument;
import org.jfree.designer.text.jedit.syntax.SyntaxStyle;
import org.jfree.designer.text.jedit.syntax.Token;
import org.jfree.designer.text.jedit.syntax.TokenMarker;

/**
 * jEdit's text area component. It is more suited for editing program source code than
 * JEditorPane, because it drops the unnecessary features (images, variable-width lines,
 * and so on) and adds a whole bunch of useful goodies such as: <ul> <li>More flexible key
 * binding scheme <li>Supports macro recorders <li>Rectangular selection <li>Bracket
 * highlighting <li>Syntax highlighting <li>Command repetition <li>Block caret can be
 * enabled </ul> It is also faster and doesn't have as many problems. It can be used in
 * other applications; the only other part of jEdit it depends on is the syntax
 * package.<p>
 * <p/>
 * To use it in your app, treat it like any other component, for example:
 * <pre>JEditTextArea ta = new JEditTextArea();
 * ta.setTokenMarker(new JavaTokenMarker());
 * ta.setText("public class Test {\n"
 *     + "    public static void main(String[] args) {\n"
 *     + "        System.out.println(\"Hello World\");\n"
 *     + "    }\n"
 *     + "}");</pre>
 *
 * @author Slava Pestov
 * @version $Id: JEditTextArea.java,v 1.3 2004/04/20 18:54:51 taqua Exp $
 */
public final class JEditTextArea
        extends JComponent
{
  /**
   * Adding components with this name to the text area will place them left of the
   * horizontal scroll bar. In jEdit, the status bar is added this way.
   */
  private static final String LEFT_OF_SCROLLBAR = "los";

  /**
   * Creates a new JEditTextArea with the default settings.
   */
  public JEditTextArea ()
  {
    this(TextAreaDefaults.getDefaults());
  }

  /**
   * Creates a new JEditTextArea with the specified settings.
   *
   * @param defaults The default settings
   */
  private JEditTextArea (final TextAreaDefaults defaults)
  {
    // Enable the necessary events
    enableEvents(AWTEvent.KEY_EVENT_MASK);

    // Initialize some misc. stuff
    painter = new TextAreaPainter(this, defaults);
    documentHandler = new DocumentHandler();
    listenerList = new EventListenerList();
    caretEvent = new MutableCaretEvent();
    lineSegment = new Segment();
    bracketLine = bracketPosition = -1;
    blink = true;

    // Initialize the GUI
    setLayout(new ScrollLayout());
    add(CENTER, painter);
    add(RIGHT, vertical = new JScrollBar(JScrollBar.VERTICAL));
    add(BOTTOM, horizontal = new JScrollBar(JScrollBar.HORIZONTAL));

    // Add some event listeners
    vertical.addAdjustmentListener(new AdjustHandler());
    horizontal.addAdjustmentListener(new AdjustHandler());
    painter.addComponentListener(new ComponentHandler());
    painter.addMouseListener(new MouseHandler());
    painter.addMouseMotionListener(new DragHandler());
    addFocusListener(new FocusHandler());

    // Load the defaults
    setInputHandler(defaults.inputHandler);
    setDocument(defaults.document);
    editable = defaults.editable;
    caretVisible = defaults.caretVisible;
    caretBlinks = defaults.caretBlinks;
    electricScroll = defaults.electricScroll;

    popup = defaults.popup;

    // We don't seem to get the initial focus event?
    focusedComponent = this;
  }


  /**
   * Returns the object responsible for painting this text area.
   */
  public final TextAreaPainter getPainter ()
  {
    return painter;
  }

  /**
   * Returns the input handler.
   */
  public final InputHandler getInputHandler ()
  {
    return inputHandler;
  }

  /**
   * Sets the input handler.
   *
   * @param inputHandler The new input handler
   */
  private void setInputHandler (final InputHandler inputHandler)
  {
    this.inputHandler = inputHandler;
  }

  /**
   * Returns true if the caret is blinking, false otherwise.
   */
  public final boolean isCaretBlinkEnabled ()
  {
    return caretBlinks;
  }

  /**
   * Toggles caret blinking.
   *
   * @param caretBlinks True if the caret should blink, false otherwise
   */
  public final void setCaretBlinkEnabled (final boolean caretBlinks)
  {
    this.caretBlinks = caretBlinks;
    if (!caretBlinks)
    {
      blink = false;
    }

    painter.invalidateSelectedLines();
  }

  /**
   * Returns true if the caret is visible, false otherwise.
   */
  public final boolean isCaretVisible ()
  {
    return (!caretBlinks || blink) && caretVisible;
  }

  /**
   * Sets if the caret should be visible.
   *
   * @param caretVisible True if the caret should be visible, false otherwise
   */
  private void setCaretVisible (final boolean caretVisible)
  {
    this.caretVisible = caretVisible;
    blink = true;

    painter.invalidateSelectedLines();
  }

  /**
   * Blinks the caret.
   */
  private void blinkCaret ()
  {
    if (caretBlinks)
    {
      blink = !blink;
      painter.invalidateSelectedLines();
    }
    else
    {
      blink = true;
    }
  }

  /**
   * Returns the number of lines from the top and button of the text area that are always
   * visible.
   */
  public final int getElectricScroll ()
  {
    return electricScroll;
  }

  /**
   * Sets the number of lines from the top and bottom of the text area that are always
   * visible
   *
   * @param electricScroll The number of lines always visible from the top or bottom
   */
  public final void setElectricScroll (final int electricScroll)
  {
    this.electricScroll = electricScroll;
  }

  /**
   * Updates the state of the scroll bars. This should be called if the number of lines in
   * the document changes, or when the size of the text are changes.
   */
  private void updateScrollBars ()
  {
    if (vertical != null && visibleLines != 0)
    {
      vertical.setValues(firstLine, visibleLines, 0, getLineCount());
      vertical.setUnitIncrement(2);
      vertical.setBlockIncrement(visibleLines);
    }

    final int width = painter.getWidth();
    if (horizontal != null && width != 0)
    {
      horizontal.setValues(-horizontalOffset, width, 0, width * 5);
      horizontal.setUnitIncrement(painter.getFontMetrics()
              .charWidth('w'));
      horizontal.setBlockIncrement(width / 2);
    }
  }

  /**
   * Returns the line displayed at the text area's origin.
   */
  public final int getFirstLine ()
  {
    return firstLine;
  }

  /**
   * Sets the line displayed at the text area's origin without updating the scroll bars.
   */
  public final void setFirstLine (final int firstLine)
  {
    if (firstLine == this.firstLine)
    {
      return;
    }
    //final int oldFirstLine = this.firstLine;
    this.firstLine = firstLine;
    if (firstLine != vertical.getValue())
    {
      updateScrollBars();
    }
    painter.repaint();
  }

  /**
   * Returns the number of lines visible in this text area.
   */
  public final int getVisibleLines ()
  {
    return visibleLines;
  }

  /**
   * Recalculates the number of visible lines. This should not be called directly.
   */
  public final void recalculateVisibleLines ()
  {
    if (painter == null)
    {
      return;
    }
    final int height = painter.getHeight();
    final int lineHeight = painter.getFontMetrics().getHeight();
    //final int oldVisibleLines = visibleLines;
    visibleLines = height / lineHeight;
    updateScrollBars();
  }

  /**
   * Returns the horizontal offset of drawn lines.
   */
  public final int getHorizontalOffset ()
  {
    return horizontalOffset;
  }

  /**
   * Sets the horizontal offset of drawn lines. This can be used to implement horizontal
   * scrolling.
   *
   * @param horizontalOffset offset The new horizontal offset
   */
  private void setHorizontalOffset (final int horizontalOffset)
  {
    if (horizontalOffset == this.horizontalOffset)
    {
      return;
    }
    this.horizontalOffset = horizontalOffset;
    if (horizontalOffset != horizontal.getValue())
    {
      updateScrollBars();
    }
    painter.repaint();
  }

  /**
   * A fast way of changing both the first line and horizontal offset.
   *
   * @param firstLine        The new first line
   * @param horizontalOffset The new horizontal offset
   * @return True if any of the values were changed, false otherwise
   */
  private boolean setOrigin (final int firstLine, final int horizontalOffset)
  {
    boolean changed = false;
    //final int oldFirstLine = this.firstLine;

    if (horizontalOffset != this.horizontalOffset)
    {
      this.horizontalOffset = horizontalOffset;
      changed = true;
    }

    if (firstLine != this.firstLine)
    {
      this.firstLine = firstLine;
      changed = true;
    }

    if (changed)
    {
      updateScrollBars();
      painter.repaint();
    }

    return changed;
  }

  /**
   * Ensures that the caret is visible by scrolling the text area if necessary.
   *
   * @return True if scrolling was actually performed, false if the caret was already
   *         visible
   */
  private boolean scrollToCaret ()
  {
    final int line = getCaretLine();
    final int lineStart = getLineStartOffset(line);
    final int offset = Math.max(0, Math.min(getLineLength(line) - 1,
            getCaretPosition() - lineStart));

    return scrollTo(line, offset);
  }

  /**
   * Ensures that the specified line and offset is visible by scrolling the text area if
   * necessary.
   *
   * @param line   The line to scroll to
   * @param offset The offset in the line to scroll to
   * @return True if scrolling was actually performed, false if the line and offset was
   *         already visible
   */
  private boolean scrollTo (final int line, final int offset)
  {
    // visibleLines == 0 before the component is realized
    // we can't do any proper scrolling then, so we have
    // this hack...
    if (visibleLines == 0)
    {
      setFirstLine(Math.max(0, line - electricScroll));
      return true;
    }

    int newFirstLine = firstLine;
    int newHorizontalOffset = horizontalOffset;

    if (line < firstLine + electricScroll)
    {
      newFirstLine = Math.max(0, line - electricScroll);
    }
    else if (line + electricScroll >= firstLine + visibleLines)
    {
      newFirstLine = (line - visibleLines) + electricScroll + 1;
      if (newFirstLine + visibleLines >= getLineCount())
      {
        newFirstLine = getLineCount() - visibleLines;
      }
      if (newFirstLine < 0)
      {
        newFirstLine = 0;
      }
    }

    final int x = _offsetToX(line, offset);
    final int width = painter.getFontMetrics().charWidth('w');

    if (x < 0)
    {
      newHorizontalOffset = Math.min(0, horizontalOffset
              - x + width + 5);
    }
    else if (x + width >= painter.getWidth())
    {
      newHorizontalOffset = horizontalOffset +
              (painter.getWidth() - x) - width - 5;
    }

    return setOrigin(newFirstLine, newHorizontalOffset);
  }

  /**
   * Converts a line index to a y co-ordinate.
   *
   * @param line The line
   */
  public final int lineToY (final int line)
  {
    final FontMetrics fm = painter.getFontMetrics();
    return (line - firstLine) * fm.getHeight()
            - (fm.getLeading() + fm.getMaxDescent());
  }

  /**
   * Converts a y co-ordinate to a line index.
   *
   * @param y The y co-ordinate
   */
  private int yToLine (final int y)
  {
    final FontMetrics fm = painter.getFontMetrics();
    final int height = fm.getHeight();
    return Math.max(0, Math.min(getLineCount() - 1,
            y / height + firstLine));
  }

  /**
   * Converts an offset in a line into an x co-ordinate. This is a slow version that can
   * be used any time.
   *
   * @param line   The line
   * @param offset The offset, from the start of the line
   */
  public final int offsetToX (final int line, final int offset)
  {
    // don't use cached tokens
    painter.currentLineTokens = null;
    return _offsetToX(line, offset);
  }

  /**
   * Converts an offset in a line into an x co-ordinate. This is a fast version that
   * should only be used if no changes were made to the text since the last repaint.
   *
   * @param line   The line
   * @param offset The offset, from the start of the line
   */
  public final int _offsetToX (final int line, final int offset)
  {
    final TokenMarker tokenMarker = getTokenMarker();

    /* Use painter's cached info for speed */
    FontMetrics fm = painter.getFontMetrics();

    getLineText(line, lineSegment);

    final int segmentOffset = lineSegment.offset;
    int x = horizontalOffset;

    /* If syntax coloring is disabled, do simple translation */
    if (tokenMarker == null)
    {
      lineSegment.count = offset;
      return x + Utilities.getTabbedTextWidth(lineSegment,
              fm, x, painter, 0);
    }
    /* If syntax coloring is enabled, we have to do this because
     * tokens can vary in width */
    else
    {
      Token tokens;
      if (painter.currentLineIndex == line
              && painter.currentLineTokens != null)
      {
        tokens = painter.currentLineTokens;
      }
      else
      {
        painter.currentLineIndex = line;
        tokens = painter.currentLineTokens
                 = tokenMarker.markTokens(lineSegment, line);
      }

      //final Toolkit toolkit = painter.getToolkit();
      final Font defaultFont = painter.getFont();
      final SyntaxStyle[] styles = painter.getStyles();

      for (; ;)
      {
        final byte id = tokens.id;
        if (id == Token.END)
        {
          return x;
        }

        if (id == Token.NULL)
        {
          fm = painter.getFontMetrics();
        }
        else
        {
          fm = styles[id].getFontMetrics(this, defaultFont);
        }

        final int length = tokens.length;

        if (offset + segmentOffset < lineSegment.offset + length)
        {
          lineSegment.count = offset - (lineSegment.offset - segmentOffset);
          return x + Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0);
        }
        else
        {
          lineSegment.count = length;
          x += Utilities.getTabbedTextWidth(lineSegment, fm, x, painter, 0);
          lineSegment.offset += length;
        }
        tokens = tokens.next;
      }
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -