All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.ui.rtextarea.Gutter Maven / Gradle / Ivy

Go to download

RSyntaxTextArea is the syntax highlighting text editor for Swing applications. Features include syntax highlighting for 40+ languages, code folding, code completion, regex find and replace, macros, code templates, undo/redo, line numbering and bracket matching.

There is a newer version: 3.5.1
Show newest version
/*
 * 02/17/2009
 *
 * Gutter.java - Manages line numbers, icons, etc. on the left-hand side of
 * an RTextArea.
 * Copyright (C) 2009 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
 */
package org.fife.ui.rtextarea;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;

import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;


/**
 * The gutter is the component on the left-hand side of the text area that
 * displays optional information such as line numbers and icons (for bookmarks,
 * debugging breakpoints, error markers, etc.).
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class Gutter extends JComponent {

	/**
	 * The text area.
	 */
	private RTextArea textArea;

	/**
	 * Renders line numbers.
	 */
	private LineNumberList lineNumberList;

	/**
	 * Renders bookmark icons, breakpoints, error icons, etc.
	 */
	private IconRowHeader iconArea;

	/**
	 * Listens for events in our text area.
	 */
	private TextAreaListener listener;


	/**
	 * Constructor.
	 *
	 * @param textArea The parent text area.
	 */
	public Gutter(RTextArea textArea) {

		listener = new TextAreaListener();
		setTextArea(textArea);
		setLayout(new BorderLayout());
		if (this.textArea!=null) {
			// Enable line numbers our first time through if they give us
			// a text area.
			setLineNumbersEnabled(true);
		}

		setBorder(new GutterBorder(0, 0, 0, 1)); // Assume ltr

		Color bg = null;
		if (textArea!=null) {
			bg = textArea.getBackground(); // May return null if image bg
		}
		setBackground(bg!=null ? bg : Color.WHITE);

	}


	/**
	 * Adds an icon that tracks an offset in the document, and is displayed
	 * adjacent to the line numbers.  This is useful for marking things such
	 * as source code errors.
	 *
	 * @param line The line to track (zero-based).
	 * @param icon The icon to display.  This should be small (say 16x16).
	 * @return A tag for this icon.
	 * @throws BadLocationException If offs is an invalid offset
	 *         into the text area.
	 * @see #addOffsetTrackingIcon(int, Icon)
	 * @see #removeTrackingIcon(GutterIconInfo)
	 */
	public GutterIconInfo addLineTrackingIcon(int line, Icon icon)
											throws BadLocationException {
		int offs = textArea.getLineStartOffset(line);
		return addOffsetTrackingIcon(offs, icon);
	}


	/**
	 * Adds an icon that tracks an offset in the document, and is displayed
	 * adjacent to the line numbers.  This is useful for marking things such
	 * as source code errors.
	 *
	 * @param offs The offset to track.
	 * @param icon The icon to display.  This should be small (say 16x16).
	 * @return A tag for this icon.
	 * @throws BadLocationException If offs is an invalid offset
	 *         into the text area.
	 * @see #addLineTrackingIcon(int, Icon)
	 * @see #removeTrackingIcon(GutterIconInfo)
	 */
	public GutterIconInfo addOffsetTrackingIcon(int offs, Icon icon)
												throws BadLocationException {
		return iconArea.addOffsetTrackingIcon(offs, icon);
	}


	/**
	 * Clears the active line range.
	 *
	 * @see #setActiveLineRange(int, int)
	 */
	public void clearActiveLineRange() {
		iconArea.clearActiveLineRange();
	}


	/**
	 * Returns the icon to use for bookmarks.
	 *
	 * @return The icon to use for bookmarks.  If this is null,
	 *         bookmarking is effectively disabled.
	 * @see #setBookmarkIcon(Icon)
	 * @see #isBookmarkingEnabled()
	 */
	public Icon getBookmarkIcon() {
		return iconArea.getBookmarkIcon();
	}


	/**
	 * Returns the bookmarks known to this gutter.
	 *
	 * @return The bookmarks.  If there are no bookmarks, an empty array is
	 *         returned.
	 */
	public GutterIconInfo[] getBookmarks() {
		return iconArea.getBookmarks();
	}


	/**
	 * Returns the color of the "border" line.
	 *
	 * @return The color.
	 * @see #setBorderColor(Color)
	 */
	public Color getBorderColor() {
		return ((GutterBorder)getBorder()).getColor();
	}


	/**
	 * Returns the color to use to paint line numbers.
	 *
	 * @return The color used when painting line numbers.
	 * @see #setLineNumberColor(Color)
	 */
	public Color getLineNumberColor() {
		return lineNumberList.getForeground();
	}


	/**
	 * Returns the font used for line numbers.
	 *
	 * @return The font used for line numbers.
	 * @see #setLineNumberFont(Font)
	 */
	public Font getLineNumberFont() {
		return lineNumberList.getFont();
	}


	/**
	 * Returns the starting line's line number.  The default value is
	 * 1.
	 *
	 * @return The index
	 * @see #setLineNumberingStartIndex(int)
	 */
	public int getLineNumberingStartIndex() {
		return lineNumberList.getLineNumberingStartIndex();
	}


	/**
	 * Returns true if the line numbers are enabled and visible.
	 *
	 * @return Whether or not line numbers are visible.
	 */
	public boolean getLineNumbersEnabled() {
		for (int i=0; ip is invalid.
	 */
	public Object[] getTrackingIcons(Point p) throws BadLocationException {
		int offs = textArea.viewToModel(new Point(0, p.y));
		int line = textArea.getLineOfOffset(offs);
		return iconArea.getTrackingIcons(line);
	}


	/**
	 * Returns whether bookmarking is enabled.
	 *
	 * @return Whether bookmarking is enabled.
	 * @see #setBookmarkingEnabled(boolean)
	 */
	public boolean isBookmarkingEnabled() {
		return iconArea.isBookmarkingEnabled();
	}


	/**
	 * Returns whether the icon row header is enabled.
	 *
	 * @return Whether the icon row header is enabled.
	 */
	public boolean isIconRowHeaderEnabled() {
		for (int i=0; inull,
	 *        bookmarking is effectively disabled.
	 * @see #getBookmarkIcon()
	 * @see #isBookmarkingEnabled()
	 */
	public void setBookmarkIcon(Icon icon) {
		iconArea.setBookmarkIcon(icon);
	}


	/**
	 * Sets whether bookmarking is enabled.  Note that a bookmarking icon
	 * must be set via {@link #setBookmarkIcon(Icon)} before bookmarks are
	 * truly enabled.
	 *
	 * @param enabled Whether bookmarking is enabled.
	 * @see #isBookmarkingEnabled()
	 * @see #setBookmarkIcon(Icon)
	 */
	public void setBookmarkingEnabled(boolean enabled) {
		iconArea.setBookmarkingEnabled(enabled);
		if (enabled && !isIconRowHeaderEnabled()) {
			setIconRowHeaderEnabled(true);
		}
	}


	/**
	 * Sets the color for the "border" line.
	 *
	 * @param color The new color.
	 * @see #getBorderColor()
	 */
	public void setBorderColor(Color color) {
		((GutterBorder)getBorder()).setColor(color);
		repaint();
	}


	/**
	 * {@inheritDoc}
	 */
	public void setComponentOrientation(ComponentOrientation o) {
		// Reuse the border to preserve its color.
		if (o.isLeftToRight()) {
			((GutterBorder)getBorder()).setEdges(0, 0, 0, 1);
		}
		else {
			((GutterBorder)getBorder()).setEdges(0, 1, 0, 0);
		}
		super.setComponentOrientation(o);
	}


	/**
	 * Toggles whether the icon row header (used for breakpoints, bookmarks,
	 * etc.) is enabled.
	 *
	 * @param enabled Whether the icon row header is enabled.
	 * @see #isIconRowHeaderEnabled()
	 */
	void setIconRowHeaderEnabled(boolean enabled) {
		if (iconArea!=null) {
			if (enabled) {
				add(iconArea, BorderLayout.LINE_START);
			}
			else {
				remove(iconArea);
			}
			revalidate();
		}
	}


	/**
	 * Sets the color to use to paint line numbers.
	 *
	 * @param color The color to use when painting line numbers.
	 * @see #getLineNumberColor()
	 */
	public void setLineNumberColor(Color color) {
		lineNumberList.setForeground(color);
	}


	/**
	 * Sets the font used for line numbers.
	 *
	 * @param font The font to use.  This cannot be null.
	 * @see #getLineNumberFont()
	 */
	public void setLineNumberFont(Font font) {
		if (font==null) {
			throw new IllegalArgumentException("font cannot be null");
		}
		lineNumberList.setFont(font);
	}


	/**
	 * Sets the starting line's line number.  The default value is
	 * 1.  Applications can call this method to change this value
	 * if they are displaying a subset of lines in a file, for example.
	 *
	 * @param index The new index.
	 * @see #getLineNumberingStartIndex()
	 */
	public void setLineNumberingStartIndex(int index) {
		lineNumberList.setLineNumberingStartIndex(index);
	}


	/**
	 * Toggles whether or not line numbers are visible.
	 *
	 * @param enabled Whether or not line numbers should be visible.
	 * @see #getLineNumbersEnabled()
	 */
	void setLineNumbersEnabled(boolean enabled) {
		if (lineNumberList!=null) {
			if (enabled) {
				add(lineNumberList);
			}
			else {
				remove(lineNumberList);
			}
			revalidate();
		}
	}


	/**
	 * Sets the text area being displayed.  This will clear any tracking
	 * icons currently displayed.
	 *
	 * @param textArea The text area.
	 */
	void setTextArea(RTextArea textArea) {
		if (this.textArea!=null) {
			listener.uninstall();
		}
		if (lineNumberList==null) {
			lineNumberList = new LineNumberList(textArea);
		}
		else {
			lineNumberList.setTextArea(textArea);
		}
		if (iconArea==null) {
			iconArea = new IconRowHeader(textArea);
		}
		else {
			iconArea.setTextArea(textArea);
		}
		if (textArea!=null) {
			listener.install(textArea);
		}
		this.textArea = textArea;
	}


	/**
	 * Programatically toggles whether there is a bookmark for the specified
	 * line.  If bookmarking is not enabled, this method does nothing.
	 *
	 * @param line The line.
	 * @return Whether a bookmark is now at the specified line.
	 * @throws BadLocationException If line is an invalid line
	 *         number in the text area.
	 */
	public boolean toggleBookmark(int line) throws BadLocationException {
		return iconArea.toggleBookmark(line);
	}


	/**
	 * The border used by the gutter.
	 */
	private static class GutterBorder extends EmptyBorder {

		private Color color;

		public GutterBorder(int top, int left, int bottom, int right) {
			super(top, left, bottom, right);
			color = new Color(221, 221, 221);
		}

		public Color getColor() {
			return color;
		}

		public void paintBorder(Component c, Graphics g, int x, int y,
								int width, int height) {
			g.setColor(color);
			if (left==1) {
				g.drawLine(0,0, 0,height);
			}
			else {
				g.drawLine(width-1,0, width-1,height);
			}
		}

		public void setColor(Color color) {
			this.color = color;
		}

		public void setEdges(int top, int left, int bottom, int right) {
			this.top = top;
			this.left = left;
			this.bottom = bottom;
			this.right = right;
		}

	}


	/**
	 * Listens for the text area resizing.
	 */
	/*
	 * This is necessary to keep child components the same height as the text
	 * area.  The worse case is when the user toggles word-wrap and it changes
	 * the height of the text area. In that case, if we listen for the
	 * "lineWrap" property change, we get notified BEFORE the text area
	 * decides on its new size, thus we cannot resize properly.  We listen
	 * instead for ComponentEvents so we change size after the text area has
	 * resized.
	 */
	private class TextAreaListener extends ComponentAdapter
						implements DocumentListener, PropertyChangeListener {

		private boolean installed;

		public void changedUpdate(DocumentEvent e) {}

		public void componentResized(java.awt.event.ComponentEvent e) {
			revalidate();
		}

		protected void handleDocumentEvent(DocumentEvent e) {
			for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy