Package org.fife.ui.rtextarea

Source Code of org.fife.ui.rtextarea.RTextAreaBase$RTAMouseListener

/*
* 04/07/2005
*
* RTextAreaBase.java - The base class for an RTextArea.
*
* This library is distributed under a modified BSD license.  See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.rtextarea;

import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.TextUI;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.StyleContext;


/**
* This is the base class for <code>RTextArea</code>; basically it's just an
* extension of <code>javax.swing.JTextArea</code> adding a bunch of properties.
* <p>
*
* This class is only supposed to be overridden by <code>RTextArea</code>.
*
* @author Robert Futrell
* @version 0.8
*/
public abstract class RTextAreaBase extends JTextArea {

  public static final String BACKGROUND_IMAGE_PROPERTY      = "background.image";
  public static final String CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY  = "RTA.currentLineHighlightColor";
  public static final String CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY  = "RTA.currentLineHighlightFade";
  public static final String HIGHLIGHT_CURRENT_LINE_PROPERTY    = "RTA.currentLineHighlight";
  public static final String ROUNDED_SELECTION_PROPERTY      = "RTA.roundedSelection";

  private boolean tabsEmulatedWithSpaces;    // If true, tabs will be expanded to spaces.

  private boolean highlightCurrentLine;    // If true, the current line is highlighted.
  private Color currentLineColor;      // The color used to highlight the current line.
  private boolean marginLineEnabled;      // If true, paint a "margin line."
  private Color marginLineColor;      // The color used to paint the margin line.
  private int marginLineX;        // The x-location of the margin line.
  private int marginSizeInChars;      // How many 'm' widths the margin line is over.
  private boolean fadeCurrentLineHighlight;  // "Fade effect" for current line highlight.
  private boolean roundedSelectionEdges;
  private int previousCaretY;
int currentCaretY;              // Used to know when to rehighlight current line.

  private BackgroundPainterStrategy backgroundPainter;  // Paints the background.

  private RTAMouseListener mouseListener;

  private static final Color DEFAULT_CARET_COLOR        = new ColorUIResource(255,51,51);
  private static final Color DEFAULT_CURRENT_LINE_HIGHLIGHT_COLOR  = new Color(255,255,170);
  private static final Color DEFAULT_MARGIN_LINE_COLOR      = new Color(255,224,224);
  private static final int DEFAULT_TAB_SIZE          = 4;
  private static final int DEFAULT_MARGIN_LINE_POSITION      = 80;


  /**
   * Constructor.
   */
  public RTextAreaBase() {
    init();
  }


  /**
   * Constructor.
   *
   * @param doc The document for the editor.
   */
  public RTextAreaBase(AbstractDocument doc) {
    super(doc);
    init();
  }


  /**
   * Constructor.
   *
   * @param text The initial text to display.
   */
  public RTextAreaBase(String text) {
    // Don't call super(text) to avoid NPE due to our funky RTextAreaUI...
    init();
    setText(text);
  }


  /**
   * Constructor.
   *
   * @param rows The number of rows to display.
   * @param cols The number of columns to display.
   * @throws IllegalArgumentException If either <code>rows</code> or
   *         <code>cols</code> is negative.
   */
  public RTextAreaBase(int rows, int cols) {
    super(rows, cols);
    init();
  }


  /**
   * Constructor.
   *
   * @param text The initial text to display.
   * @param rows The number of rows to display.
   * @param cols The number of columns to display.
   * @throws IllegalArgumentException If either <code>rows</code> or
   *         <code>cols</code> is negative.
   */
  public RTextAreaBase(String text, int rows, int cols) {
    // Don't call this super() due to NPE from our funky RTextAreaUI...
    //super(text, rows, cols);
    super(rows, cols);
    init();
    setText(text);
  }


  /**
   * Constructor.
   *
   * @param doc The document for the editor.
   * @param text The initial text to display.
   * @param rows The number of rows to display.
   * @param cols The number of columns to display.
   * @throws IllegalArgumentException If either <code>rows</code> or
   *         <code>cols</code> is negative.
   */
  public RTextAreaBase(AbstractDocument doc, String text, int rows,
              int cols) {
    // Don't call super() with text due to NPE from our funky RTextAreaUI...
    super(doc, null/*text*/, rows, cols);
    init();
    setText(text);
  }


  /**
   * Adds listeners that listen for changes to the current line, so we can
   * update our "current line highlight."  This is needed only because of an
   * apparent difference between the JRE 1.4.2 and 1.5.0 (needed on 1.4.2,
   * not needed on 1.5.0).
   */
  private void addCurrentLineHighlightListeners() {
    boolean add = true;
    MouseMotionListener[] mouseMotionListeners = getMouseMotionListeners();
    for (int i=0; i<mouseMotionListeners.length; i++) {
      if (mouseMotionListeners[i]==mouseListener) {
        add = false;
        break;
      }
    }
    if (add==true) {
      //System.err.println("Adding mouse motion listener!");
      addMouseMotionListener(mouseListener);
    }
    MouseListener[] mouseListeners = getMouseListeners();
    for (int i=0; i<mouseListeners.length; i++) {
      if (mouseListeners[i]==mouseListener) {
        add = false;
        break;
      }
    }
    if (add==true) {
      //System.err.println("Adding mouse listener!");
      addMouseListener(mouseListener);
    }
  }

  @Override
  public void addNotify() {
    super.addNotify();
    // If the caret is not on the first line, we must recalculate the line
    // highlight y-offset after the text area is sized.  This seems to be
    // the best way to do it.
    if (getCaretPosition() != 0) {
      SwingUtilities.invokeLater(new Runnable() {
        public void run() {
          System.out.println("Yo");
          possiblyUpdateCurrentLineHighlightLocation();
        }
      });
    }
  }


  /*
   * TODO: Figure out why RTextArea doesn't work with RTL orientation!
   */
//  public void applyComponentOrientation(ComponentOrientation orientation) {
//    super.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
//  }


  /**
   * Converts all instances of a number of spaces equal to a tab size
   * into a tab in this text area.
   *
   * @see #convertTabsToSpaces
   * @see #getTabsEmulated
   * @see #setTabsEmulated
   */
  public void convertSpacesToTabs() {

    // FIXME:  This is inefficient and will yield an OutOfMemoryError if
    // done on a large document.  We should scan 1 line at a time and
    // replace; it'll be slower but safer.

    int caretPosition = getCaretPosition();
    int tabSize = getTabSize();
    String tabInSpaces = "";
    for (int i=0; i<tabSize; i++)
      tabInSpaces += " ";
    String text = getText();
    setText(text.replaceAll(tabInSpaces, "\t"));
    int newDocumentLength = getDocument().getLength();

    // Place the caret back in its proper position.
    if (caretPosition<newDocumentLength)
      setCaretPosition(caretPosition);
    else
      setCaretPosition(newDocumentLength-1);

  }


  /**
   * Converts all instances of a tab into a number of spaces equivalent
   * to a tab in this text area.
   *
   * @see #convertSpacesToTabs
   * @see #getTabsEmulated
   * @see #setTabsEmulated
   */
  public void convertTabsToSpaces() {

    // FIXME:  This is inefficient and will yield an OutOfMemoryError if
    // done on a large document.  We should scan 1 line at a time and
    // replace; it'll be slower but safer.

    int caretPosition = getCaretPosition();
    int tabSize = getTabSize();
    StringBuilder tabInSpaces = new StringBuilder();
    for (int i=0; i<tabSize; i++)
      tabInSpaces.append(' ');
    String text = getText();
    setText(text.replaceAll("\t", tabInSpaces.toString()));

    // Put caret back at same place in document.
    setCaretPosition(caretPosition);

  }


  /**
   * Returns the caret event/mouse listener for <code>RTextArea</code>s.
   *
   * @return The caret event/mouse listener.
   */
  protected abstract RTAMouseListener createMouseListener();


  /**
   * Returns the a real UI to install on this text component.  Subclasses
   * can override this method to return an extended version of
   * <code>RTextAreaUI</code>.
   *
   * @return The UI.
   */
  protected abstract RTextAreaUI createRTextAreaUI();


  /**
   * Forces the current line highlight to be repainted.  This hack is
   * necessary for those situations when the view (appearance) changes
   * but the caret's location hasn't (and thus the current line highlight
   * coordinates won't get changed).  Examples of this are when
   * word wrap is toggled and when syntax styles are changed in an
   * <code>RSyntaxTextArea</code>.
   */
  protected void forceCurrentLineHighlightRepaint() {
    // Check isShowing() to prevent BadLocationException
    // in constructor if linewrap is set to true.
    if (isShowing()) {
      // Changing previousCaretY makes us sure to get a repaint.
      previousCaretY = -1;
      // Trick it into checking for the need to repaint by firing
      // a false caret event.
      fireCaretUpdate(mouseListener);
    }
  }


  /**
   * Returns the <code>java.awt.Color</code> used as the background color for
   * this text area.  If a <code>java.awt.Image</code> image is currently
   * being used instead, <code>null</code> is returned.
   *
   * @return The current background color, or <code>null</code> if an image
   *         is currently the background.
   */
  @Override
  public final Color getBackground() {
    Object bg = getBackgroundObject();
    return (bg instanceof Color) ? (Color)bg : null;
  }

  /**
   * Returns the image currently used for the background.
   * If the current background is currently a <code>java.awt.Color</code> and
   * not a <code>java.awt.Image</code>, then <code>null</code> is returned.
   *
   * @return A <code>java.awt.Image</code> used for the background, or
   *         <code>null</code> if the background is not an image.
   * @see #setBackgroundImage
   */
  public final Image getBackgroundImage() {
    Object bg = getBackgroundObject();
    return (bg instanceof Image) ? (Image)bg : null;
  }


  /**
   * Returns the <code>Object</code> representing the background for all
   * documents in this tabbed pane; either a <code>java.awt.Color</code> or a
   * <code>java.lang.Image</code> containing the image used as the background
   * for this text area.
   *
   * @return The <code>Object</code> used for the background.
   * @see #setBackgroundObject(Object newBackground)
   */
  public final Object getBackgroundObject() {
    if (backgroundPainter==null)
      return null;
    return (backgroundPainter instanceof ImageBackgroundPainterStrategy) ?
      (Object)((ImageBackgroundPainterStrategy)backgroundPainter).
          getMasterImage() :
      (Object)((ColorBackgroundPainterStrategy)backgroundPainter).
          getColor();
  }


  /**
   * Gets the line number that the caret is on.
   *
   * @return The zero-based line number that the caret is on.
   */
  public final int getCaretLineNumber() {
    try {
      return getLineOfOffset(getCaretPosition());
    } catch (BadLocationException ble) {
      return 0; // Never happens
    }
  }


  /**
   * Gets the position from the beginning of the current line that the caret
   * is on.
   *
   * @return The zero-based position from the beginning of the current line
   *         that the caret is on.
   */
  public final int getCaretOffsetFromLineStart() {
    try {
      int pos = getCaretPosition();
      return pos - getLineStartOffset(getLineOfOffset(pos));
    } catch (BadLocationException ble) {
      return 0; // Never happens
    }
  }


  /**
   * Returns the color being used to highlight the current line.  Note that
   * if highlighting the current line is turned off, you will not be seeing
   * this highlight.
   *
   * @return The color being used to highlight the current line.
   * @see #getHighlightCurrentLine()
   * @see #setHighlightCurrentLine(boolean)
   * @see #setCurrentLineHighlightColor
   */
  public Color getCurrentLineHighlightColor() {
    return currentLineColor;
  }


  /**
   * Returns the default caret color.
   *
   * @return The default caret color.
   */
  public static final Color getDefaultCaretColor() {
    return DEFAULT_CARET_COLOR;
  }


  /**
   * Returns the "default" color for highlighting the current line.  Note
   * that this color was chosen only because it looks nice (to me) against a
   * white background.
   *
   * @return The default color for highlighting the current line.
   */
  public static final Color getDefaultCurrentLineHighlightColor() {
    return DEFAULT_CURRENT_LINE_HIGHLIGHT_COLOR;
  }


  /**
   * Returns the default font for text areas.
   *
   * @return The default font.
   */
  public static final Font getDefaultFont() {

    // Use StyleContext to get a composite font for better Asian language
    // support; see Sun bug S282887.
    StyleContext sc = StyleContext.getDefaultStyleContext();
    Font font = null;

    if (isOSX()) {
      // Snow Leopard (1.6) uses Menlo as default monospaced font,
      // pre-Snow Leopard used Monaco.
      font = sc.getFont("Menlo", Font.PLAIN, 12);
      if (!"Menlo".equals(font.getFamily())) {
        font = sc.getFont("Monaco", Font.PLAIN, 12);
        if (!"Monaco".equals(font.getFamily())) { // Shouldn't happen
          font = sc.getFont("Monospaced", Font.PLAIN, 13);
        }
      }
    }
    else {
      // Consolas added in Vista, used by VS2010+.
      font = sc.getFont("Consolas", Font.PLAIN, 13);
      if (!"Consolas".equals(font.getFamily())) {
        font = sc.getFont("Monospaced", Font.PLAIN, 13);
      }
    }

    //System.out.println(font.getFamily() + ", " + font.getName());
    return font;

  }


  /**
   * Returns the default foreground color for text in this text area.
   *
   * @return The default foreground color.
   */
  public static final Color getDefaultForeground() {
    return Color.BLACK;
  }


  /**
   * Returns the default color for the margin line.
   *
   * @return The default margin line color.
   * @see #getMarginLineColor()
   * @see #setMarginLineColor(Color)
   */
  public static final Color getDefaultMarginLineColor() {
    return DEFAULT_MARGIN_LINE_COLOR;
  }


  /**
   * Returns the default margin line position.
   *
   * @return The default margin line position.
   * @see #getMarginLinePosition
   * @see #setMarginLinePosition
   */
  public static final int getDefaultMarginLinePosition() {
    return DEFAULT_MARGIN_LINE_POSITION;
  }


  /**
   * Returns the default tab size, in spaces.
   *
   * @return The default tab size.
   */
  public static final int getDefaultTabSize() {
    return DEFAULT_TAB_SIZE;
  }


  /**
   * Returns whether the current line highlight is faded.
   *
   * @return Whether the current line highlight is faded.
   * @see #setFadeCurrentLineHighlight
   */
  public boolean getFadeCurrentLineHighlight() {
    return fadeCurrentLineHighlight;
  }


  /**
   * Returns whether or not the current line is highlighted.
   *
   * @return Whether or the current line is highlighted.
   * @see #setHighlightCurrentLine(boolean)
   * @see #getCurrentLineHighlightColor
   * @see #setCurrentLineHighlightColor
   */
  public boolean getHighlightCurrentLine() {
    return highlightCurrentLine;
  }


  /**
   * Returns the offset of the last character of the line that the caret is
   * on.
   *
   * @return The last offset of the line that the caret is currently on.
   */
  public final int getLineEndOffsetOfCurrentLine() {
    try {
      return getLineEndOffset(getCaretLineNumber());
    } catch (BadLocationException ble) {
      return 0; // Never happens
    }
  }


  /**
   * Returns the height of a line of text in this text area.
   *
   * @return The height of a line of text.
   */
  public int getLineHeight() {
    return getRowHeight();
  }


  /**
   * Returns the offset of the first character of the line that the caret is
   * on.
   *
   * @return The first offset of the line that the caret is currently on.
   */
  public final int getLineStartOffsetOfCurrentLine() {
    try {
      return getLineStartOffset(getCaretLineNumber());
    } catch (BadLocationException ble) {
      return 0; // Never happens
    }
  }


  /**
   * Returns the color used to paint the margin line.
   *
   * @return The margin line color.
   * @see #setMarginLineColor(Color)
   */
  public Color getMarginLineColor() {
    return marginLineColor;
  }


  /**
   * Returns the margin line position (in pixels) from the left-hand side of
   * the text area.
   *
   * @return The margin line position.
   * @see #getDefaultMarginLinePosition
   * @see #setMarginLinePosition
   */
  public int getMarginLinePixelLocation() {
    return marginLineX;
  }


  /**
   * Returns the margin line position (which is the number of 'm' widths in
   * the current font the margin line is over).
   *
   * @return The margin line position.
   * @see #getDefaultMarginLinePosition
   * @see #setMarginLinePosition
   */
  public int getMarginLinePosition() {
    return marginSizeInChars;
  }


  /**
   * Returns whether selection edges are rounded in this text area.
   *
   * @return Whether selection edges are rounded.
   * @see #setRoundedSelectionEdges(boolean)
   */
  public boolean getRoundedSelectionEdges() {
    return roundedSelectionEdges;
  }


  /**
   * Returns whether or not tabs are emulated with spaces (i.e., "soft"
   * tabs).
   *
   * @return <code>true</code> if tabs are emulated with spaces;
   *         <code>false</code> if they aren't.
   * @see #setTabsEmulated
   */
  public boolean getTabsEmulated() {
    return tabsEmulatedWithSpaces;
  }


  /**
   * Initializes this text area.
   */
  protected void init() {

    // Sets the UI.  Note that setUI() is overridden in RTextArea to only
    // update the popup menu; this method must be called to set the real
    // UI.  This is done because the look and feel of an RTextArea is
    // independent of the installed Java look and feels.
    setRTextAreaUI(createRTextAreaUI());

    // So we get notified when the component is resized.
    enableEvents(AWTEvent.COMPONENT_EVENT_MASK|AWTEvent.KEY_EVENT_MASK);

    // Defaults for various properties.
    setHighlightCurrentLine(true);
    setCurrentLineHighlightColor(getDefaultCurrentLineHighlightColor());
    setMarginLineEnabled(false);
    setMarginLineColor(getDefaultMarginLineColor());
    setMarginLinePosition(getDefaultMarginLinePosition());
    setBackgroundObject(Color.WHITE);
    setWrapStyleWord(true);// We only support wrapping at word boundaries.
    setTabSize(5);
    setForeground(Color.BLACK);
    setTabsEmulated(false);

    // Stuff needed by the caret listener below.
    previousCaretY = currentCaretY = getInsets().top;

    // Stuff to highlight the current line.
    mouseListener = createMouseListener();
    // Also acts as a focus listener so we can update our shared actions
    // (cut, copy, etc. on the popup menu).
    addFocusListener(mouseListener);
    addCurrentLineHighlightListeners();

  }


  /**
   * Returns whether or not the margin line is being painted.
   *
   * @return Whether or not the margin line is being painted.
   * @see #setMarginLineEnabled
   */
  public boolean isMarginLineEnabled() {
    return marginLineEnabled;
  }


  /**
   * Returns whether the OS we're running on is OS X.
   *
   * @return Whether the OS we're running on is OS X.
   */
  public static boolean isOSX() {
    // Recommended at:
    // http://developer.apple.com/mac/library/technotes/tn2002/tn2110.html
    String osName = System.getProperty("os.name").toLowerCase();
    return osName.startsWith("mac os x");
  }


  /**
   * Paints the text area.
   *
   * @param g The graphics context with which to paint.
   */
  @Override
  protected void paintComponent(Graphics g) {

    //long startTime = System.currentTimeMillis();

    backgroundPainter.paint(g, getVisibleRect());

    // Paint the main part of the text area.
    TextUI ui = getUI();
    if (ui != null) {
      // Not allowed to modify g, so make a copy.
      Graphics scratchGraphics = g.create();
      try {
        ui.update(scratchGraphics, this);
      } finally {
        scratchGraphics.dispose();
      }
    }

    //long endTime = System.currentTimeMillis();
    //System.err.println(endTime-startTime);

  }


  /**
   * Updates the current line highlight location.
   */
  protected void possiblyUpdateCurrentLineHighlightLocation() {

    int width = getWidth();
    int lineHeight = getLineHeight();
    int dot = getCaretPosition();

    // If we're wrapping lines we need to check the actual y-coordinate
    // of the caret, not just the line number, since a single logical
    // line can span multiple physical lines.
    if (getLineWrap()) {
      try {
        Rectangle temp = modelToView(dot);
        if (temp!=null) {
          currentCaretY = temp.y;
        }
      } catch (BadLocationException ble) {
        ble.printStackTrace(); // Should never happen.
      }
    }

    // No line wrap - we can simply check the line number (quicker).
    else {
//      Document doc = getDocument();
//      if (doc!=null) {
//        Element map = doc.getDefaultRootElement();
//        int caretLine = map.getElementIndex(dot);
//        Rectangle alloc = ((RTextAreaUI)getUI()).
//                      getVisibleEditorRect();
//        if (alloc!=null)
//          currentCaretY = alloc.y + caretLine*lineHeight;
//      }
// Modified for code folding requirements
try {
  Rectangle temp = modelToView(dot);
  if (temp!=null) {
    currentCaretY = temp.y;
  }
} catch (BadLocationException ble) {
  ble.printStackTrace(); // Should never happen.
}
    }

    // Repaint current line (to fill in entire highlight), and old line
    // (to erase entire old highlight) if necessary.  Always repaint
    // current line in case selection is added or removed.
    repaint(0,currentCaretY, width,lineHeight);
    if (previousCaretY!=currentCaretY) {
      repaint(0,previousCaretY, width,lineHeight);
    }

    previousCaretY = currentCaretY;

  }


  /**
   * Overridden so we can tell when the text area is resized and update the
   * current-line highlight, if necessary (i.e., if it is enabled and if
   * lineWrap is enabled.
   *
   * @param e The component event about to be sent to all registered
   *        <code>ComponentListener</code>s.
   */
  @Override
  protected void processComponentEvent(ComponentEvent e) {

    // In line wrap mode, resizing the text area means that the caret's
    // "line" could change - not to a different logical line, but a
    // different physical one.  So, here we force a repaint of the current
    // line's highlight if necessary.
    if (e.getID()==ComponentEvent.COMPONENT_RESIZED &&
        getLineWrap()==true && getHighlightCurrentLine()) {
      previousCaretY = -1// So we are sure to repaint.
      fireCaretUpdate(mouseListener);
    }

    super.processComponentEvent(e);

  }


  /**
   * Sets the background color of this text editor.  Note that this is
   * equivalent to calling <code>setBackgroundObject(bg)</code>.
   *
   * NOTE:  the opaque property is set to <code>true</code> when the
   * background is set to a color (by this method).  When an image is used
   * for the background, opaque is set to false.  This is because
   * we perform better when setOpaque is true, but if we use an
   * image for the background when opaque is true, we get on-screen
   * garbage when the user scrolls via the arrow keys.  Thus we
   * need setOpaque to be false in that case.<p>
   * You never have to change the opaque property yourself; it is always done
   * for you.
   *
   * @param bg The color to use as the background color.
   */
  @Override
  public void setBackground(Color bg) {
    Object oldBG = getBackgroundObject();
    if (oldBG instanceof Color) { // Just change color of strategy.
      ((ColorBackgroundPainterStrategy)backgroundPainter).
            setColor(bg);
    }
    else { // Was an image painter...
      backgroundPainter = new ColorBackgroundPainterStrategy(bg);
    }
    setOpaque(true);
    firePropertyChange("background", oldBG, bg);
    repaint();
  }


  /**
   * Sets this image as the background image.  This method fires a
   * property change event of type {@link #BACKGROUND_IMAGE_PROPERTY}.<p>
   *
   * NOTE:  the opaque property is set to <code>true</code> when the
   * background is set to a color.  When an image is used for the
   * background (by this method), opaque is set to false.  This is because
   * we perform better when setOpaque is true, but if we use an
   * image for the background when opaque is true, we get on-screen
   * garbage when the user scrolls via the arrow keys.  Thus we
   * need setOpaque to be false in that case.<p>
   * You never have to change the opaque property yourself; it is always done
   * for you.
   *
   * @param image The image to use as this text area's background.
   * @see #getBackgroundImage
   */
  public void setBackgroundImage(Image image) {
    Object oldBG = getBackgroundObject();
    if (oldBG instanceof Image) { // Just change image being displayed.
      ((ImageBackgroundPainterStrategy)backgroundPainter).
          setImage(image);
    }
    else { // Was a color strategy...
      ImageBackgroundPainterStrategy strategy =
        new BufferedImageBackgroundPainterStrategy(this);
      strategy.setImage(image);
      backgroundPainter = strategy;
    }
    setOpaque(false);
    firePropertyChange(BACKGROUND_IMAGE_PROPERTY, oldBG, image);
    repaint();
  }


  /**
   * Makes the background into this <code>Object</code>.
   *
   * @param newBackground The <code>java.awt.Color</code> or
   *        <code>java.awt.Image</code> object. If <code>newBackground</code>
   *        is not either of these, the background is set to plain white.
   */
  public void setBackgroundObject(Object newBackground) {
    if (newBackground instanceof Color) {
      setBackground((Color)newBackground);
    }
    else if (newBackground instanceof Image) {
      setBackgroundImage((Image)newBackground);
    }
    else {
      setBackground(Color.WHITE);
    }

  }


  /*
   * TODO: Figure out why RTextArea doesn't work with RTL (e.g. Arabic)
   * and fix it!
   */
//  public void setComponentOrientation(ComponentOrientation o) {
//    if (!o.isLeftToRight()) {
//      o = ComponentOrientation.LEFT_TO_RIGHT;
//    }
//    super.setComponentOrientation(o);
//  }


  /**
   * Sets the color to use to highlight the current line.  Note that if
   * highlighting the current line is turned off, you will not be able to
   * see this highlight.  This method fires a property change of type
   * {@link #CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY}.
   *
   * @param color The color to use to highlight the current line.
   * @throws NullPointerException if <code>color</code> is <code>null</code>.
   * @see #getHighlightCurrentLine()
   * @see #setHighlightCurrentLine(boolean)
   * @see #getCurrentLineHighlightColor
   */
  public void setCurrentLineHighlightColor(Color color) {
    if (color==null)
      throw new NullPointerException();
    if (!color.equals(currentLineColor)) {
      Color old = currentLineColor;
      currentLineColor = color;
      firePropertyChange(CURRENT_LINE_HIGHLIGHT_COLOR_PROPERTY,
              old, color);
    }
  }


  /**
   * Sets whether the current line highlight should have a "fade" effect.
   * This method fires a property change event of type
   * <code>CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY</code>.
   *
   * @param fade Whether the fade effect should be enabled.
   * @see #getFadeCurrentLineHighlight
   */
  public void setFadeCurrentLineHighlight(boolean fade) {
    if (fade!=fadeCurrentLineHighlight) {
      fadeCurrentLineHighlight = fade;
      if (getHighlightCurrentLine())
        forceCurrentLineHighlightRepaint();
      firePropertyChange(CURRENT_LINE_HIGHLIGHT_FADE_PROPERTY,
              !fade, fade);
    }
  }


  /**
   * Sets the font for this text area.  This is overridden only so that we
   * can update the size of the "current line highlight" and the location of
   * the "margin line," if necessary.
   *
   * @param font The font to use for this text component.
   */
  @Override
  public void setFont(Font font) {
    if (font!=null && font.getSize()<=0) {
      throw new IllegalArgumentException("Font size must be > 0");
    }
    super.setFont(font);
    if (font!=null) {
      updateMarginLineX();
      if (highlightCurrentLine)
        possiblyUpdateCurrentLineHighlightLocation();
    }
  }


  /**
   * Sets whether or not the current line is highlighted.  This method
   * fires a property change of type {@link #HIGHLIGHT_CURRENT_LINE_PROPERTY}.
   *
   * @param highlight Whether or not to highlight the current line.
   * @see #getHighlightCurrentLine()
   * @see #getCurrentLineHighlightColor
   * @see #setCurrentLineHighlightColor
   */
  public void setHighlightCurrentLine(boolean highlight) {
    if (highlight!=highlightCurrentLine) {
      highlightCurrentLine = highlight;
      firePropertyChange(HIGHLIGHT_CURRENT_LINE_PROPERTY,
              !highlight, highlight);
      repaint(); // Repaint entire width of line.
    }
  }


  /**
   * Sets whether or not word wrap is enabled.  This is overridden so that
   * the "current line highlight" gets updated if it needs to be.
   *
   * @param wrap Whether or not word wrap should be enabled.
   */
  @Override
  public void setLineWrap(boolean wrap) {
    super.setLineWrap(wrap);
    forceCurrentLineHighlightRepaint();
  }


  /**
   * Overridden to update the current line highlight location.
   *
   * @param insets The new insets.
   */
  @Override
  public void setMargin(Insets insets) {
    Insets old = getInsets();
    int oldTop = old!=null ? old.top : 0;
    int newTop = insets!=null ? insets.top : 0;
    if (oldTop!=newTop) {
      // The entire editor will be automatically repainted if it is
      // visible, so no need to call repaint() or forceCurrentLine...().
      previousCaretY = currentCaretY = newTop;
    }
    super.setMargin(insets);
  }


  /**
   * Sets the color used to paint the margin line.
   *
   * @param color The new margin line color.
   * @see #getDefaultMarginLineColor()
   * @see #getMarginLineColor()
   */
  public void setMarginLineColor(Color color) {
    marginLineColor = color;
    if (marginLineEnabled) {
      Rectangle visibleRect = getVisibleRect();
      repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
    }
  }


  /**
   * Enables or disables the margin line.
   *
   * @param enabled Whether or not the margin line should be enabled.
   * @see #isMarginLineEnabled
   */
  public void setMarginLineEnabled(boolean enabled) {
    if (enabled!=marginLineEnabled) {
      marginLineEnabled = enabled;
      if (marginLineEnabled) {
        Rectangle visibleRect = getVisibleRect();
        repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
      }
    }
  }


  /**
   * Sets the number of 'm' widths the margin line is over.
   *
   * @param size The margin size.
   * #see #getDefaultMarginLinePosition
   * @see #getMarginLinePosition
   */
  public void setMarginLinePosition(int size) {
    marginSizeInChars = size;
    if (marginLineEnabled) {
      Rectangle visibleRect = getVisibleRect();
      repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
      updateMarginLineX();
      repaint(marginLineX,visibleRect.y, 1,visibleRect.height);
    }
  }


  /**
   * Sets whether the edges of selections are rounded in this text area.
   * This method fires a property change of type
   * {@link #ROUNDED_SELECTION_PROPERTY}.
   *
   * @param rounded Whether selection edges should be rounded.
   * @see #getRoundedSelectionEdges()
   */
  public void setRoundedSelectionEdges(boolean rounded) {
    if (roundedSelectionEdges!=rounded) {
      roundedSelectionEdges = rounded;
      Caret c = getCaret();
      if (c instanceof ConfigurableCaret) {
        ((ConfigurableCaret)c).setRoundedSelectionEdges(rounded);
        if (c.getDot()!=c.getMark()) { // e.g., there's is a selection
          repaint();
        }
      }
      firePropertyChange(ROUNDED_SELECTION_PROPERTY, !rounded,
                      rounded);
    }
  }


  /**
   * Sets the UI for this <code>RTextArea</code>.  Note that, for instances
   * of <code>RTextArea</code>, <code>setUI</code> only updates the popup
   * menu; this is because <code>RTextArea</code>s' look and feels are
   * independent of the Java Look and Feel.  This method is here so
   * subclasses can set a UI (subclass of <code>RTextAreaUI</code>) if they
   * have to.
   *
   * @param ui The new UI.
   * @see #setUI
   */
  protected void setRTextAreaUI(RTextAreaUI ui) {

    super.setUI(ui);

    // Workaround as setUI makes the text area opaque, even if we don't
    // want it to be.
    setOpaque(getBackgroundObject() instanceof Color);

  }


  /**
   * Changes whether or not tabs should be emulated with spaces (i.e., soft
   * tabs).  Note that this affects all tabs inserted AFTER this call, not
   * tabs already in the document.  For that, see
   * {@link #convertTabsToSpaces} and {@link #convertSpacesToTabs}.
   *
   * @param areEmulated Whether or not tabs should be emulated with spaces.
   * @see #convertSpacesToTabs
   * @see #convertTabsToSpaces
   * @see #getTabsEmulated
   */
  public void setTabsEmulated(boolean areEmulated) {
    tabsEmulatedWithSpaces = areEmulated;
  }


  /**
   * Workaround, since in JDK1.4 it appears that <code>setTabSize()</code>
   * doesn't work for a <code>JTextArea</code> unless you use the constructor
   * specifying the number of rows and columns...<p>
   * Sets the number of characters to expand tabs to. This will be multiplied
   * by the maximum advance for variable width fonts. A PropertyChange event
   * ("tabSize") is fired when the tab size changes.
   *
   * @param size Number of characters to expand to.
   */
  @Override
  public void setTabSize(int size) {
    super.setTabSize(size);
    boolean b = getLineWrap();
    setLineWrap(!b);
    setLineWrap(b);
  }


  /**
   * This is here so subclasses such as <code>RSyntaxTextArea</code> that
   * have multiple fonts can define exactly what it means, for example, for
   * the margin line to be "80 characters" over.
   */
  protected void updateMarginLineX() {
    Font font = getFont();
    if (font == null) {
      marginLineX = 0;
      return;
    }
    marginLineX = getFontMetrics(font).charWidth('m') *
                        marginSizeInChars;
  }


  /**
   * Returns the y-coordinate of the specified line.
   *
   * @param line The line number.
   * @return The y-coordinate of the top of the line, or <code>-1</code> if
   *         this text area doesn't yet have a positive size or the line is
   *         hidden (i.e. from folding).
   * @throws BadLocationException If <code>line</code> isn't a valid line
   *         number for this document.
   */
  public int yForLine(int line) throws BadLocationException {
    return ((RTextAreaUI)getUI()).yForLine(line);
  }


  /**
   * Returns the y-coordinate of the line containing an offset.
   *
   * @param offs The offset info the document.
   * @return The y-coordinate of the top of the offset, or <code>-1</code> if
   *         this text area doesn't yet have a positive size or the line is
   *         hidden (i.e. from folding).
   * @throws BadLocationException If <code>offs</code> isn't a valid offset
   *         into the document.
   */
  public int yForLineContaining(int offs) throws BadLocationException {
    return ((RTextAreaUI)getUI()).yForLineContaining(offs);
  }


  protected class RTAMouseListener extends CaretEvent implements
          MouseListener, MouseMotionListener, FocusListener {

    RTAMouseListener(RTextAreaBase textArea) {
      super(textArea);
    }

    public void focusGained(FocusEvent e) {}
    public void focusLost(FocusEvent e) {}
    public void mouseDragged(MouseEvent e) {}
    public void mouseMoved(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}

    @Override
    public int getDot() {
      return dot;
    }

    @Override
    public int getMark() {
      return mark;
    }

    protected int dot;
    protected int mark;

  }


}
TOP

Related Classes of org.fife.ui.rtextarea.RTextAreaBase$RTAMouseListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.