/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2004 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

package org.gnu.gtk;

import java.util.Vector;

import org.gnu.gdk.Point;
import org.gnu.gdk.Rectangle;
import org.gnu.gdk.Window;
import org.gnu.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.Type;
import org.gnu.gtk.event.TextViewEvent;
import org.gnu.gtk.event.TextViewListener;
import org.gnu.glib.Handle;
import org.gnu.pango.TabArray;

/**
 * Widget for displaying textual data.
 * 
 * <h2>Conceptual Overview</h2>
 * 
 * <p>
 * java-gnome has an extremely powerful framework for multiline text editing.
 * The primary objects involved in the process are {@link TextBuffer}, which
 * represents the text being edited, and {@link TextView}, a widget which can
 * display a TextBuffer. Each buffer can be displayed by any number of views.
 * 
 * <p>
 * One of the important things to remember about text in java-gnome is that it's
 * in the UTF-8 encoding. This means that one character can be encoded as
 * multiple bytes. Character counts are usually referred to as offsets, while
 * byte counts are called indexes. If you confuse these two, things will work
 * fine with ASCII, but as soon as your buffer contains multibyte characters,
 * bad things will happen.
 * 
 * <p>
 * Text in a buffer can be marked with tags. A tag is an attribute that can be
 * applied to some range of text. For example, a tag might be called "bold" and
 * make the text inside the tag bold. However, the tag concept is more general
 * than that; tags don't have to affect appearance. They can instead affect the
 * behavior of mouse and key presses, "lock" a range of text so the user can't
 * edit it, or countless other things. A tag is represented by a {@link TextTag}
 * object. One TextTag can be applied to any number of text ranges in any number
 * of buffers.
 * 
 * <p>
 * Each tag is stored in a {@link TextTagTable}. A tag table defines a set of
 * tags that can be used together. Each buffer has one tag table associated with
 * it; only tags from that tag table can be used with the buffer. A single tag
 * table can be shared between multiple buffers, however.
 * 
 * <p>
 * Tags can have names, which is convenient sometimes (for example, you can name
 * your tag that makes things bold "bold"), but they can also be anonymous
 * (which is convenient if you're creating tags on-the-fly).
 * 
 * <p>
 * Most text manipulation is accomplished with iterators, represented by a
 * {@link TextIter}. An iterator represents a position between two characters
 * in the text buffer. Iterators are not valid indefinitely; whenever the buffer
 * is modified in a way that affects the number of characters in the buffer, all
 * outstanding iterators become invalid. (Note that deleting 5 characters and
 * then reinserting 5 still invalidates iterators, though you end up with the
 * same number of characters you pass through a state with a different number).
 * 
 * <p>
 * Because of this, iterators can't be used to preserve positions across buffer
 * modifications. To preserve a position, the {@link TextMark} object is ideal.
 * You can think of a mark as an invisible cursor or insertion point; it floats
 * in the buffer, saving a position. If the text surrounding the mark is
 * deleted, the mark remains in the position the text once occupied; if text is
 * inserted at the mark, the mark ends up either to the left or to the right of
 * the new text, depending on its gravity. The standard text cursor in
 * left-to-right languages is a mark with right gravity, because it stays to the
 * right of inserted text.
 * 
 * <p>
 * Like tags, marks can be either named or anonymous. There are two marks
 * built-in to {@link TextBuffer}; these are named "insert" and
 * "selection_bound" and refer to the insertion point and the boundary of the
 * selection which is not the insertion point, respectively. If no text is
 * selected, these two marks will be in the same position. You can manipulate
 * what is selected and where the cursor appears by moving these marks around.
 * 
 * <p>
 * Text buffers always contain at least one line, but may be empty (that is,
 * buffers can contain zero characters). The last line in the text buffer never
 * ends in a line separator (such as newline); the other lines in the buffer
 * always end in a line separator. Line separators count as characters when
 * computing character counts and character offsets. Note that some Unicode line
 * separators are represented with multiple bytes in UTF-8, and the
 * two-character sequence "\r\n" is also considered a line separator.
 */
public class TextView extends Container {

    /**
     * Class to handle returns from {@link #getLineYRange}. Contains the
     * starting y-coordinates of a line and the height of the line.
     */
    public final static class VerticalLineRange {
        private final int y;

        private final int height;

        public VerticalLineRange(int y, int height) {
            this.y = y;
            this.height = height;
        }

        /**
         * @return The y-coordinate of the line in buffer coordinates
         */
        public final int getY() {
            return y;
        }

        /**
         * @return The height of the line
         */
        public final int getHeight() {
            return height;
        }
    }

    /**
     * The list of objects interested in this widget's events
     */
    private Vector listeners;

    /**
     * Constructs a new TextView. If you don't specify a buffer before using it,
     * a default one will be created for you.
     */
    public TextView() {
        super(gtk_text_view_new());
    }

    /**
     * Creates a new TextView widget displaying the buffer buffer. One buffer
     * can be shared among many widgets.
     * 
     * @param buffer
     *            Buffer to use
     */
    public TextView(TextBuffer buffer) {
        super(gtk_text_view_new_with_buffer(buffer.getHandle()));
    }

    /**
     * Construct a TextView from a handle to a native resource.
     */
    public TextView(Handle handle) {
        super(handle);
    }

    /**
     * Internal static factory method to be used by Java-Gnome only.
     */
    public static TextView getTextView(Handle handle) {
        if (handle == null)
            return null;

        TextView obj = (TextView) getGObjectFromHandle(handle);
        if (obj == null)
            obj = new TextView(handle);

        return obj;
    }

    /**
     * Sets buffer as the buffer being displayed by the view.
     * 
     * @param buffer
     *            The new buffer to display
     */
    public void setBuffer(TextBuffer buffer) {
        gtk_text_view_set_buffer(getHandle(), buffer.getHandle());
    }

    /**
     * Returns the buffer being used
     * 
     * @return The buffer used in the widget.
     */
    public TextBuffer getBuffer() {
        Handle hndl = gtk_text_view_get_buffer(getHandle());
        return TextBuffer.getTextBuffer(hndl);
    }

    /**
     * Scrolls the view so that mark is on the screen in the position indicated
     * by xAlign and yAlign. An alignment of 0.0 indicates left or top, 1.0
     * indicates right or bottom, 0.5 means center.
     * 
     * @param mark
     *            A {@link TextMark}
     * @param withinMargin
     *            Margin as a fraction of screen size
     * @param useAlign
     *            Whether to use alignment arguments.
     * @param xAlign
     *            Horizontal alignment of mark within visible area.
     * @param yAlign :
     *            vertical alignment of mark within visible area
     */
    public void scrollToMark(TextMark mark, double withinMargin,
            boolean useAlign, double xAlign, double yAlign) {
        gtk_text_view_scroll_to_mark(getHandle(), mark.getHandle(),
                withinMargin, useAlign, xAlign, yAlign);
    }

    /**
     * Scrolls the view so that mark is on the screen. The text scrolls the
     * minimal distance to get the mark onscreen, possibly not scrolling at all.
     * The effective screen for purposes of this function is reduced by a margin
     * of size withinMargin.
     * 
     * @param mark
     *            A {@link TextMark}
     * @param withinMargin
     *            Margin as a fraction of screen size
     */
    public void scrollToMark(TextMark mark, double withinMargin) {
        gtk_text_view_scroll_to_mark(getHandle(), mark.getHandle(),
                withinMargin, false, 0.0, 0.0);
    }

    /**
     * Returns an iterator for the location pointed to by the coordinates
     * <code>x</code> and <code>y</code> within the buffer. Coordinates
     * obtained in relation to the widget must be converted to buffer
     * coordinates using windowToBufferCoords before passing them into this
     * method.
     * 
     * @param x
     *            x buffer coordinate
     * @param y
     *            y buffer coordinate
     * @return an iterator to a given x-y coordinate location
     */
    public TextIter getIterAtLocation(int x, int y) {
        Handle handle = gtk_text_view_get_iter_at_location(getHandle(), x, y);
        return TextIter.getTextIter(handle);
    }

    /**
     * Returns an iterator for the location pointed to by bufferCoords,
     * representing coordinates within the buffer. Coordinates obtained in
     * relation to the widget must be converted to buffer coordinates using
     * windowToBufferCoords.
     * 
     * @param bufferCoords
     *            a Point representing a pair of buffer coordinates
     * @return an iterator to a given x-y coordinate location
     */
    public TextIter getIterAtLocation(Point bufferCoords) {
        Handle handle = gtk_text_view_get_iter_at_location(getHandle(),
                bufferCoords.getX(), bufferCoords.getY());
        return TextIter.getTextIter(handle);
    }

    /**
     * Converts coordinates on the window identified by the <code>winType</code>
     * to buffer coordinates.
     * 
     * @param winType
     *            a {@link TextWindowType}.
     * @param xCoord
     *            the x-coordinate in relation to the Window win
     * @param yCoord
     *            the y-coordinate in relation to the Window win
     * @return a Point representing the buffer coordinates
     */
    public Point windowToBufferCoords(TextWindowType winType, int xCoord,
            int yCoord) {
        int[] bufferX = new int[1];
        int[] bufferY = new int[1];

        gtk_text_view_window_to_buffer_coords(this.getHandle(), winType
                .getValue(), xCoord, yCoord, bufferX, bufferY);

        return new Point(bufferX[0], bufferY[0]);
    }

    /**
     * Scrolls text view so that iter is on the screen in the position indicated
     * by xalign and yalign. An alignment of 0.0 indicates left or top, 1.0
     * indicates right or bottom, 0.5 means center. The effective screen for
     * purposes of this function is reduced by a margin of size within_margin.
     * NOTE: This function uses the currently-computed height of the lines in
     * the text buffer. Note that line heights are computed in an idle handler;
     * so this function may not have the desired effect if it's called before
     * the height computations. To avoid oddness, consider using
     * {@link #scrollToMark(TextMark, double, boolean, double, double)} which
     * saves a point to be scrolled to after line validation.
     * 
     * @param iter
     *            a TextIter
     * @param withinMargin
     *            Margin as a [0.0,0.5) fraction of screen size.
     * @param xAlign :
     *            horizontal alignment of mark within visible area.
     * @param yAlign :
     *            vertical alignment of mark within visible area
     * @return TRUE if scrolling occurred
     */
    public boolean scrollToIter(TextIter iter, double withinMargin,
            double xAlign, double yAlign) {
        return gtk_text_view_scroll_to_iter(getHandle(), iter.getHandle(),
                withinMargin, true, xAlign, yAlign);
    }

    /**
     * Scrolls text view so that iter is on the screen. The effective screen for
     * purposes of this function is reduced by a margin of size within_margin.
     * NOTE: This function uses the currently-computed height of the lines in
     * the text buffer. Note that line heights are computed in an idle handler;
     * so this function may not have the desired effect if it's called before
     * the height computations. To avoid oddness, consider using
     * gtk_text_view_scroll_to_mark() which saves a point to be scrolled to
     * after line validation.
     * 
     * @param iter
     *            a TextIter
     * @param withinMargin
     *            Margin as a [0.0,0.5) fraction of screen size.
     * @return TRUE if scrolling occurred
     */
    public boolean scrollToIter(TextIter iter, double withinMargin) {
        return gtk_text_view_scroll_to_iter(getHandle(), iter.getHandle(),
                withinMargin, false, 0, 0);
    }

    /**
     * Moves the cursor to the currently visible region of the buffer, it it
     * isn't there already.
     * 
     * @return TRUE if the cursor had to be moved.
     */
    public boolean moveCursorOnScreen() {
        return gtk_text_view_place_cursor_onscreen(getHandle());
    }

    /**
     * Moves a mark within the buffer so that it's located within the
     * currently-visible text area.
     * 
     * @return TRUE if the mark had to be moved.
     */
    public boolean moveMarkOnScreen(TextMark mark) {
        return gtk_text_view_move_mark_onscreen(getHandle(), mark.getHandle());
    }

    /**
     * Adds a child widget in the text buffer, at the given anchor.
     * 
     * @param child
     *            A Widget
     * @param anchor
     *            A TextChildAnchor in the TextBuffer for his view
     */
    public void addChild(Widget child, TextChildAnchor anchor) {
        gtk_text_view_add_child_at_anchor(getHandle(), child.getHandle(),
                anchor.getHandle());
    }

    /**
     * Gets the line wrapping for the view.
     * 
     * @return the line wrap setting for the view.
     */
    public WrapMode getWrapMode() {
        return WrapMode.intern(gtk_text_view_get_wrap_mode(getHandle()));
    }

    /**
     * Sets the line wrapping for the view.
     * 
     * @param wrapMode
     *            A WrapMode
     */
    public void setWrapMode(WrapMode wrapMode) {
        gtk_text_view_set_wrap_mode(getHandle(), wrapMode.getValue());
    }

    /**
     * Sets the default editability of the GtkTextView. You can override this
     * default setting with tags in the buffer, using the "editable" attribute
     * of tags.
     * 
     * @param setting
     *            Whether it's editable
     */
    public void setEditable(boolean setting) {
        gtk_text_view_set_editable(getHandle(), setting);
    }

    /**
     * Returns the default editability of the GtkTextView. Tags in the buffer
     * may override this setting for some ranges of text.
     * 
     * @return true if the widget is editable.
     */
    public boolean getEditable() {
        return gtk_text_view_get_editable(getHandle());
    }

    /**
     * Find out whether the cursor is being displayed.
     * 
     * @return Whether the insertion cursor is visible
     */
    public boolean getCursorVisible() {
        return gtk_text_view_get_cursor_visible(getHandle());
    }

    /**
     * Toggles whether the insertion point is displayed. A buffer with no
     * editable text probably shouldn't have a visible cursor, so you may want
     * to turn the cursor off.
     * 
     * @param setting
     *            Whether to show the insertion cursor
     */
    public void setCursorVisible(boolean setting) {
        gtk_text_view_set_cursor_visible(getHandle(), setting);
    }

    /**
     * Gets the default number of pixels to put above paragraphs.
     * 
     * @return the default number of pixels to put above paragraphs.
     */
    public int getPixelsAboveLines() {
        return gtk_text_view_get_pixels_above_lines(getHandle());
    }

    /**
     * Gets the default number of pixels of blank space to put below paragraphs.
     * 
     * @return the default number of pixels of blank space to put below
     *         paragraphs
     */
    public int getPixelsBelowLines() {
        return gtk_text_view_get_pixels_below_lines(getHandle());
    }

    /**
     * Gets the default number of pixels of blank space to leave between
     * display/wrapped lines within a paragraph.
     * 
     * @return the default number of pixels of blank space to leave between
     *         display/wrapped lines within a paragraph.
     */
    public int getPixelsInsideWrap() {
        return gtk_text_view_get_pixels_inside_wrap(getHandle());
    }

    /**
     * Sets the default number of blank pixels above paragraphs in text_view.
     * Tags in the buffer for text_view may override the defaults.
     * 
     * @param pixelsAboveLines
     *            Pixels above paragraphs
     */
    public void setPixelsAboveLines(int pixelsAboveLines) {
        gtk_text_view_set_pixels_above_lines(getHandle(), pixelsAboveLines);
    }

    /**
     * Sets the default number of pixels of blank space to put below paragraphs
     * in text_view. May be overridden by tags applied to text_view's buffer.
     * 
     * @param pixelsBelowLines
     *            Pixels below paragraphs
     * @deprecated Use {@link #setPixelsBelowLines(int)} instead.
     */
    public void setPixelsBelowLine(int pixelsBelowLines) {
        gtk_text_view_set_pixels_below_lines(getHandle(), pixelsBelowLines);
    }

    /**
     * Sets the default number of pixels of blank space to put below paragraphs
     * in text_view. May be overridden by tags applied to text_view's buffer.
     * 
     * @param pixelsBelowLines
     *            Pixels below paragraphs
     */
    public void setPixelsBelowLines(int pixelsBelowLines) {
        gtk_text_view_set_pixels_below_lines(getHandle(), pixelsBelowLines);
    }

    /**
     * Sets the default number of pixels of blank space to leave between
     * display/wrapped lines within a paragraph. May be overridden by tags in
     * text_view's buffer.
     * 
     * @param pixelsInsideWrap
     *            Default number of pixels between wrapped lines
     */
    public void setPixelsInsideWrap(int pixelsInsideWrap) {
        gtk_text_view_set_pixels_inside_wrap(getHandle(), pixelsInsideWrap);
    }

    /**
     * Gets the default justification of text in this TextView. Tags in the
     * buffer may override the default.
     * 
     * @return The default justification
     */
    public Justification getJustification() {
        return Justification.intern(gtk_text_view_get_justification(this
                .getHandle()));
    }

    /**
     * Sets the default justification of text in text_view. Tags in the view's
     * buffer may override the default.
     * 
     * @param justification
     *            The justification to use.
     */
    public void setJustification(Justification justification) {
        gtk_text_view_set_justification(getHandle(), justification.getValue());
    }

    /**
     * Gets the default left margin size of paragraphs in the TextView. Tags in
     * the buffer may override the default.
     * 
     * @return The left margin, in pixels
     */
    public int getLeftMargin() {
        return gtk_text_view_get_left_margin(getHandle());
    }

    /**
     * Gets the default right margin size of paragraphs in the TextView. Tags in
     * the buffer may override the default.
     * 
     * @return The right margin, in pixels
     */
    public int getRightMargin() {
        return gtk_text_view_get_right_margin(getHandle());
    }

    /**
     * Sets the default left margin for text in text_view. Tags in the buffer
     * may override the default.
     * 
     * @param leftMargin
     *            Size of left margin, in pixels
     */
    public void setLeftMargin(int leftMargin) {
        gtk_text_view_set_left_margin(getHandle(), leftMargin);
    }

    /**
     * Sets the default right margin for text in text_view. Tags in the buffer
     * may override the default.
     * 
     * @param rightMargin
     *            Size of right margin, in pixels
     */
    public void setRightMargin(int rightMargin) {
        gtk_text_view_set_right_margin(getHandle(), rightMargin);
    }

    /**
     * Gets the default indentation of paragraphs in text_view. Tags in the
     * view's buffer may override the default. The indentation may be negative.
     * 
     * @return The number of pixels of indentation
     */
    public int getIndent() {
        return gtk_text_view_get_indent(getHandle());
    }

    /**
     * Sets the default indentation for paragraphs in text_view. Tags in the
     * buffer may override the default.
     * 
     * @param indent
     *            Indentation in pixels
     */
    public void setIndent(int indent) {
        gtk_text_view_set_indent(getHandle(), indent);
    }

    /**
     * Gets the default tabs for text_view. Tags in the buffer may override the
     * defaults. The returned array will be NULL if "standard" (8-space) tabs
     * are used.
     */
    public TabArray getTabs() {
        return TabArray.getTabArray(gtk_text_view_get_tabs(getHandle()));
    }

    /**
     * Sets the default tab stops for paragraphs in text_view. Tags in the
     * buffer may override the default.
     * 
     * @param tabStops
     *            tabs as a PangoTabArray
     */
    public void setTabs(org.gnu.pango.TabArray tabStops) {
        gtk_text_view_set_tabs(getHandle(), tabStops.getHandle());
    }

    /**
     * Obtains a copy of the default text attributes. These are the attributes
     * used for text unless a tag overrides them.
     * 
     * @return The text attributes being used
     */
    public TextAttributes getDefaultAttributes() {
        Handle handle = gtk_text_view_get_default_attributes(getHandle());
        return TextAttributes.getTextAttributes(handle);
    }

    /**
     * Sets the behavior of the text widget when the Tab key is pressed. If
     * TRUE, a tab character is inserted. If is FALSE, the keyboard focus is
     * moved to the next widget in the focus chain.
     * 
     * @param acceptsTab
     *            TRUE if pressing the Tab key should insert a tab character,
     *            FALSE, if pressing the Tab key should move the keyboard focus
     * 
     * @since 2.4
     */
    public void setAcceptsTab(boolean acceptsTab) {
        gtk_text_view_set_accepts_tab(getHandle(), acceptsTab);
    }

    /**
     * Returns whether pressing the Tab key inserts a tab characters.
     * 
     * @see #setAcceptsTab(boolean)
     * 
     * @since 2.4
     */
    public boolean getAcceptsTab() {
        return gtk_text_view_get_accepts_tab(getHandle());
    }

    /**
     * Changes the overwrite mode (whether text is overwritten)
     * 
     * @param overwrite
     *            TRUE to turn on overwrite mode, FALSE to turn it off
     * 
     * @since 2.4
     */
    public void setOverwrite(boolean overwrite) {
        gtk_text_view_set_overwrite(getHandle(), overwrite);
    }

    /**
     * Returns whether the TextView is in overwrite mode or not.
     * 
     * @see #setOverwrite(boolean)
     * @since 2.4
     */
    public boolean getOverwrite() {
        return gtk_text_view_get_overwrite(getHandle());
    }

    public Adjustment getHAdjustment() {
        return Adjustment.getAdjustment(getHAdjustment(getHandle()));
    }

    public Adjustment getVAdjustment() {
        return Adjustment.getAdjustment(getVAdjustment(getHandle()));
    }

    public Class getEventListenerClass(String signal) {
        Class cls = evtMap.getEventListenerClass(signal);
        if (cls == null)
            cls = super.getEventListenerClass(signal);
        return cls;
    }

    public EventType getEventType(String signal) {
        EventType et = evtMap.getEventType(signal);
        if (et == null)
            et = super.getEventType(signal);
        return et;
    }

    /**
     * Gets the y coordinate of the top of the line containing <code>iter</code>,
     * and the height of the line. The coordinate is a buffer coordinate;
     * convert to window coordinates with
     * {@link #windowToBufferCoords(TextWindowType, int, int)}.
     * 
     * @param iter
     *            A valid TextIter
     * @return The y-coordinate and Height of the line containing iter
     */
    public VerticalLineRange getLineYRange(TextIter iter) {
        int[] y = new int[1];
        int[] height = new int[1];

        gtk_text_view_get_line_yrange(getHandle(), iter.getHandle(), y, height);

        return new VerticalLineRange(y[0], height[0]);
    }

    /**
     * Converts coordinate (xCoord, yCoord) to coordinates for the window
     * <code>winType</code>. Note that you can't convert coordinates for a
     * non-existing window (see
     * {@link TextView#setBorderWindowSize(TextWindowType, int)}.
     * 
     * @param winType
     *            a {@link TextWindowType}
     * @param xCoord
     *            buffer X coordinate
     * @param yCoord
     *            buffer Y coordinate
     * @return the corresponding window coordinates
     */
    public Point bufferToWindowCoords(TextWindowType winType, int xCoord,
            int yCoord) {
        int[] windowX = new int[1];
        int[] windowY = new int[1];

        gtk_text_view_buffer_to_window_coords(getHandle(), winType.getValue(),
                xCoord, yCoord, windowX, windowY);

        return new Point(windowX[0], windowY[0]);
    }

    /**
     * Sets the width of the {@link TextWindowType#LEFT} or
     * {@link TextWindowType#RIGHT} windows, or the height of
     * {@link TextWindowType#TOP} or {@link TextWindowType#BOTTOM} Windows.
     * Automatically destroys the corresponding window if the size is set to 0,
     * and creates the window if the size is set to non-zero. This function can
     * only be used for the "border windows," it doesn't work with
     * {@link TextWindowType#WIDGET}, {@link TextWindowType#TEXT}, or
     * {@link TextWindowType#PRIVATE}.
     * 
     * @param type
     *            The window to affect
     * @param size
     *            Width or height of the window
     */
    public void setBorderWindowSize(TextWindowType type, int size) {
        gtk_text_view_set_border_window_size(getHandle(), type.getValue(), size);
    }

    /**
     * Gets the width of the specified border window.
     * 
     * @param type
     *            The window to return size from
     * @return The size of the window
     * 
     * @see TextView#setBorderWindowSize(TextWindowType, int).
     */
    public int getBorderWindowSize(TextWindowType type) {
        return gtk_text_view_get_border_window_size(getHandle(), type
                .getValue());
    }

    /**
     * Retrieves the {@link org.gnu.gdk.Window} corresponding to an area of the
     * text view; possible windows include the overall widget window, child
     * windows on the left, right, top, bottom, and the window that displays the
     * text buffer. Windows are null and nonexistent if their width or height is
     * 0, and are nonexistent before the widget has been realized.
     * 
     * @param type
     *            The window to get
     * @return The window, or null
     */
    public Window getWindow(TextWindowType type) {
        Handle hndl = gtk_text_view_get_window(getHandle(), type.getValue());
        return Window.getWindowFromHandle(hndl);
    }

    /**
     * Usually used to find out which window an event corresponds to.
     * 
     * @param win
     *            A window
     * @return The type of the window
     */
    public TextWindowType getWindowType(Window win) {
        return TextWindowType.intern(gtk_text_view_get_window_type(getHandle(),
                win.getHandle()));
    }

    /**
     * Scrolls this the minimum distance such that mark is contained within the
     * visible area of the widget.
     * 
     * @param mark
     *            a mark in the buffer of the TextView.
     */
    public void scrollMarkOnScreen(TextMark mark) {
        gtk_text_view_scroll_mark_onscreen(this.getHandle(), mark.getHandle());
    }

    /**
     * Returns a Rectangle with the currently-visible region of the buffer, in
     * buffer coordinates. Convert to window coordinates with
     * {@link #bufferToWindowCoords(TextWindowType, int, int)}.
     */
    public Rectangle getVisibleRectangle() {
        return Rectangle.getRectangle(gtk_text_view_get_visible_rect(this
                .getHandle()));
    }

    private static EventMap evtMap = new EventMap();
    static {
        addEvents(evtMap);
    }

    /**
     * Implementation method to build an EventMap for this widget class. Not
     * useful (or supported) for application use.
     */
    private static void addEvents(EventMap anEvtMap) {
        anEvtMap.addEvent("copy_clipboard", "handleCopyClipboard",
                TextViewEvent.Type.COPY_CLIPBOARD, TextViewListener.class);
        anEvtMap.addEvent("cut_clipboard", "handleCutClipboard",
                TextViewEvent.Type.CUT_CLIPBOARD, TextViewListener.class);
        anEvtMap.addEvent("paste_clipboard", "handlePasteClipboard",
                TextViewEvent.Type.PASTE_CLIPBOARD, TextViewListener.class);
        anEvtMap.addEvent("delete_from_cursor", "handleDeleteFromCursor",
                TextViewEvent.Type.DELETE_FROM_CURSOR, TextViewListener.class);
        anEvtMap.addEvent("insert_at_cursor", "handleInsertAtCursor",
                TextViewEvent.Type.INSERT_AT_CURSOR, TextViewListener.class);
        anEvtMap.addEvent("move_cursor", "handleMoveCursor",
                TextViewEvent.Type.MOVE_CURSOR, TextViewListener.class);
        anEvtMap.addEvent("move_focus", "handleMoveFocus",
                TextViewEvent.Type.MOVE_FOCUS, TextViewListener.class);
        anEvtMap.addEvent("page_horizontally", "handlePageHorizontally",
                TextViewEvent.Type.PAGE_HORIZONTALLY, TextViewListener.class);
        anEvtMap.addEvent("populate_popup", "handlePopulatePopup",
                TextViewEvent.Type.POPULATE_POPUP, TextViewListener.class);
        anEvtMap.addEvent("set_anchor", "handleSetAnchor",
                TextViewEvent.Type.SET_ANCHOR, TextViewListener.class);
        anEvtMap.addEvent("set_scroll_adjustments",
                "handleSetScrollAdjustments",
                TextViewEvent.Type.SET_SCROLL_ADJUSTMENTS,
                TextViewListener.class);
        anEvtMap.addEvent("toggle_overwrite", "handleToggleOverwrite",
                TextViewEvent.Type.TOGGLE_OVERWRITE, TextViewListener.class);
    }

    /***************************************************************************
     * TextView event handling.
     **************************************************************************/
    /**
     * Register an object to receive text view event notification.
     * 
     * @param listener
     *            The object that has implemented the TextViewListener interface
     *            that is to receive the text view events.
     */
    public void addListener(TextViewListener listener) {
        // Don't add the listener a second time if it is in the Vector.
        int i = findListener(listeners, listener);
        if (i == -1) {
            if (null == listeners) {
                evtMap.initialize(this, TextViewEvent.Type.COPY_CLIPBOARD);
                evtMap.initialize(this, TextViewEvent.Type.CUT_CLIPBOARD);
                evtMap.initialize(this, TextViewEvent.Type.PASTE_CLIPBOARD);
                evtMap.initialize(this, TextViewEvent.Type.PASTE_CLIPBOARD);
                evtMap.initialize(this, TextViewEvent.Type.INSERT_AT_CURSOR);
                evtMap.initialize(this, TextViewEvent.Type.MOVE_CURSOR);
                evtMap.initialize(this, TextViewEvent.Type.MOVE_FOCUS);
                evtMap.initialize(this, TextViewEvent.Type.PAGE_HORIZONTALLY);
                evtMap.initialize(this, TextViewEvent.Type.POPULATE_POPUP);
                evtMap.initialize(this, TextViewEvent.Type.POPULATE_POPUP);
                evtMap.initialize(this,
                        TextViewEvent.Type.SET_SCROLL_ADJUSTMENTS);
                evtMap.initialize(this, TextViewEvent.Type.TOGGLE_OVERWRITE);
                listeners = new Vector();
            }
            listeners.addElement(listener);
        }
    }

    /**
     * Unregister an object that was receiving text view event notification.
     * 
     * @param listener
     *            The object that is to no longer receive text view events.
     */
    public void removeListener(TextViewListener listener) {
        int i = findListener(listeners, listener);
        if (i > -1) {
            listeners.remove(i);
        }
        if (0 == listeners.size()) {
            evtMap.uninitialize(this, TextViewEvent.Type.COPY_CLIPBOARD);
            evtMap.uninitialize(this, TextViewEvent.Type.CUT_CLIPBOARD);
            evtMap.uninitialize(this, TextViewEvent.Type.PASTE_CLIPBOARD);
            evtMap.uninitialize(this, TextViewEvent.Type.PASTE_CLIPBOARD);
            evtMap.uninitialize(this, TextViewEvent.Type.INSERT_AT_CURSOR);
            evtMap.uninitialize(this, TextViewEvent.Type.MOVE_CURSOR);
            evtMap.uninitialize(this, TextViewEvent.Type.MOVE_FOCUS);
            evtMap.uninitialize(this, TextViewEvent.Type.PAGE_HORIZONTALLY);
            evtMap.uninitialize(this, TextViewEvent.Type.POPULATE_POPUP);
            evtMap.uninitialize(this, TextViewEvent.Type.POPULATE_POPUP);
            evtMap
                    .uninitialize(this,
                            TextViewEvent.Type.SET_SCROLL_ADJUSTMENTS);
            evtMap.uninitialize(this, TextViewEvent.Type.TOGGLE_OVERWRITE);
            listeners = null;
        }
    }

    protected void fireTextViewEvent(TextViewEvent event) {
        if (null == listeners) {
            return;
        }
        int size = listeners.size();
        int i = 0;
        while (i < size) {
            TextViewListener tvc = (TextViewListener) listeners.elementAt(i);
            tvc.textViewEvent(event);
            i++;
        }
    }

    private void handleCopyClipboard() {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.COPY_CLIPBOARD));
    }

    private void handleCutClipboard() {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.CUT_CLIPBOARD));
    }

    private void handlePasteClipboard() {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.PASTE_CLIPBOARD));
    }

    private void handleInsertAtCursor(String str) {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.INSERT_AT_CURSOR));
    }

    private void handleDeleteFromCursor(int deleteType, int count) {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.DELETE_FROM_CURSOR));
    }

    private void handleMoveCursor(int step, int count, boolean extendedSelection) {
        TextViewEvent tve = new TextViewEvent(this,
                TextViewEvent.Type.MOVE_CURSOR);
        tve.setMovementStep(MovementStep.intern(step));
        tve.setCount(count);
        fireTextViewEvent(tve);
    }

    private void handleMoveFocus(int direction) {
        fireTextViewEvent(new TextViewEvent(this, TextViewEvent.Type.MOVE_FOCUS));
    }

    private void handlePageHorizontally(int count, boolean extendedSelection) {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.PAGE_HORIZONTALLY));
    }

    private void handlePopulatePopup(Handle menu) {
        TextViewEvent tve = new TextViewEvent(this,
                TextViewEvent.Type.POPULATE_POPUP);
        tve.setMenu(Menu.getMenu(menu));
        fireTextViewEvent(tve);
    }

    private void handleSetScrollAdjustments(Handle hajd, Handle vadj) {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.SET_SCROLL_ADJUSTMENTS));
    }

    private void handleSetAnchor() {
        fireTextViewEvent(new TextViewEvent(this, TextViewEvent.Type.SET_ANCHOR));
    }

    private void handleToggleOverwrite() {
        fireTextViewEvent(new TextViewEvent(this,
                TextViewEvent.Type.TOGGLE_OVERWRITE));
    }

    /**
     * Retrieve the runtime type used by the GLib library.
     */
    public static Type getType() {
        return new Type(gtk_text_view_get_type());
    }

    native static final protected Handle getHAdjustment(Handle textView);

    native static final protected Handle getVAdjustment(Handle textView);

    native static final protected int gtk_text_view_get_type();

    native static final protected Handle gtk_text_view_new();

    native static final protected Handle gtk_text_view_new_with_buffer(
            Handle buffer);

    native static final protected void gtk_text_view_set_buffer(
            Handle textView, Handle buffer);

    native static final protected Handle gtk_text_view_get_buffer(
            Handle textView);

    native static final protected boolean gtk_text_view_scroll_to_iter(
            Handle textView, Handle iter, double withinMargin,
            boolean useAlign, double xalign, double yalign);

    native static final protected void gtk_text_view_scroll_to_mark(
            Handle textView, Handle mark, double withinMargin,
            boolean useAlign, double xalign, double yalign);

    native static final protected void gtk_text_view_scroll_mark_onscreen(
            Handle textView, Handle mark);

    native static final protected boolean gtk_text_view_move_mark_onscreen(
            Handle textView, Handle mark);

    native static final protected boolean gtk_text_view_place_cursor_onscreen(
            Handle textView);

    native static final protected Handle gtk_text_view_get_visible_rect(
            Handle textView);

    native static final protected void gtk_text_view_set_cursor_visible(
            Handle textView, boolean setting);

    native static final protected boolean gtk_text_view_get_cursor_visible(
            Handle textView);

    native static final protected Handle gtk_text_view_get_iter_location(
            Handle textView, Handle iter);

    native static final protected Handle gtk_text_view_get_iter_at_location(
            Handle textView, int x, int y);

    native static final protected void gtk_text_view_get_line_yrange(
            Handle textView, Handle iter, int[] y, int[] height);

    native static final protected void gtk_text_view_get_line_at_y(
            Handle textView, Handle targetIter, int y, int[] lneTop);

    native static final protected void gtk_text_view_buffer_to_window_coords(
            Handle textView, int win, int bufferX, int bufferY, int[] windowX,
            int[] windowY);

    native static final protected void gtk_text_view_window_to_buffer_coords(
            Handle textView, int win, int windowX, int windowY, int[] bufferX,
            int[] bufferY);

    native static final protected Handle gtk_text_view_get_window(
            Handle textView, int win);

    native static final protected int gtk_text_view_get_window_type(
            Handle textView, Handle window);

    native static final protected void gtk_text_view_set_border_window_size(
            Handle textView, int type, int size);

    native static final protected int gtk_text_view_get_border_window_size(
            Handle textView, int type);

    native static final protected boolean gtk_text_view_forward_display_line(
            Handle textView, Handle iter);

    native static final protected boolean gtk_text_view_backward_display_line(
            Handle textView, Handle iter);

    native static final protected boolean gtk_text_view_forward_display_line_end(
            Handle textView, Handle iter);

    native static final protected boolean gtk_text_view_backward_display_line_start(
            Handle textView, Handle iter);

    native static final protected boolean gtk_text_view_starts_display_line(
            Handle textView, Handle iter);

    native static final protected boolean gtk_text_view_move_visually(
            Handle textView, Handle iter, int count);

    native static final protected void gtk_text_view_add_child_at_anchor(
            Handle textView, Handle child, Handle anchor);

    native static final protected void gtk_text_view_add_child_in_window(
            Handle textView, Handle child, int whichWindow, int xpos, int ypos);

    native static final protected void gtk_text_view_move_child(
            Handle textView, Handle child, int xpos, int ypos);

    native static final protected void gtk_text_view_set_wrap_mode(
            Handle textView, int wrapMode);

    native static final protected int gtk_text_view_get_wrap_mode(
            Handle textView);

    native static final protected void gtk_text_view_set_editable(
            Handle textView, boolean setting);

    native static final protected boolean gtk_text_view_get_editable(
            Handle textView);

    native static final protected void gtk_text_view_set_pixels_above_lines(
            Handle textView, int pixelsAboveLines);

    native static final protected int gtk_text_view_get_pixels_above_lines(
            Handle textView);

    native static final protected void gtk_text_view_set_pixels_below_lines(
            Handle textView, int pixelsBelowLines);

    native static final protected int gtk_text_view_get_pixels_below_lines(
            Handle textView);

    native static final protected void gtk_text_view_set_pixels_inside_wrap(
            Handle textView, int pixelsInsideWrap);

    native static final protected int gtk_text_view_get_pixels_inside_wrap(
            Handle textView);

    native static final protected void gtk_text_view_set_justification(
            Handle textView, int justification);

    native static final protected int gtk_text_view_get_justification(
            Handle textView);

    native static final protected void gtk_text_view_set_left_margin(
            Handle textView, int leftMargin);

    native static final protected int gtk_text_view_get_left_margin(
            Handle textView);

    native static final protected void gtk_text_view_set_right_margin(
            Handle textView, int rightMargin);

    native static final protected int gtk_text_view_get_right_margin(
            Handle textView);

    native static final protected void gtk_text_view_set_indent(
            Handle textView, int indent);

    native static final protected int gtk_text_view_get_indent(Handle textView);

    native static final protected void gtk_text_view_set_tabs(Handle textView,
            Handle tabs);

    native static final protected Handle gtk_text_view_get_tabs(Handle textView);

    native static final protected Handle gtk_text_view_get_default_attributes(
            Handle textView);

    protected native static final void gtk_text_view_set_accepts_tab(
            Handle view, boolean setting);

    protected native static final boolean gtk_text_view_get_accepts_tab(
            Handle textview);

    protected native static final void gtk_text_view_set_overwrite(Handle view,
            boolean setting);

    protected native static final boolean gtk_text_view_get_overwrite(
            Handle view);
}
