/*
 * 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.glib.EventMap;
import org.gnu.glib.EventType;
import org.gnu.glib.GObject;
import org.gnu.glib.Handle;
import org.gnu.glib.Type;
import org.gnu.gtk.event.EntryEvent;
import org.gnu.gtk.event.EntryListener;

/**
 * A single line text entry widget. A fairly large set of key bindings are 
 * supported by default. If the entered text is longer than the allocation 
 * of the widget, the widget will scroll so that the cursor position is 
 * visible. 
 */
public class Entry extends Widget implements Editable {
	
	/**
	 * Creates a new Entry widget.
	 */
	public Entry(){
		super(gtk_entry_new ());
	}
	
	public Entry(Handle handle) {
	    super(handle);
	}

	/**
	 * Sets the text in the widget to the given value, replacing the current 
	 * contents.
	 * @param text The new text.
	 */
	public void setText(String text){
	    if (null == text)
	        text = "";
		gtk_entry_set_text(getHandle(), text);
	}

	/**
	 * Retrieve the contents of the entry widget. 
	 * @return the text of the widget
	 */
	public String getText(){
		return gtk_entry_get_text(getHandle());
	}

	/**
	 * Sets whether the contents of the entry are visible or not. When visibility 
	 * is set to FALSE, characters are displayed as the invisible char, and will 
	 * also appear that way when the text in the entry widget is copied elsewhere.
	 * <p>The default invisible char is the asterisk '*', but it can be changed 
	 * with setInvisibleChar().
	 */
	public void setVisible(boolean visible){
		gtk_entry_set_visibility(getHandle(), visible);
	}
	
	/**
	 * Retrieves whether the contents of the entry are visible or not.
	 */
	public boolean getVisible() {
		return gtk_entry_get_visibility(getHandle());
	}

	/**
	 * Sets the character to use in place of the actual text when
	 * setVisibility has been called to set text visibility to
	 * FALSE (ie this is the character used in "password mode" to
	 * show the user how many characters have been typed). The
	 * default invisible char is an asterisk ('*'). If you set the
	 * invisible char to 0, then the user will get no feedback at
	 * all; there will be no text on the screen as they type.
	 */
	public void setInvisibleChar(char character){
		gtk_entry_set_invisible_char(getHandle(), (byte) character);
	}

	/**
	 * Sets the maximum allowed length of the contents of the widget. If the 
	 * current contents are longer than the given length, then they will be 
	 * truncated to fit.
	 * @param max The maximum length of the entry, or 0 for no maximum. 
	 * (other than the maximum length of entries.) The value passed in will be 
	 * clamped to the range 0-65536.
	 */
	public void setMaxLength(int max){
		gtk_entry_set_max_length(getHandle(), max);
	}
	
	/**
	 * Returns the maximum length of the contents of the widget.
	 */
	public int getMaxLength() {
		return gtk_entry_get_max_length(getHandle());
	}

	/**
	 * Gets the value set by {@link #setHasFrame(boolean)}.
	 */
	public boolean getHasFrame(){
		return gtk_entry_get_has_frame(getHandle());
	}

	/**
	 * Gets the value set by {@link #setWidth(int)}.
	 * @return Number of chars to request space for, or negative if unset.
	 */
	public int getWidth(){
		return gtk_entry_get_width_chars(getHandle());
	}

	/**
	 * If setting is TRUE, pressing Enter in the entry will activate the 
	 * default widget for the window containing the entry. This usually 
	 * means that the dialog box containing the entry will be closed, 
	 * since the default widget is usually one of the dialog buttons.
	 * @param setting TRUE to activate window's default widget on Enter
	 * keypress
	 */
	public void setActivateDefaultWidget(boolean setting){
		gtk_entry_set_activates_default(getHandle(), setting);
	}

	/**
	 * Sets whether the entry has a beveled frame around it.
	 */
	public void setHasFrame(boolean setting){
		gtk_entry_set_has_frame(getHandle(), setting);
	}

	/**
	 * Changes the size request of the entry to be about the right size 
	 * for number characters. Note that it changes the size <em>request</em>,
	 * the size can still be affected by how you pack the widget into 
	 * containers. If n_chars is -1, the size reverts to the default entry 
	 * size.
	 * @param number Width in Characters
	 */
	public void setWidth(int number){
		gtk_entry_set_width_chars(getHandle(), number);
	}

	/**
	 * Gets the Layout used to display the entry. This layout is
	 * useful to convert text positions to pixel positions in combination
	 * with getLayoutOffsets.
	 * @return The PangoLayout for this entry
	 */
	public org.gnu.pango.Layout getLayout(){
	    Handle hndl = gtk_entry_get_layout(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj)
			return (org.gnu.pango.Layout)obj;
		return new org.gnu.pango.Layout(hndl);
	}
	
	/**
	 * Obtains the position of the Layout used to render text in the entry,
	 * in widget coordinates.  Useful if you want to line up the text in
	 * the entry with some other text, e.g. when using the entry to implement
	 * editable cells in a sheet widget.
	 * @return A {@link org.gnu.gdk.Point} identifying the x and y offset
	 * of the layout.
	 */
	public Point getLayoutOffsets() {
		int [] x = new int[1];
		int [] y = new int[1];
		gtk_entry_get_layout_offsets(getHandle(), x, y);
		return new Point(x[0], y[0]);
	}

    /**
     * Sets the alignment for the contents of the entry. This controls
     * the horizontal positioning of the contents when the displayed
     * text is shorter than the width of the entry.
     *
     * @param xalign The horizontal alignment, from 0 (left) to 1
     * (right). Reversed for RTL layouts.
     */
    public void setAlignment( float xalign ) {
        gtk_entry_set_alignment(getHandle(), xalign);
    }
    /**
     * Gets the value set by {@link #setAlignment}.
     */
    public float getAlignment() {
        return gtk_entry_get_alignment(getHandle());
    }
    /**
     * Converts from a position in the entry contents (returned by
     * {@link #getText}) to a position in the entry's {@link
     * org.gnu.pango.Layout PangoLayout} (returned by {@link
     * #getLayout}, with text retrieved via {@link
     * org.gnu.pango.Layout#getText}).
     */
    public int layoutIndexToTextIndex( int layoutIndex ) {
        return gtk_entry_layout_index_to_text_index(getHandle(), layoutIndex);
    }
    /**
     * Converts from a position in the entry's {@link
     * org.gnu.pango.Layout PangoLayout} (returned by {@link
     * #getLayout}) to a position in the entry contents (returned by
     * {@link #getText}).
     */
    public int textIndexToLayoutIndex( int textIndex ) {
        return gtk_entry_text_index_to_layout_index(getHandle(), textIndex);
    }

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

	/** Listeners for handling Editable events */
	private Vector entryListeners = null;

	/**
	 * Register an object to handle spin events.
	 * @see EntryListener
	 */
	public void addListener(EntryListener listener) {
		// Don't add the listener a second time if it is in the Vector.
		int i = findListener(entryListeners, listener);
		if (i == -1) {
			if (null == entryListeners) {
				evtMap.initialize(this, EntryEvent.Type.ACTIVATE);
				evtMap.initialize(this, EntryEvent.Type.MOVE_CURSOR);
				evtMap.initialize(this, EntryEvent.Type.INSERT_AT_CURSOR);
				evtMap.initialize(this, EntryEvent.Type.DELETE_FROM_CURSOR);
				evtMap.initialize(this, EntryEvent.Type.CUT_CLIPBOARD);
				evtMap.initialize(this, EntryEvent.Type.COPY_CLIPBOARD);
				evtMap.initialize(this, EntryEvent.Type.PASTE_CLIPBOARD);
				evtMap.initialize(this, EntryEvent.Type.TOGGLE_OVERWRITE);
				evtMap.initialize(this, EntryEvent.Type.CHANGED);
				evtMap.initialize(this, EntryEvent.Type.DELETE_TEXT);
				evtMap.initialize(this, EntryEvent.Type.INSERT_TEXT);
				entryListeners = new Vector();
			}
			entryListeners.addElement(listener);
		}
	}
	/**
	 * Removes a listener
	 * @see #addListener(EntryListener)
	 */
	public void removeListener(EntryListener listener) {
		int i = findListener(entryListeners, listener);
		if (i > -1)
			entryListeners.remove(i);
		if (0 == entryListeners.size()) {
			evtMap.uninitialize(this, EntryEvent.Type.ACTIVATE);
			evtMap.uninitialize(this, EntryEvent.Type.MOVE_CURSOR);
			evtMap.uninitialize(this, EntryEvent.Type.INSERT_AT_CURSOR);
			evtMap.uninitialize(this, EntryEvent.Type.DELETE_FROM_CURSOR);
			evtMap.uninitialize(this, EntryEvent.Type.CUT_CLIPBOARD);
			evtMap.uninitialize(this, EntryEvent.Type.COPY_CLIPBOARD);
			evtMap.uninitialize(this, EntryEvent.Type.PASTE_CLIPBOARD);
			evtMap.uninitialize(this, EntryEvent.Type.TOGGLE_OVERWRITE);
			evtMap.uninitialize(this, EntryEvent.Type.CHANGED);
			evtMap.uninitialize(this, EntryEvent.Type.DELETE_TEXT);
			evtMap.uninitialize(this, EntryEvent.Type.INSERT_TEXT);
			entryListeners = null;
		}
	}

	protected void fireEntryEvent(EntryEvent event) {
		if (null == entryListeners)
			return;
		int size = entryListeners.size();
		int i = 0;
		while (i < size) {
			EntryListener el = (EntryListener)entryListeners.elementAt(i);
			el.entryEvent(event);
			i++;
		}
	}

	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;
	}

	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("activate", "handleActivate", EntryEvent.Type.ACTIVATE, EntryListener.class);
		anEvtMap.addEvent("move_cursor", "handleMoveCursor", EntryEvent.Type.MOVE_CURSOR, EntryListener.class);
		anEvtMap.addEvent("insert_at_cursor", "handleInsertAtCursor", EntryEvent.Type.INSERT_AT_CURSOR, EntryListener.class);
		anEvtMap.addEvent("delete_from_cursor", "handleDeleteFromCursor", EntryEvent.Type.DELETE_FROM_CURSOR, EntryListener.class);
		anEvtMap.addEvent("cut_clipboard", "handleCutClipboard", EntryEvent.Type.CUT_CLIPBOARD, EntryListener.class);
		anEvtMap.addEvent("copy_clipboard", "handleCopyClipboard", EntryEvent.Type.COPY_CLIPBOARD, EntryListener.class);
		anEvtMap.addEvent("paste_clipboard", "handlePasteClipboard", EntryEvent.Type.PASTE_CLIPBOARD, EntryListener.class);
		anEvtMap.addEvent("toggle_overwrite", "handleToggleOverwrite", EntryEvent.Type.TOGGLE_OVERWRITE, EntryListener.class);
		/*
		 * From Editable
		 */
		anEvtMap.addEvent("changed", "handleChanged", EntryEvent.Type.CHANGED, EntryListener.class);
		anEvtMap.addEvent("delete_text", "handleDeleteText", EntryEvent.Type.DELETE_TEXT, EntryListener.class);
		anEvtMap.addEvent("insert_text", "handleInsertText", EntryEvent.Type.INSERT_TEXT, EntryListener.class);
	}

	private void handleActivate() {
		fireEntryEvent(new EntryEvent(this, EntryEvent.Type.ACTIVATE));
	}

	private void handleMoveCursor(int step, int count, boolean extendedSelection) {
		EntryEvent ee = new EntryEvent(this, EntryEvent.Type.MOVE_CURSOR);
		ee.setMovementStep(MovementStep.intern(step));
		ee.setCount(count);
		ee.setExtendedSelection(extendedSelection);
		fireEntryEvent(ee);
	}

	private void handleInsertAtCursor(String text) {
		EntryEvent ee = new EntryEvent(this, EntryEvent.Type.INSERT_AT_CURSOR);
		ee.setText(text);
		fireEntryEvent(ee);
	}
	
	private void handleDeleteFromCursor(int deleteType, int count) {
		EntryEvent ee = new EntryEvent(this, EntryEvent.Type.DELETE_FROM_CURSOR);
		ee.setDeleteType(DeleteType.intern(deleteType));
		ee.setCount(count);
		fireEntryEvent(ee);
	}
	
	private void handleCutClipboard() {
		fireEntryEvent(new EntryEvent(this, EntryEvent.Type.CUT_CLIPBOARD));
	}
	
	private void handleCopyClipboard() {
		fireEntryEvent(new EntryEvent(this, EntryEvent.Type.COPY_CLIPBOARD));
	}
	
	private void handlePasteClipboard() {
		fireEntryEvent(new EntryEvent(this, EntryEvent.Type.PASTE_CLIPBOARD));
	}
	
	public void handleToggleOverwrite() {
		fireEntryEvent(new EntryEvent(this, EntryEvent.Type.TOGGLE_OVERWRITE));
	}
	
	private void handleChanged() {
		fireEntryEvent(new EntryEvent(this, EntryEvent.Type.CHANGED));
	}

	private void handleDeleteText(int start, int end) {
		EntryEvent ee = new EntryEvent(this, EntryEvent.Type.DELETE_TEXT);
		ee.setStartPosition(start);
		ee.setEndPosition(end);
		fireEntryEvent(ee);
	}

	private void handleInsertText(String text, int length, Handle position) {
		EntryEvent ee = new EntryEvent(this, EntryEvent.Type.INSERT_TEXT);
		ee.setText(text);
		//ee.setInsertPosition(position);
                //Should be something like:
                ee.setInsertPosition( getIntFromHandle(position) );
		fireEntryEvent(ee);
	}
	
	public void setCompletion(EntryCompletion completion) {
		gtk_entry_set_completion(getHandle(), completion.getHandle());
	}
	
	public EntryCompletion getCompletion() {
	    Handle hndl = gtk_entry_get_completion(getHandle());
		GObject obj = getGObjectFromHandle(hndl);
		if (null != obj)
			return (EntryCompletion)obj;
		return new EntryCompletion(hndl);
	}

	
	/****************************
	 * Methods from Editable
	 ***************************/
	public void selectRegion(int start, int end) {
		EditableHelper.gtk_editable_select_region(getHandle(), start, end);
	}

	public int insertText(String text, int offset) {
		int[] ptr = new int[1];
		ptr[0] = offset;
		EditableHelper.gtk_editable_insert_text(getHandle(), text, text.length(), ptr);
		return ptr[0];
	}
	
	public void deleteText(int start, int end) {
		EditableHelper.gtk_editable_delete_text(getHandle(), start, end);
	}

	public String getCharacters(int start, int end) {
		return EditableHelper.gtk_editable_get_chars(getHandle(), start, end);
	}

	public void cutClipboard() {
		EditableHelper.gtk_editable_cut_clipboard(getHandle());
	}
	
	public void copyClipboard() {
		EditableHelper.gtk_editable_copy_clipboard(getHandle());
	}
	
	public void pasteClipboard() {
		EditableHelper.gtk_editable_paste_clipboard(getHandle());
	}
	
	public void deleteSelection() {
		EditableHelper.gtk_editable_delete_selection(getHandle());
	}
	
	public void setCursorPosition(int position) {
		EditableHelper.gtk_editable_set_position(getHandle(), position);
	}
	
	public int getCursorPosition() {
		return EditableHelper.gtk_editable_get_position(getHandle());
	}
	
	public void setEditable(boolean isEditable) {
		EditableHelper.gtk_editable_set_editable(getHandle(), isEditable);
	}
	
	public boolean getEditable() {
		return EditableHelper.gtk_editable_get_editable(getHandle());
	}
	

    native static final protected int gtk_entry_get_type ();
    native static final protected Handle gtk_entry_new ();
    native static final protected void gtk_entry_set_visibility (Handle entry, boolean visible);
    native static final protected boolean gtk_entry_get_visibility (Handle entry);
    native static final protected void gtk_entry_set_invisible_char (Handle entry, byte ch);
    native static final protected byte gtk_entry_get_invisible_char (Handle entry);
    native static final protected void gtk_entry_set_has_frame (Handle entry, boolean setting);
    native static final protected boolean gtk_entry_get_has_frame (Handle entry);
    native static final protected void gtk_entry_set_max_length (Handle entry, int max);
    native static final protected int gtk_entry_get_max_length (Handle entry);
    native static final protected void gtk_entry_set_activates_default (Handle entry, boolean setting);
    native static final protected boolean gtk_entry_get_activates_default (Handle entry);
    native static final protected void gtk_entry_set_width_chars (Handle entry, int numChars);
    native static final protected int gtk_entry_get_width_chars (Handle entry);
    native static final protected void gtk_entry_set_text (Handle entry, String text);
    native static final protected String gtk_entry_get_text (Handle entry);
    native static final protected Handle gtk_entry_get_layout (Handle entry);
    native static final protected void gtk_entry_get_layout_offsets (Handle entry, int [] x, int [] y);
    native static final protected void gtk_entry_set_completion(Handle entry, Handle completion);
    native static final protected Handle gtk_entry_get_completion(Handle entry);
    native static final private void gtk_entry_set_alignment(Handle entry, float xalign);
    native static final private float gtk_entry_get_alignment(Handle entry);
    native static final private int gtk_entry_layout_index_to_text_index(Handle entry, int layout_index);
    native static final private int gtk_entry_text_index_to_layout_index(Handle entry, int text_index);

    /* Deprecated functions.
    native static final private Handle gtk_entry_new_with_max_length(int max);
    native static final private void gtk_entry_append_text(Handle entry, String text);
    native static final private void gtk_entry_prepend_text(Handle entry, String text);
    native static final private void gtk_entry_set_position(Handle entry, int position);
    native static final private void gtk_entry_select_region(Handle entry, int start, int end);
    native static final private void gtk_entry_set_editable(Handle entry, boolean editable);
    */
}

