/*
 * 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.glib.EventMap;
import org.gnu.glib.GObject;
import org.gnu.glib.Type;
import org.gnu.gtk.event.TextBufferEvent;
import org.gnu.gtk.event.TextBufferListener;
import org.gnu.gtk.event.GtkEventType;

/**
 * The TextBuffer is the object used to store data to be displayed in a {@link
 * TextView} widget. See the {@link TextView} widget description for details.
 */
public class TextBuffer extends GObject {

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

	/**
	 * Create butter from native handle 
	 * */
	protected TextBuffer(int handle) {
		super(handle);
		initializeEventHandlers();
	}
	/**
	 * Creates a new text buffer.
	 * @param table A tag table
	 */
	public TextBuffer(TextTagTable table) {
		super(gtk_text_buffer_new(table.getHandle()));
		initializeEventHandlers();
	}

	/**
	 * Creates a new buffer and a new default TextTagTable.
	 */
	public TextBuffer() {
		super(gtk_text_buffer_new_noTable());
		initializeEventHandlers();
	}

	/**
	 * Obtains the number of lines in the buffer. This value is cached, so the
	 * function is very fast.
	 * @return The number of lines in the bufer.
	 */
	public int getLineCount() {
		return gtk_text_buffer_get_line_count(getHandle());
	}

	/**
	 * Gets the number of characters in the buffer; note that characters and
	 * bytes are not the same, you can't e.g. expect the contents of the buffer
	 * in string form to be this many bytes long. The character count is cached,
	 * so this function is very fast.
	 * @return Number of characters in the buffer.
	 */
	public int getCharCount() {
		return gtk_text_buffer_get_char_count(getHandle());
	}

	/**
	 * Get the GtkTextTagTable associated with this buffer.
	 * @return Associated TextTagTable.
	 */
	public TextTagTable getTextTagTable() {
		int hndl = gtk_text_buffer_get_tag_table(getHandle());
		GObject obj = retrieveGObject(hndl);
		if (null != obj)
			return (TextTagTable)obj;
		return new TextTagTable(hndl);
	}

	/**
	 * Inserts text at position iter. Emits the "insert_text" signal; insertion
	 * actually occurs in the default handler for the signal. iter is
	 * invalidated when insertion occurs (because the buffer contents change),
	 * but the default signal handler revalidates it to point to the end of the
	 * inserted text.
	 * @param iter A position in the buffer
	 * @param text The text to insert
	 */
	public void insertText(TextIter iter, String text) {
		gtk_text_buffer_insert(getHandle(), iter.getHandle(), text);
	}

	/**
	 * Inserts text at the current cursor position
	 * @param text The text to insert
	 */
	public void insertText(String text) {
		gtk_text_buffer_insert_at_cursor(getHandle(), text);
	}

	/**
	 * Like {@link #insertText(TextIter, String)}, but the insertion will not
	 * occur if iter is at a non-editable location in the buffer. Usually you
	 * want to prevent insertions at ineditable locations if the insertion
	 * results from a user action (is interactive).
	 *
	 * <p>defaultEditable indicates the editability of text that doesn't have a
	 * tag affecting editability applied to it. Typically the result of
	 * {@link TextView#getEditable()} is appropriate here.
	 *
	 * @param iter A position in buffer
	 * @param text The text to insert
	 * @param defaultEditable Default editability of buffer
	 * @return Whether text was actually inserted
	 */
	public boolean insertTextInteractive(TextIter iter, String text, boolean defaultEditable) {
		return gtk_text_buffer_insert_interactive(getHandle(), iter.getHandle(), text, defaultEditable);
	}

	/**
	 * Calls {@link #insertTextInteractive(TextIter, String, boolean)} at the cursor position.
	 *
	 * <p>defaultEditable indicates the editability of text that doesn't have a
	 * tag affecting editability applied to it. Typically the result of
	 * {@link TextView#getEditable()} is appropriate here.
	 *
	 * @param text The text to insert.
	 * @param defaultEditable Default editability of buffer
	 * @return Whether text was actually inserted.
	 */
	public boolean insertTextInteractive(String text, boolean defaultEditable) {
		return gtk_text_buffer_insert_interactive_at_cursor(getHandle(), text, defaultEditable);
	}

	/**
	 * Copies text, tags, and pixbufs between start and end (the order of start
	 * and end doesn't matter) and inserts the copy at iter. Used instead of
	 * simply getting/inserting text because it preserves images and tags. If
	 * start and end are in a different buffer from buffer, the two buffers must
	 * share the same tag table.
	 * <p>Implemented via emissions of the insert_text and apply_tag signals, so expect
	 * those.
	 *
	 * @param iter A position in the buffer
	 * @param start A position in a TextBuffer
	 * @param end Another position in the same buffer as start
	 */
	public void insertRange(TextIter iter, TextIter start, TextIter end) {
		gtk_text_buffer_insert_range(getHandle(), iter.getHandle(), start.getHandle(), end.getHandle());
	}

	/** 
	 * Inserts text and applies a tag to that text.
	 *
	 * @param iter Place to insert text
	 * @param text Text to insert
	 * @param tag name of TextTag to apply to that text.
	 */
	public void insertText( TextIter iter, String text, String tag ){
		insert_with_tag( getHandle(), iter.getHandle(), text, tag );
	}

	/** 
	 * Inserts text and applies  tags to that text.
	 *
	 * @param iter Place to insert text
	 * @param text Text to insert
	 * @param tags Array of TextTag names to apply to that text.
	 */
	public void insertText( TextIter iter, String text, String[] tags ){
		TextMark mark = createMark( "jginternal", getEndIter(), true);
		insertText(iter, text );
		TextIter start = getIter( mark );
		deleteMark( "jginternal" );
		for( int i = 0; i < tags.length; i++ ){
			applyTag( tags[i], start, iter );
		}
	}

	/**
	 * Same as {@link #insertRange(TextIter, TextIter, TextIter)}, but does
	 * nothing if the insertion point isn't editable. The defaultEditable
	 * parameter indicates whether the text is editable at iter if no tags
	 * enclosing iter affect editability. Typically the result of {@link
	 * TextView#getEditable()} is appropriate here.
	 * @param iter A position in buffer
	 * @param start A position in a TextBuffer
	 * @param end Another position in the same buffer as start
	 * @param defaultEditable default editability of the buffer
	 * @return Whether an insertion was possible at iter.
	 */
	public boolean insertRangeInteractive(TextIter iter, TextIter start, TextIter end, boolean defaultEditable) {
		return gtk_text_buffer_insert_range_interactive(
			getHandle(),
			iter.getHandle(),
			start.getHandle(),
			end.getHandle(),
			defaultEditable);
	}

	/**
	 * Deletes text between <code>start</code> and <code>end</code>. The order
	 * of start and end is not actually relevant. This function actually emits
	 * the "delete_range" signal, and the default handler of that signal deletes
	 * the text. Because the buffer is modified, all outstanding iterators
	 * become invalid after calling this function; however, the start and end
	 * will be re-initialized to point to the location where text was deleted.
	 * @param start A position in buffer
	 * @param end Another position in buffer.
	 */
	public void deleteText(TextIter start, TextIter end) {
		gtk_text_buffer_delete(getHandle(), start.getHandle(), end.getHandle());
	}

	/**
	 * Deletes all <em>editable</em> text in the given range. Calls {@link
	 * #deleteText(TextIter, TextIter)} for each editable sub-range of
	 * start,end. <code>start</code> and <code>end</code> are revalidated to
	 * point to the location of the last deleted range, or left untouched if no
	 * text was deleted.
	 * @param start Start of range to delete
	 * @param end End of range
	 * @param defaultEditable Ehether the buffer is editable by default
	 * @return Whether some text was actually deleted
	 */
	public boolean deleteTextInteractive(TextIter start, TextIter end, boolean defaultEditable) {
		return gtk_text_buffer_delete_interactive(getHandle(), start.getHandle(), end.getHandle(), defaultEditable);
	}

	/**
	 * Deletes current contents of buffer, and inserts <code>text</code>
	 * instead.
	 * @param text The text to use as the content of the buffer.
	 */
	public void setText(String text) {
		gtk_text_buffer_set_text(getHandle(), text);
	}

	/**
	 * Returns the text in the range start,end. Excludes undisplayed text (text
	 * marked with tags that set the invisibility attribute) if
	 * includeHiddenChars is FALSE. Does not include characters representing
	 * embedded images, so byte and character indexes into the returned string
	 * do not correspond to byte and character indexes into the buffer. Contrast
	 * with {@link #getSlice(TextIter, TextIter, boolean)}.
	 * @param start Start of a range
	 * @param end End of a range
	 * @param includeHiddenChars Whether to include invisible text
	 * @return The text of the buffer
	 */
	public String getText(TextIter start, TextIter end, boolean includeHiddenChars) {
		return gtk_text_buffer_get_text(getHandle(), start.getHandle(), end.getHandle(), includeHiddenChars);
	}

	/**
	 * Returns the text in the range start,end. Excludes undisplayed text (text
	 * marked with tags that set the invisibility attribute) if
	 * includeHiddenChars is FALSE. The returned string includes a 0xFFFC
	 * character whenever the buffer contains embedded images, so byte and
	 * character indexes into the returned string do correspond to byte and
	 * character indexes into the buffer. Contrast with
	 * {@link #getText(TextIter, TextIter, boolean)}. Note that 0xFFFC can occur in normal text as
	 * well, so it is not a reliable indicator that a pixbuf or widget is in the
	 * buffer.
	 * @param  start Start of a range
	 * @param end End of a range.
	 * @param includeHiddenChars Whether to include invisible text
	 * @return The string
	 */
	public String getSlice(TextIter start, TextIter end, boolean includeHiddenChars) {
		return gtk_text_buffer_get_slice(getHandle(), start.getHandle(), end.getHandle(), includeHiddenChars);
	}

	/**
	 * Inserts an image into the text buffer at iter. The image will be counted
	 * as one character in character counts, and when obtaining the buffer
	 * contents as a string, will be represented by the Unicode "object
	 * replacement character" 0xFFFC. Note that the "slice" variants for
	 * obtaining portions of the buffer as a string include this character for
	 * pixbufs, but the "text" variants do not.
	 * @param iter Location to insert the pixbuf
	 * @param pixbuf A Pixbuf
	 */
	public void insertPixbuf(TextIter iter, org.gnu.gdk.Pixbuf pixbuf) {
		gtk_text_buffer_insert_pixbuf(getHandle(), iter.getHandle(), pixbuf.getHandle());
	}

	/**
	 * Inserts a child widget anchor into the text buffer at iter. The anchor
	 * will be counted as one character in character counts, and when obtaining
	 * the buffer contents as a string, will be represented by the Unicode
	 * "object replacement character" 0xFFFC. Note that the "slice" variants for
	 * obtaining portions of the buffer as a string include this character for
	 * child anchors, but the "text" variants do not.
	 * @param iter Location to insert the anchor
	 * @param anchor A TextChildAnchor
	 */
	public void inserChildAnchor(TextIter iter, TextChildAnchor anchor) {
		gtk_text_buffer_insert_child_anchor(getHandle(), iter.getHandle(), anchor.getHandle());
	}

	/**
	 * This is a convenience function which simply creates a child anchor with
	 * {link TextChildAnchor#TextChildAnchor()} and inserts it into the buffer
	 * @param iter Location in the buffer
	 * @return The created child anchor
	 */
	public TextChildAnchor createChildAnchor(TextIter iter) {
		return new TextChildAnchor(gtk_text_buffer_create_child_anchor(getHandle(), iter.getHandle()));
	}

	/**
	 * Creates a mark at position <code>where</code>. Otherwise, the mark can be
	 * retrieved by name using {@link #getMark(String)}. If a mark has left
	 * gravity, and text is inserted at the mark's current location, the mark
	 * will be moved to the left of the newly-inserted text. If the mark has
	 * right gravity (leftGravity = FALSE), the mark will end up on the right of
	 * newly-inserted text. The standard left-to-right cursor is a mark with
	 * right gravity (when you type, the cursor stays on the right side of the
	 * text you're typing).
	 *
	 * <p>Emits the "mark_set" event as notification of the mark's initial placement.
	 *
	 * @param markName Name for mark
	 * @param where Location to place mark
	 * @param leftGravity Whether the mark has left gravity
	 * @return The new TextMark object
	 */
	public TextMark createMark(String markName, TextIter where, boolean leftGravity) {
		return new TextMark(gtk_text_buffer_create_mark(getHandle(), markName, where.getHandle(), leftGravity));
	}

	/**
	 * Moves <code>mark</code> to the new location <code>where</code>. Emits the
	 * "mark_set" event  as notification of the move.
	 * @param mark A TextMark
	 * @param where New location for mark in buffer
	 */
	public void moveMark(TextMark mark, TextIter where) {
		gtk_text_buffer_move_mark(getHandle(), mark.getHandle(), where.getHandle());
	}

	/**
	 * Moves the mark named <code>name</code> (which must exist) to location
	 * <code>where</code>
	 * @param name Name of a mark
	 * @param where New location for mark
	 */
	public void moveMark(String name, TextIter where) {
		gtk_text_buffer_move_mark_by_name(getHandle(), name, where.getHandle());
	}

	/**
	 * Deletes <code>mark</code>, so that it's no longer located anywhere in the
	 * buffer. There is no way to undelete a mark. 
	* {@link TextMark#getDeleted()} indicates that a mark no longer belongs to a buffer. The
	 * "mark_deleted" signal will be emitted as notification after the mark is
	 * deleted.
	 * @param mark A TextMark in buffer
	 */
	public void deleteMark(TextMark mark) {
		gtk_text_buffer_delete_mark(getHandle(), mark.getHandle());
	}

	/**
	 * Deletes the mark named <code>name</code>; the mark must exist. 
	 * @see #deleteMark(TextMark)
	 * @param name The name of the mark to delete
	 */
	public void deleteMark(String name) {
		gtk_text_buffer_delete_mark_by_name(getHandle(), name);
	}

	/**
	 * Returns true if a mark exists with the given name.
	 */
	public boolean markExists(String name) {
		return (gtk_text_buffer_get_mark(getHandle(), name) != 0);
	}

	/**
	 * Returns the mark named <code>name</code> in the buffer, or <code>null</code> if no such mark exists in the buffer
	 */
	public TextMark getMark(String name) {
		int markHandle = gtk_text_buffer_get_mark(getHandle(), name);
		if (0 == markHandle)
			return null;
		GObject obj = retrieveGObject(markHandle);
		if (null != obj)
			return (TextMark)obj;
		return new TextMark(markHandle);
	}

	/**
	 * Returns the mark that represents the cursor (insertion point). Equivalent
	 * to calling gtk_text_buffer_get_mark() to get the mark named "insert", but
	 * very slightly more efficient, and involves less typing.
	 * @return Insertion point mark
	 */
	public TextMark getInsert() {
		int hndl = gtk_text_buffer_get_insert(getHandle());
		GObject obj = retrieveGObject(hndl);
		if (null != obj)
			return (TextMark)obj;
		return new TextMark(hndl);
	}

	/**
	 * Returns the mark that represents the selection bound. Equivalent to
	 * calling {@link #getMark(String)} to get the mark named "selection_bound",
	 * but very slightly more efficient, and involves less typing.
	 *
	 * <p>The currently-selected text in buffer is the region between the
	 * "selection_bound" and "insert" marks. If "selection_bound" and "insert"
	 * are in the same place, then there is no current selection.
	 * @return Mark for the selection
	 */
	public TextMark getSelectionBound() {
		int hndl = gtk_text_buffer_get_selection_bound(getHandle());
		GObject obj = retrieveGObject(hndl);
		if (null != obj)
			return (TextMark)obj;
		return new TextMark(hndl);
	}

	/**
	 * This function moves the "insert" and "selection_bound" marks
	 * simultaneously. If you move them to the same place in two steps with
	 * {@link #moveMark(TextMark, TextIter)}, you will temporarily select a region in between
	 * their old and new locations, which can be pretty inefficient since the
	 * temporarily-selected region will force stuff to be recalculated. This
	 * function moves them as a unit, which can be optimized.
	 * @param where Where to put the cursor
	 */
	public void placeCursor(TextIter where) {
		gtk_text_buffer_place_cursor(getHandle(), where.getHandle());
	}

	/**
	 * Emits the "apply_tag" signal on buffer. The default handler for the
	 * signal applies tag to the given range. start and end do not have to be in
	 * order.
	 * @param tag A TextTag
	 * @param start One bound of range to be tagged
	 * @param end Other bound of range to be tagged
	 */
	public void applyTag(TextTag tag, TextIter start, TextIter end) {
		gtk_text_buffer_apply_tag(getHandle(), tag.getHandle(), start.getHandle(), end.getHandle());
	}

	/**
	 * Emits the "remove_tag" signal. The default handler for the signal removes
	 * all occurrences of tag from the given range. start and end don't have to
	 * be in order
	 * @param tag A TextTag
	 * @param start One bound of range to be untagged
	 * @param end Other bound of range to be untagged
	 */
	public void removeTag(TextTag tag, TextIter start, TextIter end) {
		gtk_text_buffer_remove_tag(getHandle(), tag.getHandle(), start.getHandle(), end.getHandle());
	}

	/**
	 * Looks up a tag by name and then applies it.
	 * @param name Name of a named TextTag
	 * @param start One bound of range to be tagged
	 * @param end Other bound of range to be tagged
	 */
	public void applyTag(String name, TextIter start, TextIter end) {
		gtk_text_buffer_apply_tag_by_name(getHandle(), name, start.getHandle(), end.getHandle());
	}

	/**
	 * Removes a tag based on it's name.
	 * @param name Name of a TextTag
	 * @param start One bound of range to be untagged
	 * @param end Other bound of range to be untagged
	 */
	public void removeTag(String name, TextIter start, TextIter end) {
		gtk_text_buffer_remove_tag_by_name(getHandle(), name, start.getHandle(), end.getHandle());
	}

	/**
	 * Removes all tags in the range between start and end. Be careful with this
	 * function; it could remove tags added in code unrelated to the code you're
	 * currently writing. That is, using this function is probably a bad idea if
	 * you have two or more unrelated code sections that add tags.
	 * @param start One bound of range to be untagged
	 * @param end Other bound of range to be untagged
	 */
	public void removeAllTags(TextTag start, TextTag end) {
		gtk_text_buffer_remove_all_tags(getHandle(), start.getHandle(), end.getHandle());
	}

	/**
	 * Obtains an iterator pointing to charOffset within the given line. The
	 * charOffset must exist, offsets off the end of the line are not allowed.
	 * Note characters, not bytes; UTF-8 may encode one character as multiple
	 * bytes.
	 * @param lineNumber Line number counting from 0
	 * @param charOffset Char offset from start of line
	 * @return Iterator
	 */
	public TextIter getIter(int lineNumber, int charOffset) {
		return new TextIter(gtk_text_buffer_get_iter_at_line_offsetMOD(getHandle(), lineNumber, charOffset));
	}
	native static final protected int gtk_text_buffer_get_iter_at_line_offsetMOD(
		int buffer,
		int lineNumber,
		int charOffset);

	/**
	 * Obtains an iterator pointing to charOffset within the entire string,
	 * statring from the beginning
	 * @param charOffset Character offset from the beginning of the text
	 * @return Iterator pointing to that point.
	 */
	public TextIter getIter(int charOffset) {
		return new TextIter(gtk_text_buffer_get_iter_at_offsetMOD(getHandle(), charOffset));
	}
	native static final protected int gtk_text_buffer_get_iter_at_offsetMOD(int buffer, int charOffset);

	/**
	 * Obtains an iterator pointing to the start of the given line
	 * @param line The line 
	 * @return Iterator
	 */
	public TextIter getLineIter(int line) {
		return new TextIter(gtk_text_buffer_get_iter_at_lineMOD(getHandle(), line));
	}
	native static final protected int gtk_text_buffer_get_iter_at_lineMOD(int buffer, int line);

	/**
	 * Returns an iter at the given mark
	 * @param mark The position for the iter
	 * @return Iterator
	 */
	public TextIter getIter(TextMark mark) {
		return new TextIter(gtk_text_buffer_get_iter_at_markMOD(getHandle(), mark.getHandle()));
	}
	native static final protected int gtk_text_buffer_get_iter_at_markMOD(int buffer, int mark);

	/**
	 * Obtains the location of anchor within buffer.
	 * @param anchor An anchor that appears in the buffer.
	 * @return Iterator
	 */
	public TextIter getIter(TextChildAnchor anchor) {
		return new TextIter(gtk_text_buffer_get_iter_at_child_anchorMOD(getHandle(), anchor.getHandle()));
	}
	native static final protected int gtk_text_buffer_get_iter_at_child_anchorMOD(int buffer, int anchor);

	/**
	 * Returns an iterator for the start of the text
	 * @return iterator
	 */
	public TextIter getStartIter() {
		return new TextIter(gtk_text_buffer_get_start_iterMOD(getHandle()));
	}
	/**
	 * Returns Iterator for the end of the text
	 * @return Iterator for the end of the text.
	 */
	public TextIter getEndIter() {
		return new TextIter(gtk_text_buffer_get_end_iterMOD(getHandle()));
	}
	native static final protected int gtk_text_buffer_get_start_iterMOD(int buffer);
	native static final protected int gtk_text_buffer_get_end_iterMOD(int buffer);

	/**
	 * Indicates whether the buffer has been modified since the last call to
	 * {@link #setModified(boolean)} set the modification flag to FALSE. Used
	 * for example to enable a "save" function in a text editor.
	 * @return TRUE if the buffer has been modified
	 */
	public boolean getModified() {
		return gtk_text_buffer_get_modified(getHandle());
	}

	/**
	 * Used to keep track of whether the buffer has been modified since the last
	 * time it was saved. Whenever the buffer is saved to disk, call
	 * setModified(FALSE). When the buffer is modified, it will automatically
	 * toggled on the modified bit again. When the modified bit flips, the
	 * buffer emits a "modified_changed" event.
	 * @param setting Modification flag setting
	 */
	public void setModified(boolean setting) {
		gtk_text_buffer_set_modified(getHandle(), setting);
	}

	/**
	 * Deletes the range between the "insert" and "selection_bound" marks, that
	 * is, the currently-selected text. If interactive is TRUE, the editability
	 * of the selection will be considered (users can't delete uneditable text).
	 * @param interactive Whether the deletion is caused by user interaction
	 * @param defaultEditable Whether the buffer is editable by default
	 * @returns Whether there was a non-empty selection to delete
	 */
	public boolean deleteSelection(boolean interactive, boolean defaultEditable) {
		return gtk_text_buffer_delete_selection(getHandle(), interactive, defaultEditable);
	}

	/**
	 * Pastes the contents of a clipboard at location. (Note: pasting is
	 * asynchronous, that is, we'll ask for the paste data and return, and at
	 * some point later after the main loop runs, the paste data will be
	 * inserted.)
	 * @param clipboard The Clipboard to paste from
	 * @param location Location to insert pasted text
	 * @param defaultEditable Whether the buffer is editable by default
	 */
	public void pasteClipboard(Clipboard clipboard, TextIter location, boolean defaultEditable) {
		gtk_text_buffer_paste_clipboard(getHandle(), clipboard.getHandle(), location.getHandle(), defaultEditable);
	}

	/**
	 * Pastes the contents of a clipboard at the insertion point. (Note: pasting
	 * is asynchronous, that is, we'll ask for the paste data and return, and at
	 * some point later after the main loop runs, the paste data will be
	 * inserted.)
	 * @param clipboard The Clipboard to paste from
	 * @param defaultEditable Whether the buffer is editable by default
	 */
	public void pasteClipboard(Clipboard clipboard, boolean defaultEditable) {
		gtk_text_buffer_paste_clipboardInsertPosition(getHandle(), clipboard.getHandle(), defaultEditable);
	}
	native protected static final void gtk_text_buffer_paste_clipboardInsertPosition(
		int handle,
		int clip,
		boolean editable);

	/**
	 * Copies the currently-selected text to a clipboard.
	 * @param clip The clipboard to copy the text to
	 */
	public void copyClipboard(Clipboard clip) {
		gtk_text_buffer_copy_clipboard(getHandle(), clip.getHandle());
	}

	/**
	 * Copies the currently-selected text to a clipboard, then deletes said text if it's editable.
	 * @param clipboard The Clipboard object to cut to.
	 * @param defaultEditable Default editability of the buffer
	 */
	public void cutClipboard(Clipboard clipboard, boolean defaultEditable) {
		gtk_text_buffer_cut_clipboard(getHandle(), clipboard.getHandle(), defaultEditable);
	}

	native protected static final boolean gtk_text_buffer_get_selection_exists(int handle);
	native protected static final int gtk_text_buffer_get_selection_start(int handle);
	native protected static final int gtk_text_buffer_get_selection_end(int handle);
	/**
	 * Returns true if an area of the buffer is selected
	 * @return True if an area is selected
	 */
	public boolean getSelected() {
		return gtk_text_buffer_get_selection_exists(getHandle());
	}

	/**
	 * Returns an iterator for the start of the selected text, or
	 * <code>null</code> if no text is selected.
	 */
	public TextIter getSelectionStart() {
		if (getSelected()) {
			return new TextIter(gtk_text_buffer_get_selection_start(getHandle()));
		} else {
			return null;
		}
	}

	/**
	 * Returns an iterator for the end of the selection, or <code>null</code> if
	 * no text is selected.
	 */
	public TextIter getSelectionEnd() {
		if (getSelected()) {
			return new TextIter(gtk_text_buffer_get_selection_end(getHandle()));
		} else {
			return null;
		}
	}

	/**
	 * Called to indicate that the buffer operations between here and a call to
	 * {@link #endUserAction()} are part of a single user-visible operation. The
	 * operations between beginUserAction() and endUserAction() can then be
	 * grouped when creating an undo stack. TextBuffer maintains a count of
	 * calls to beginUserAction() that have not been closed with a call to
	 * endUserAction(), and emits the "begin_user_action" and "end_user_action"
	 * event only for the outermost pair of calls. This allows you to build user
	 * actions from other user actions.
	 *
	 * <p>The "interactive" buffer mutation functions
	 * automatically call begin/end user action around the
	 * buffer operations they perform, so there's no need to add extra calls if
	 * you user action consists solely of a single call to one of those
	 * functions.
	 */
	public void beginUserAction() {
		gtk_text_buffer_begin_user_action(getHandle());
	}

	/**
	 * Should be paired with a call to {@link #beginUserAction()}. See
	 * that function for a full explanation.
	 */
	public void endUserAction() {
		gtk_text_buffer_end_user_action(getHandle());
	}

	/**
	 * Adds clipboard to the list of clipboards in which the selection contents
	 * of buffer are available. In most cases, clipboard will be the
	 * GtkClipboard of type GDK_SELECTION_PRIMARY for a view of buffer.
	 * @param clipboard A ClipBoard
	 */
	public void addClipboard(Clipboard clipboard) {
		gtk_text_buffer_add_selection_clipboard(getHandle(), clipboard.getHandle());
	}

	/**
	 * Removes a clipboard added with {@link #addClipboard(Clipboard)}
	 * @param clipboard The clipboard to remove
	 */
	public void removeClipboard(Clipboard clipboard) {
		gtk_text_buffer_remove_selection_clipboard(getHandle(), clipboard.getHandle());
	}

	/**
	 * This function moves the "insert" and "selection_bound" marks
	 * simultaneously. If you move them in two steps with moveMark, you will
	 * temporarily select a region in between their old and new locations, which
	 * can be pretty inefficient since the temporarily-selected region will
	 * force stuff to be recalculated. This function moves them as a unit, which
	 * can be optimized.
	 *
	 * @param ins  where to put the "insert" mark
	 * @param bound where to put the "selection_bound" mark 
	 *
	 * @since 2.4
	 */
	public void selectRange( TextIter ins, TextIter bound ){
		gtk_text_buffer_select_range    (getHandle(), ins.getHandle(), bound.getHandle() );
	}

	/****************************************
	 * TextView event handling.
	 ****************************************/
	protected void initializeEventHandlers() {
		if (!eventsInitialized) {
			evtMap.initialize(this);
			eventsInitialized = true;
		}
	}

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

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

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

	private static void addEvents(EventMap evtMap) {
		evtMap.addEvent("apply_tag", "handleApplyTag", TextBufferEvent.Type.APPLY_TAG, TextBufferListener.class);
		evtMap.addEvent("changed", "handleChanged", TextBufferEvent.Type.CHANGED, TextBufferListener.class);
		evtMap.addEvent(
			"delete_range",
			"handleDeleteRange",
			TextBufferEvent.Type.DELETE_RANGE,
			TextBufferListener.class);
		evtMap.addEvent(
			"insert_child_anchor",
			"handleInsertChildAnchor",
			TextBufferEvent.Type.INSERT_CHILD_ANCHOR,
			TextBufferListener.class);
		evtMap.addEvent(
			"insert_pixbuf",
			"handleInsertPixbuf",
			TextBufferEvent.Type.INSERT_PIXBUF,
			TextBufferListener.class);
		evtMap.addEvent("insert_text", "handleInsertText", TextBufferEvent.Type.INSERT_TEXT, TextBufferListener.class);
		evtMap.addEvent(
			"mark_deleted",
			"handleMarkDelete",
			TextBufferEvent.Type.MARK_DELETED,
			TextBufferListener.class);
		evtMap.addEvent("mark_set", "handleMarkSet", TextBufferEvent.Type.MARK_SET, TextBufferListener.class);
		evtMap.addEvent(
			"modified_changed",
			"handleModifiedChanged",
			TextBufferEvent.Type.MODIFIED_CHANGED,
			TextBufferListener.class);
		evtMap.addEvent("remove_tag", "handleRemoveTag", TextBufferEvent.Type.REMOVE_TAG, TextBufferListener.class);
	}

	/**
	 * Register an object to receive text view event notification.
	 * 
	 * @param listener The object that has implemented the 
	 * TextBufferListener interface that is to receive the text view events.
	 */
	public void addListener(TextBufferListener 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) {
				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(TextBufferListener listener) {
		int i = findListener(listeners, listener);
		if (i > -1) {
			listeners.remove(i);
		}
		if (0 == listeners.size()) {
			listeners = null;
		}
	}

	protected void fireTextBufferEvent(TextBufferEvent event) {
		if (null == listeners) {
			return;
		}
		int size = listeners.size();
		int i = 0;
		while (i < size) {
			TextBufferListener tbc = (TextBufferListener)listeners.elementAt(i);
			tbc.textBufferEvent(event);
			i++;
		}
	}

	/**
	 * Give us a way to locate a specific listener in a Vector.
	* @param list The Vector of listeners to search.
	* @param listener The object that is to be located in the Vector.
	* @return Returns the index of the listener in the Vector, or -1 if
	*                 the listener is not contained in the Vector.
	 */
	protected static int findListener(Vector list, Object listener) {
		if (null == list || null == listener)
			return -1;
		return list.indexOf(listener);
	}

	private void handleApplyTag(int tag, int startIter, int endIter) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.APPLY_TAG));
	}

	private void handleChanged() {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.CHANGED));
	}

	private void handleDeleteRange(int startIter, int endIter) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.DELETE_RANGE));
	}

	private void handleInsertChildAnchor(int position, int childAnchor) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.INSERT_CHILD_ANCHOR));
	}

	private void handleInsertPixbuf(int position, int pixbuf) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.INSERT_PIXBUF));
	}

	private void handleInsertText(int position, String text, int length) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.INSERT_TEXT));
	}

	private void handleMarkDelete(int mark) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.MARK_DELETED));
	}

	private void handleMarkSet(int location, int mark) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.MARK_SET));
	}

	private void handleModifiedChanged() {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.MODIFIED_CHANGED));
	}

	private void handleRemoveTag(int tag, int startChar, int endChar) {
		fireTextBufferEvent(new TextBufferEvent(this, TextBufferEvent.Type.REMOVE_TAG));
	}

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

	/**
	 * Creates a TextTag initialised for use in this TextBuffer.
	 */
	public TextTag createTag(String name) {
		return new TextTag(create_tag(getHandle(), name));
	}

	/****************************************
	 * BEGINNING OF JNI CODE
	 ****************************************/
	native static final protected int gtk_text_buffer_get_type();
	native static final protected int gtk_text_buffer_new(int table);
	native static final protected int gtk_text_buffer_get_line_count(int buffer);
	native static final protected int gtk_text_buffer_get_char_count(int buffer);
	native static final protected int gtk_text_buffer_get_tag_table(int buffer);
	native static final protected void gtk_text_buffer_set_text(int buffer, String text);
	native static final protected void gtk_text_buffer_insert(int buffer, int iter, String text);
	native static final protected void gtk_text_buffer_insert_at_cursor(int buffer, String text);
	native static final protected boolean gtk_text_buffer_insert_interactive(
		int buffer,
		int iter,
		String text,
		boolean defaultEditable);
	native static final protected boolean gtk_text_buffer_insert_interactive_at_cursor(
		int buffer,
		String text,
		boolean defaultEditable);
	native static final protected void gtk_text_buffer_insert_range(int buffer, int iter, int start, int end);
	native static final protected boolean gtk_text_buffer_insert_range_interactive(
		int buffer,
		int iter,
		int start,
		int end,
		boolean defaultEditable);
	native static final protected void gtk_text_buffer_delete(int buffer, int start, int end);
	native static final protected boolean gtk_text_buffer_delete_interactive(
		int buffer,
		int start,
		int end,
		boolean defaultEditable);
	native static final protected String gtk_text_buffer_get_text(
		int buffer,
		int start,
		int end,
		boolean includeHiddenChars);
	native static final protected String gtk_text_buffer_get_slice(
		int buffer,
		int start,
		int end,
		boolean includeHiddenChars);
	native static final protected void gtk_text_buffer_insert_pixbuf(int buffer, int iter, int pixbuf);
	native static final protected void gtk_text_buffer_insert_child_anchor(int buffer, int iter, int anchor);
	native static final protected int gtk_text_buffer_create_child_anchor(int buffer, int iter);
	native static final protected int gtk_text_buffer_create_mark(
		int buffer,
		String markName,
		int where,
		boolean leftGravity);
	native static final protected void gtk_text_buffer_move_mark(int buffer, int mark, int where);
	native static final protected void gtk_text_buffer_delete_mark(int buffer, int mark);
	native static final protected int gtk_text_buffer_get_mark(int buffer, String name);
	native static final protected void gtk_text_buffer_move_mark_by_name(int buffer, String name, int where);
	native static final protected void gtk_text_buffer_delete_mark_by_name(int buffer, String name);
	native static final protected int gtk_text_buffer_get_insert(int buffer);
	native static final protected int gtk_text_buffer_get_selection_bound(int buffer);
	native static final protected void gtk_text_buffer_place_cursor(int buffer, int where);
	native static final protected void gtk_text_buffer_apply_tag(int buffer, int tag, int start, int end);
	native static final protected void gtk_text_buffer_remove_tag(int buffer, int tag, int start, int end);
	native static final protected void gtk_text_buffer_apply_tag_by_name(int buffer, String name, int start, int end);
	native static final protected void gtk_text_buffer_remove_tag_by_name(int buffer, String name, int start, int end);
	native static final protected void gtk_text_buffer_remove_all_tags(int buffer, int start, int end);
	native static final protected void gtk_text_buffer_get_iter_at_line_offset(
		int buffer,
		int iter,
		int lineNumber,
		int charOffset);
	native static final protected void gtk_text_buffer_get_iter_at_line_index(
		int buffer,
		int iter,
		int lineNumber,
		int byteIndex);
	native static final protected void gtk_text_buffer_get_iter_at_offset(int buffer, int iter, int charOffset);
	native static final protected void gtk_text_buffer_get_iter_at_line(int buffer, int iter, int lineNumber);
	native static final protected void gtk_text_buffer_get_start_iter(int buffer, int iter);
	native static final protected void gtk_text_buffer_get_end_iter(int buffer, int iter);
	native static final protected void gtk_text_buffer_get_bounds(int buffer, int start, int end);
	native static final protected void gtk_text_buffer_get_iter_at_mark(int buffer, int iter, int mark);
	native static final protected void gtk_text_buffer_get_iter_at_child_anchor(int buffer, int iter, int anchor);
	native static final protected boolean gtk_text_buffer_get_modified(int buffer);
	native static final protected void gtk_text_buffer_set_modified(int buffer, boolean setting);
	native static final protected void gtk_text_buffer_add_selection_clipboard(int buffer, int clipboard);
	native static final protected void gtk_text_buffer_remove_selection_clipboard(int buffer, int clipboard);
	native static final protected void gtk_text_buffer_cut_clipboard(
		int buffer,
		int clipboard,
		boolean defaultEditable);
	native static final protected void gtk_text_buffer_copy_clipboard(int buffer, int clipboard);
	native static final protected void gtk_text_buffer_paste_clipboard(
		int buffer,
		int clipboard,
		int overrideLocation,
		boolean defaultEditable);
	native static final protected boolean gtk_text_buffer_get_selection_bounds(int buffer, int start, int end);
	native static final protected boolean gtk_text_buffer_delete_selection(
		int buffer,
		boolean interactive,
		boolean defaultEditable);
	native static final protected void gtk_text_buffer_begin_user_action(int buffer);
	native static final protected void gtk_text_buffer_end_user_action(int buffer);

	native static final protected int gtk_text_buffer_new_noTable();
	native static final protected int create_tag(int buffer, String name);
	native static final protected void insert_with_tag( int buffer, int iter, String text, String tag );
	native static final protected void gtk_text_buffer_select_range    (int handle, int ins, int bound );
	/****************************************
	 * END OF JNI CODE
	 ****************************************/
}
