/*
 * 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.NotebookEvent;
import org.gnu.gtk.event.NotebookListener;
import org.gnu.gtk.event.GtkEventType;

/**
 * A Notebook widget is a container whose children are pages that can be switched
 * between using tab labels along one edge.
 * <p>
 * There are many configuration options for a Notebook.  Among other things you can
 * choose on which edge the tabs appear, whether, if there are too many tabs to fit th
 * notebook should be made bigger or scrolling arrows added, and whether there will
 * be a popup menu allowing the user to switch between pages.
 */
public class Notebook extends Container {

	/**
	 * Container to hold all listeners that are to receive events.
	 */
	private Vector listeners = null;

	/**
	 * Construct a new Notebook object with no pages.
	 */
	public Notebook() {
		super(gtk_notebook_new());
		initializeEventHandlers();
	}

	/**
	 * Construct a new Notebook from a handle to a native resource.
	 */
	public Notebook(int handle) {
		super(handle);
	    initializeEventHandlers();
	}

	/**
	 * Append a page to the Notebook.
	 *
	 * @param child The child widget to add to the newly created page.
	 * @param tabLabel The Widget to be used as the label for the page or <code>null</code
	 * to use the default label, 'page N'.
	 */
	public void appendPage(Widget child, Widget tabLabel) {
		if (null == tabLabel) {
			Notebook.gtk_notebook_append_page(getHandle(), child.getHandle(), 0);
		} else {
			Notebook.gtk_notebook_append_page(getHandle(), child.getHandle(), tabLabel.getHandle());
		}
	}

	/**
	 * Append a page to the Notebook.
	 * 	
	 * @param child The child widget to add to the newly created page.
	 * @param tabLabel The Widget to be used as the label for the page or <code>null</code
	 * to use the default label, 'page N'.
	 * @param menuLabel The widget to use as a label for the page-switching menu, if that is
	 * enabled.  If <code>null</code> and <code>tabLabel</code> is a Label or <code>null</code>
	 * then the menu label will be a newly created label with the same text as the <code>tabLabel</code>.
	 */
	public void appendPage(Widget child, Widget tabLabel, Widget menuLabel) {
		if (null == menuLabel) {
			if (null == tabLabel) {
				Notebook.gtk_notebook_append_page_menu(getHandle(), child.getHandle(), 0, 0);
			} else {
				Notebook.gtk_notebook_append_page_menu(getHandle(), child.getHandle(), tabLabel.getHandle(), 0);
			}
		} else {
			Notebook.gtk_notebook_append_page_menu(
				getHandle(),
				child.getHandle(),
				tabLabel.getHandle(),
				menuLabel.getHandle());
		}
	}

	/**
	 * Prepend a page to the Notebook.
	 *
	 * @param child The child widget to add to the newly created page.
	 * @param tabLabel The Widget to be used as the label for the page or <code>null</code
	 * to use the default label, 'page N'.
	 */
	public void prependPage(Widget child, Widget tabLabel) {
		if (null == tabLabel) {
			Notebook.gtk_notebook_prepend_page(getHandle(), child.getHandle(), 0);
		} else {
			Notebook.gtk_notebook_prepend_page(getHandle(), child.getHandle(), tabLabel.getHandle());
		}
	}

	/**
	 * Prepend a page to the Notebook.
	 * 	
	 * @param child The child widget to add to the newly created page.
	 * @param tabLabel The Widget to be used as the label for the page or <code>null</code
	 * to use the default label, 'page N'.
	 * @param menuLabel The widget to use as a label for the page-switching menu, if that is
	 * enabled.  If <code>null</code> and <code>tabLabel</code> is a Label or <code>null</code>
	 * then the menu label will be a newly created label with the same text as the <code>tabLabel</code>.
	 */
	public void prependPage(Widget child, Widget tabLabel, Widget menuLabel) {
		if (null == menuLabel) {
			if (null == tabLabel) {
				Notebook.gtk_notebook_prepend_page_menu(getHandle(), child.getHandle(), 0, 0);
			} else {
				Notebook.gtk_notebook_prepend_page_menu(getHandle(), child.getHandle(), tabLabel.getHandle(), 0);
			}
		} else {
			Notebook.gtk_notebook_prepend_page_menu(
				getHandle(),
				child.getHandle(),
				tabLabel.getHandle(),
				menuLabel.getHandle());
		}
	}

	/**
	 * Insert a page into the Notebook at the given position.
	 *
	 * @param child The child widget to add to the newly created page.
	 * @param tabLabel The Widget to be used as the label for the page or <code>null</code
	 * to use the default label, 'page N'.
	 * @param position The index (starting at 0) at which to insert the page.
	 */
	public void insertPage(Widget child, Widget tabLabel, int position) {
		if (null == tabLabel) {
			Notebook.gtk_notebook_insert_page(getHandle(), child.getHandle(), 0, position);
		} else {
			Notebook.gtk_notebook_insert_page(getHandle(), child.getHandle(), tabLabel.getHandle(), position);
		}
	}

	/**
	 * Insert a page into the Notebook at the given position.
	 * 	
	 * @param child The child widget to add to the newly created page.
	 * @param tabLabel The Widget to be used as the label for the page or <code>null</code
	 * to use the default label, 'page N'.
	 * @param menuLabel The widget to use as a label for the page-switching menu, if that is
	 * enabled.  If <code>null</code> and <code>tabLabel</code> is a Label or <code>null</code>
	 * then the menu label will be a newly created label with the same text as the <code>tabLabel</code>.
	 * @param position The index (starting at 0) at which to insert the page.
	 */
	public void insertPage(Widget child, Widget tabLabel, Widget menuLabel, int position) {
		if (null == menuLabel) {
			if (null == tabLabel) {
				Notebook.gtk_notebook_insert_page_menu(getHandle(), child.getHandle(), 0, 0, position);
			} else {
				Notebook.gtk_notebook_insert_page_menu(getHandle(), child.getHandle(), tabLabel.getHandle(), 0, position);
			}
		} else {
			Notebook.gtk_notebook_insert_page_menu(
				getHandle(),
				child.getHandle(),
				tabLabel.getHandle(),
				menuLabel.getHandle(),
				position);
		}
	}

	/**
	 * Removes a page fro the Notegook given its index in the notebook.
	 * 
	 * @param pageNumber The offset (starting with 0) for the page to remove.
	 */
	public void removePage(int pageNumber) {
		Notebook.gtk_notebook_remove_page(getHandle(), pageNumber);
	}

	/**
	 * Finds the index of the page which contains the given child.
	 * 
	 * @param child The widget to locate.
	 * @return The index of the page which contaons the given child.
	 */
	public int pageNum(Widget child) {
		return Notebook.gtk_notebook_page_num(getHandle(), child.getHandle());
	}

	/**
	 * Switches to the next page.  Nothing happens if the current page is the
	 * last page.
	 */
	public void nextPage() {
		Notebook.gtk_notebook_next_page(getHandle());
	}

	/**
	 * Switches to the previous page.  Nothing happens if the current page is the
	 * first page.
	 */
	public void prevPage() {
		Notebook.gtk_notebook_prev_page(getHandle());
	}

	/**
	 * Reorders the page containing <code>child</code>, so that it appears in position
	 * </code>position</code>. If <code>position</code> is greater than or equal to the
	 * number of children in the list or negative, <code>child</code> will be moved to the
	 * end of the list.
	 * 
	 * @param child The widget to move.
	 * @param position The index (starting with 0) to move the child.
	 */
	public void reorderChild(Widget child, int position) {
		Notebook.gtk_notebook_reorder_child(getHandle(), child.getHandle(), position);
	}

	/**
	 * Sets the edge at which the tabs for switching pages in the Notebook are drawn.
	 */
	public void setTabPosition(PositionType pos) {
		Notebook.gtk_notebook_set_tab_pos(getHandle(), pos.getValue());
	}

	/**
	 * sets whether to show the tabs for the notebook or not.
	 */
	public void setShowTabs(boolean showTabs) {
		Notebook.gtk_notebook_set_show_tabs(getHandle(), showTabs);
	}

	/**
	 * Sets whether a bevel will be drawn around the notebook pages.  
	 */
	public void setShowBorder(boolean showBorder) {
		Notebook.gtk_notebook_set_show_border(getHandle(), showBorder);
	}

	/**
	 * Sets whether the tab label area will have arrows for scrolling if there are too
	 * many tabs to fit in the area.
	 */
	public void setScrollable(boolean scrollable) {
		Notebook.gtk_notebook_set_scrollable(getHandle(), scrollable);
	}

	/**
	 * Enables the popup menu.
	 */
	public void popupEnable() {
		Notebook.gtk_notebook_popup_enable(getHandle());
	}

	/**
	 * disables the popup menu.
	 */
	public void popupDisable() {
		Notebook.gtk_notebook_popup_disable(getHandle());
	}

	/**
	 * Returns the page number of the current page.
	 */
	public int getCurrentPage() {
		return Notebook.gtk_notebook_get_current_page(getHandle());
	}

	/**
	 * sets the page at offset <code>pageNum</code> to the current page.
	 */
	public void setCurrentPage(int pageNum) {
		Notebook.gtk_notebook_set_current_page(getHandle(), pageNum);
	}

	/**
	 * Returns the child Widget contained int he page number <code>pageNum</code>.
	 */
	public Widget getPage(int pageNum) {
		int hndl = Notebook.gtk_notebook_get_nth_page(getHandle(), pageNum);
		if (0 == hndl)
			return null;
		GObject obj = retrieveGObject(hndl);
		if (null != obj)
			return (Widget)obj;
		return new Widget(hndl);
	}

	/**
	 * Changes the menu label for the page containing <code>child</code>.
	 */
	public void setMenuLabel(Widget child, Widget menuLabel) {
		Notebook.gtk_notebook_set_menu_label(getHandle(), child.getHandle(), menuLabel.getHandle());
	}

	/**
	 * Creates a new label and sets it as the menu label of <code>child</code>.
	 */
	public void setMenuLabel(Widget child, String menuText) {
		Notebook.gtk_notebook_set_menu_label_text(getHandle(), child.getHandle(), menuText);
	}

	/**
	 * Changes the tab label for <code>child</code>.
	 */
	public void setTabLabel(Widget child, Widget tabLabel) {
		if (null == tabLabel) {
			Notebook.gtk_notebook_set_tab_label(getHandle(), child.getHandle(), 0);
		} else {
			Notebook.gtk_notebook_set_tab_label(getHandle(), child.getHandle(), tabLabel.getHandle());
		}
	}

	/**
	 * Creates a new label and sets it as the labelf or the page containing <code>child</code>.
	 */
	public void setTabLabel(Widget child, String tabText) {
		Notebook.gtk_notebook_set_tab_label_text(getHandle(), child.getHandle(), tabText);
	}

	/**
	 * Sets the packing parameters for the tab label of the page containing <code>child</code>.
	 * 
	 * @see Box
	 */
	public void setTabLabelPacking(Widget child, boolean expand, boolean fill, PackType packType) {
		Notebook.gtk_notebook_set_tab_label_packing(getHandle(), child.getHandle(), expand, fill, packType.getValue());
	}
	
	/**
	 * Return the number of pages currently on the notebook.
	 * @return
	 */
	public int getNumPages() {
		return gtk_notebook_get_n_pages(getHandle());
	}

	/**
	 * Initialize this widget to receive its' own events so it can
	 * propogate them to the listeners.
	 */
	protected void initializeEventHandlers() {
		if (!eventsInitialized) {
			super.initializeEventHandlers();
			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);
	}

	/**
	* 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("select_page", "handleSelectPage", NotebookEvent.Type.SELECT_PAGE, NotebookListener.class);
		anEvtMap.addEvent("switch_page", "handleSwitchPage", NotebookEvent.Type.SWITCH_PAGE, NotebookListener.class);
	}

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

	protected void fireNotebookEvent(NotebookEvent event) {
		if (null == listeners) {
			return;
		}
		int size = listeners.size();
		int i = 0;
		while (i < size) {
			NotebookListener nl = (NotebookListener)listeners.elementAt(i);
			nl.notebookEvent(event);
			i++;
		}
	}

	private boolean handleSelectPage(boolean moveFocus) {
		fireNotebookEvent(new NotebookEvent(this, NotebookEvent.Type.SELECT_PAGE));
		return true;
	}

	private void handleSwitchPage(int page, int pageNum) {
		fireNotebookEvent(new NotebookEvent(this, NotebookEvent.Type.SWITCH_PAGE, pageNum));
	}

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

	/****************************************
	 * BEGINNING OF JNI CODE
	 ****************************************/
	native static final protected int gtk_notebook_get_type();
	native static final protected int gtk_notebook_new();
	native static final protected void gtk_notebook_append_page(int notebook, int child, int tabLabel);
	native static final protected void gtk_notebook_append_page_menu(
		int notebook,
		int child,
		int tabLabel,
		int menuLabel);
	native static final protected void gtk_notebook_prepend_page(int notebook, int child, int tabLabel);
	native static final protected void gtk_notebook_prepend_page_menu(
		int notebook,
		int child,
		int tabLabel,
		int menuLabel);
	native static final protected void gtk_notebook_insert_page(int notebook, int child, int tabLabel, int position);
	native static final protected void gtk_notebook_insert_page_menu(
		int notebook,
		int child,
		int tabLabel,
		int menuLabel,
		int position);
	native static final protected void gtk_notebook_remove_page(int notebook, int pageNum);
	native static final protected int gtk_notebook_get_current_page(int notebook);
	native static final protected int gtk_notebook_get_nth_page(int notebook, int pageNum);
	native static final protected int gtk_notebook_page_num(int notebook, int child);
	native static final protected void gtk_notebook_set_current_page(int notebook, int pageNum);
	native static final protected void gtk_notebook_next_page(int notebook);
	native static final protected void gtk_notebook_prev_page(int notebook);
	native static final protected void gtk_notebook_set_show_border(int notebook, boolean showBorder);
	native static final protected boolean gtk_notebook_get_show_border(int notebook);
	native static final protected void gtk_notebook_set_show_tabs(int notebook, boolean showTabs);
	native static final protected boolean gtk_notebook_get_show_tabs(int notebook);
	native static final protected void gtk_notebook_set_tab_pos(int notebook, int pos);
	native static final protected int gtk_notebook_get_tab_pos(int notebook);
	native static final protected void gtk_notebook_set_scrollable(int notebook, boolean scrollable);
	native static final protected boolean gtk_notebook_get_scrollable(int notebook);
	native static final protected void gtk_notebook_popup_enable(int notebook);
	native static final protected void gtk_notebook_popup_disable(int notebook);
	native static final protected int gtk_notebook_get_tab_label(int notebook, int child);
	native static final protected void gtk_notebook_set_tab_label(int notebook, int child, int tabLabel);
	native static final protected void gtk_notebook_set_tab_label_text(int notebook, int child, String tabText);
	native static final protected String gtk_notebook_get_tab_label_text(int notebook, int child);
	native static final protected int gtk_notebook_get_menu_label(int notebook, int child);
	native static final protected void gtk_notebook_set_menu_label(int notebook, int child, int menuLabel);
	native static final protected void gtk_notebook_set_menu_label_text(int notebook, int child, String menuText);
	native static final protected String gtk_notebook_get_menu_label_text(int notebook, int child);
	native static final protected void gtk_notebook_query_tab_label_packing(
		int notebook,
		int child,
		boolean[] expand,
		boolean[] fill,
		int[] packType);
	native static final protected void gtk_notebook_set_tab_label_packing(
		int notebook,
		int child,
		boolean expand,
		boolean fill,
		int packType);
	native static final protected void gtk_notebook_reorder_child(int notebook, int child, int position);
	native static final protected int gtk_notebook_get_n_pages(int notebook);
	/****************************************
	 * END OF JNI CODE
	 ****************************************/
}
