/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2002 The Inti Development Team.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

//! @file inti/gtk/itemfactory.h
//! @brief A GtkItemFactory C++ wrapper interface.
//!
//! Itemfactories are a quick way to implement a menu heirarchy. You can create simple
//! menus or entire an entire menu bar with its associated menu items and submenus.
//! Item factories are easy to use, especially when creating many menus, and there is
//! no difference in appearence from menus created manually. There are some trade-offs
//! in control:
//! <ul>
//! <li>Each class can only have one item factory map.
//! <li>Each menu item in the item factory map must be connected to its own slot.
//! </ul>
//!
//! What you need to decide is which is more important, control or ease of programming.
//! You will find example code using an ItemFactory in itemfactory.cc in the
//! <examples/menu> directory.

#ifndef INTI_GTK_ITEM_FACTORY_H
#define INTI_GTK_ITEM_FACTORY_H

#ifndef INTI_GTK_OBJECT_H
#include <inti/gtk/object.h>
#endif

#ifndef __GTK_ITEM_FACTORY_H__
#include <gtk/gtkitemfactory.h>
#endif

#ifndef _CPP_VECTOR
#include <vector>
#endif

namespace Inti {

namespace G {
class Scanner;
}

namespace Gtk {

class AccelGroup;
class ItemFactoryClass;
class MenuBar;
class Menu;
class MenuItem;
class OptionMenu;
class Widget;

//! @class ItemFactoryEntry itemfactory.h inti/gtk/itemfactory.h
//! @brief A C++ class that represents a GtkItemFactoryEntry.
//!
//! ItemFactoryEntry holds all the information needed to create an item in an item factory map.
//! Each entry in a map is referred to by a path that specifies the hierarchical relationship
//! between the entries. The first six data members in ItemFactoryEntry are indentical to the fields
//! in GtkItemFactoryEntry. This is so an ItemFactoryEntry can be cast to a GtkItemFactoryEntry when
//! passed to a C function expecting the latter. Like GtkItemFactoryEntry not all the data members
//! are used. The <EM>callback</EM> and <EM>callback_action</EM> members are seldom used. The
//! <EM>path</EM> and <EM>item_type</EM> are always used. The <EM>accelerator</EM> and
//! <EM>image_item_or_stock_item</EM> are sometimes used, depending on the item entry type. You
//! don't have to worry about which members to use though, macros are defined for each item entry
//! type that require the correct number of arguments.
//!
//! A <EM>path</EM> is a character string, with / delimiters indicating the levels (parent/child
//! relationships) in the hierarchy. For example, the file menu would be "/_File", and the New
//! item on the file menu would be "/File/_New".
//!
//! The <EM>accelerator</EM> is a character string indicating the keyboard shortcut for the menu
//! item, if any. It consists of any modifiers and the accelerator key. For example, "\<Ctrl\>N" is
//! used for the New item on the file menu. Alternatively this could be "\<Ctrl\>N" or "\<control\>N".
//! Other modifiers are \<Alt\>, \<Shift\>, \<Shft\> and \<Release\>. A modifier can have any variation in
//! capitalization because GTK+ checks the modifer one letter at a time for both upper and lower
//! case.
//!
//! The <EM>item_type</EM> is not used directly. Instead each item_type has its own macro. Some of
//! the macros defined are IFM_ITEM, IFM_STOCK_ITEM, IFM_RADIO_ITEM, IFM_TEAROFF_ITEM, IFM_SEPARATOR
//! and IFM_BRANCH. All of the macros are listed in <inti/gtk/itemfactory.h> (see the itemfactory.h
//! file reference).

template<typename T> struct ItemFactoryEntry
{
	typedef void (T::*PMF)(); //!< A generic pointer-to-member function.
	const char *path; //!< The path to the menu item.
	const char *accelerator; //!< The accelerator for the menu item.
	GtkItemFactoryCallback callback; //!< The C menu item callback; unused.
	unsigned int callback_action; //!< The callback action; unused.
	const char *item_type; //!< The menu item type to create.
	const void* image_item_or_stock_item; //!< An image or stock item.
	PMF pmf; //!< A pointer to the member function to call when the menu item is activated.
};

//! @class ItemFactory itemfactory.h inti/gtk/itemfactory.h
//! @brief A GtkItemFactory C++ wrapper class.
//!
//! Itemfactories are a quick way to implement a menu heirarchy. You can create simple
//! menus or entire an entire menu bar with its associated menu items and submenus.
//! Item factories are easy to use, especially when creating many menus, and there is
//! no difference in appearence from menus created manually. There are some trade-offs
//! in control:
//! <ul>
//! <li>Each class can only have one item factory map.
//! <li>Each menu item in the item factory map must be connected to its own slot.
//! </ul>
//!
//! What you need to decide is which is more important, control or ease of programming.
//! You will find example code using an ItemFactory in itemfactory.cc in the
//! <examples/menu> directory (see the itemfactory.h file reference).
//!
//! The 8 basic steps in using an ItemFactory are:
//! <ol>
//! <li>Add an ItemFactory pointer to your class declaration. Make this a smart pointer
//!     so you don't have to call unref(). 
//! <li>Add the DECLARE_ITEM_FACTORY_MAP macro to you class declaration. This macro takes
//!     the class name as an argument.
//! <li>In the class source file define your item factory map. The map is just a series of macros.
//!     It begins with the BEGIN_ITEM_FACTORY_MAP macro which takes the class name as an argument.
//!     Then comes a list of one or more menu item macros, one for each submenu and menu item in
//!     the menu. Last comes the END_ITEM_FACTORY_MAP macro. Together these macros define an array
//!     of Gtk::ItemFactoryEntry that contains all the information needed to implement the menu
//!     hierarchy.
//! <li>Construct a new ItemFactory.
//! <li>Call Gtk::Window::add_accel_group() to add the AccelGroup to the owner window. If you
//!     don't pass an AccelGroup to the ItemFactory constructor the ItemFactory will create a
//!     new AccelGroup for you. In this case you can call accel_group() to get the AccelGroup
//!     to pass to Gtk::Window::add_accel_group().
//! <li>Call create_items() to create the menus and menu items in the item factory map. This
//!     method takes a reference to the class, which must be a class derived from G::Object.
//! <li>Call one of the Accessor methods menu_bar(), menu() or option_menu() to get a pointer
//!     to the new menu. Use this pointer to connect any extra signals and to add or pack the
//!     new menu into another widget.
//! <li>Call submenu() or get_item() if you need to access a submenu or menu item. Note, the
//!     menu item path string you pass as an argument is without any preceding underscore. 
//!     This is because the underscores specifed in the item factory map are parsed out of
//!     the path string by GTK+.
//! </ol>
//! And that's it! Simple? Well it is once you get used to it.

class ItemFactory : public Object
{
	friend class G::Object;
	friend class ItemFactoryClass;

	ItemFactory(const ItemFactory&);
	ItemFactory& operator=(const ItemFactory&);

protected:
//! @name Constructors
//! @{

	explicit ItemFactory(GtkItemFactory *item_factory, bool reference = true);
	//!< Construct a new ItemFactory from an existing GtkItemFactory.
	//!< @param item_factory A pointer to a GtkItemFactory.
	//!< @param reference Set false if the initial reference count is floating, set true if it's not.
	//!<
	//!< <BR>The <EM>item_factory</EM> can be a newly created GtkItemFactory or an existing
	//!< GtkItemFactory (see G::Object::Object).

//! @}
	
public:
	typedef Slot0<void> DestroySlot;
	//!< Signature of the callback slot to be called when a popup menu is unposted.
	//!< <B>Example:</B> Method signature for DestroySlot.
	//!< @code
	//!< void method();
	//!< @endcode

//! @name Constructors
//! @{

	ItemFactory();
	//!< Construct a new ItemFactory with the container type GTK_TYPE_MENU_BAR, the path "\<main\>"
	//!< and a default accelerator group.

	ItemFactory(GType container_type, const char *path, AccelGroup *accel_group = 0);
	//!< Construct a new ItemFactory with the specified container type, path and accelerator group.
	//!< @param container_type The kind of menu to create; can be GTK_TYPE_MENU_BAR, GTK_TYPE_MENU
	//!<                       or GTK_TYPE_OPTION_MENU.
	//!< @param path The factory path of the new item factory, a string of the form "\<name\>".
	//!< @param accel_group An AccelGroup to which the accelerators for the menu items will be added,
	//!<                    or null to create a new one.

	virtual ~ItemFactory();
	//!< Destructor.

//! @}
//! @name Accessors
//! @{

	GtkItemFactory* gtk_item_factory() const { return (GtkItemFactory*)instance; }
	//!< Get a pointer to the GtkItemFactory structure.

	GtkItemFactoryClass* gtk_item_factory_class() const;
	//!< Get a pointer to the GtkItemFactoryClass structure.

	operator GtkItemFactory* () const;
	//!< Conversion operator; safely converts an ItemFactory to a GtkItemFactory pointer.

	AccelGroup* accel_group() const;
	//!< Gets the accelerator group for the ItemFactory.

	MenuBar* menu_bar() const;
	//!< Returns the MenuBar if the container type is GTK_TYPE_MENU_BAR, otherwise it returns null.

	Menu* menu() const;
	//!< Returns the Menu if the container type is GTK_TYPE_MENU, otherwise it returns null.

	OptionMenu* option_menu() const;
	//!< Returns the OptionMenu if the container type is GTK_TYPE_OPTION_MENU, otherwise it returns null.

	Menu* submenu(const char* path) const;
	//!< Obtains the submenu which corresponds to path.
	//!< @param path The path to the submenu.
	//!< @return The submenu for the given path, or null if path doesn't lead to a submenu.

	Menu* submenu_by_action(unsigned int action) const;
	//!< Obtains the submenu which was constructed from the ItemFactoryEntry with the given action.
	//!< @param action An action as specified in the callback_action field of ItemFactoryEntry.
	//!< @return The submenu which corresponds to the given action, or null if no submenu was found.
	//!<
	//!< <BR>If there are multiple items with the same action, the result is undefined.

	MenuItem* get_item(const char *path) const;
	//!< Obtains the menu item which corresponds to path.
	//!< @param path The path to the menu item.
	//!< @return The menu item for the given path, or null if path doesn't lead to a menu item.

	MenuItem* get_item_by_action(unsigned int action) const;
	//!< Obtains the menu item which was constructed from the first ItemFactoryEntry with the given action.
	//!< @param action An action as specified in the callback_action field of ItemFactoryEntry.
	//!< @return The menu item which corresponds to the given action, or null if no menu item was found.

//! @}

	static String path_from_widget(const Widget& widget);
	//!< If widget has been created by an item factory, returns the full path to it. 
	//!< @param widget A widget.
	//!< @return The full path to widget if it has been created by an item factory, null otherwise.
	//!<
	//!< <BR>The full path of a widget is the concatenation of the factory path specified in the
	//!< ItemFactory constructor with the path specified in the ItemFactoryEntry from which
	//!< the widget was created.

	static String path_from_entry(const char *entry_path);
	//!< Returns <EM>entry_path</EM> without any underscores.
	//!< @param entry_path The path for an entry as specified in the item factroy map.
	//!< @return The entry path without any underscores.
	//!<
	//!< <BR>This method is used internally by ItemFactory to parse out underscores from the
	//!< entry paths specified in an item factroy map. It is provided as a separate public 
	//!< method in case you ever need to iterate over each entry in a map. If you do you will
	//!< need the entry path without any underscores.

//! @name Methods
//! @{

	void delete_item(const char *path);
	//!< Deletes the menu item which was created for path.
	//!< @param path A path.

	void popup(unsigned int x, unsigned int y, unsigned int mouse_button, unsigned int time = GDK_CURRENT_TIME);
	//!< Pops up the menu constructed from the item factory at (x, y).
	//!< @param x The x position.
	//!< @param y The y position.
	//!< @param mouse_button The mouse button which was pressed to initiate the popup.
	//!< @param time The time at which the activation event occurred.
	//!<
	//!< <BR>The mouse_button parameter should be the mouse button pressed to initiate the menu
	//!< popup. If the menu popup was initiated by something other than a mouse button press, such
	//!< as a mouse button release or a keypress, mouse_button should be 0. The time parameter should
	//!< be the time stamp of the event that initiated the popup. If such an event is not available,
	//!< use GDK_CURRENT_TIME instead. The operation of the mouse_button and the time parameters is
	//!< the same as the button and activation_time parameters for Gtk::Menu::popup().

	void popup(unsigned int x, unsigned int y, unsigned int mouse_button, const DestroySlot *destroy, unsigned int time = GDK_CURRENT_TIME);
	//!< Pops up the menu constructed from the item factory at (x, y).
	//!< @param x The x position.
	//!< @param y The y position.
	//!< @param mouse_button The mouse button which was pressed to initiate the popup.
	//!< @param destroy A callback slot to be called when the menu is unposted.
	//!< @param time The time at which the activation event occurred.
	//!<
	//!< <BR>The mouse_button parameter should be the mouse button pressed to initiate the menu
	//!< popup. If the menu popup was initiated by something other than a mouse button press, such
	//!< as a mouse button release or a keypress, mouse_button should be 0. The time parameter should
	//!< be the time stamp of the event that initiated the popup. If such an event is not available,
	//!< use GDK_CURRENT_TIME instead. The operation of the mouse_button and the time parameters is
	//!< the same as the button and activation_time parameters for Gtk::Menu::popup().

//! @}
	
	static ItemFactory* from_widget(const Widget& widget);
	//!< Obtains the item factory from which a widget was created.
	//!< @param widget A widget.
	//!< @return The item factory from which widget was created, or null.
	
//! @name Templates
//! @{ 

	template<typename T>
	void create_item(T& owner, ItemFactoryEntry<T>& entry)
	{
		gtk_item_factory_create_item(gtk_item_factory(), reinterpret_cast<GtkItemFactoryEntry*>(&entry), 0, 1);
		if (entry.pmf)
			get_item(path_from_entry(entry.path).c_str())->sig_activate().connect(slot(&owner, entry.pmf));
	}
	//!< Creates a menu item for entry.
	//!< @param owner The owner of the item factory.
	//!< @param entry The ItemFactoryEntry to create an item for.

	template<typename T>
	void create_items(T& owner, std::vector<ItemFactoryEntry<T>*>& entries)
	{
		g_return_if_fail(!entries.empty());
		for (int i = 0; i < entries.size(); i++)
			create_item(owner, *entries[i]);
	}
	//!< Creates the menu items from entries.
	//!< @param owner The owner of the item factory.
	//!< @param entries A vector of ItemFactoryEntry.
	
	template<typename T>
	void create_items(T& owner)
	{
		int i = 0;
		while (owner.item_factory_map[i].path)
		{
			create_item(owner, owner.item_factory_map[i]);
			i++;
		}
	}
	//!< Creates the menu items from the entries in owner's item factory map.
	//!< @param owner The owner of the item factory.

	template<typename T>
	void delete_entry(ItemFactoryEntry<T>& entry)
	{
		gtk_item_factory_delete_entry(gtk_item_factory(), reinterpret_cast<GtkItemFactoryEntry*>(&entry));
	}
	//!< Deletes the menu item which was created from entry.
	//!< @param entry A ItemFactoryEntry.
	
	template<typename T>
	void delete_entries(std::vector<ItemFactoryEntry<T>*>& entries)
	{
		g_return_if_fail(!entries.empty());
		for (int i = 0; i < entries.size(); i++)
			delete_entry(*entries[i]);
	}
	//!< Deletes the menu items which were created from entries.
	//!< @param entries An vector of ItemFactoryEntry.

	template<typename T>
	void delete_entries(T& owner)
	{
		int i = 0;
		while ((owner.item_factory_map[i]).path)
		{
			delete_entry(item_factory_map[i]);
			i++;
		}
	}
	//!< Deletes the menu items which were created from the entries in owner's item factory map.
	//!< @param owner The owner of the item factory.

//! @}
};

} // namespace Gtk

} // namespace Inti

//! Macro that declares the necessary class members needed to manage an item factory map.
//! @param klass The name of the owner class for the item factory map.

#define DECLARE_ITEM_FACTORY_MAP(klass)\
private:\
	friend class Gtk::ItemFactory;\
	static Inti::Gtk::ItemFactoryEntry< klass > item_factory_map[];\
	typedef Inti::Gtk::ItemFactoryEntry< klass >::PMF MyPMF;\
	typedef klass MyClass;\
public:

//! Macro that begins an item factory map.
//! @param klass The name of the owner class of the item factory map.

#define BEGIN_ITEM_FACTORY_MAP(klass)\
	Inti::Gtk::ItemFactoryEntry< klass > klass::item_factory_map[] = {

//! Macro that ends an item factory map.

#define END_ITEM_FACTORY_MAP\
	{ 0, 0, 0, 0, 0, 0, 0 }};

//! Macro that creates a title item.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param accelerator A string indicating the keyboard shortcut for the menu item, or null.
//! @param function The name of the class method to call, without any scope prefix.

#define IFM_TITLE(path, accelerator, function)\
	{ path, accelerator, 0, 0, "<Title>", 0, (MyPMF)&MyClass::function }

//! Macro that creates a simple item.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param accelerator A string indicating the keyboard shortcut for the menu item, or null.
//! @param function The name of the class method to call, without any scope prefix.

#define IFM_ITEM(path, accelerator, function)\
	{ path, accelerator, 0, 0, "<Item>", 0, (MyPMF)&MyClass::function }

//! Macro that creates an image item.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param accelerator A string indicating the keyboard shortcut for the menu item, or null.
//! @param function The name of the class method to call, without any scope prefix.
//! @param pixbuf_stream A pointer to an inlined pixbuf stream.

#define IFM_IMAGE_ITEM(path, accelerator, function, pixbuf_stream)\
	{ path, accelerator, 0, 0, "<ImageItem>", pixbuf_stream, (MyPMF)&MyClass::function }

//! Macro that creates a stock image item.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param accelerator A string indicating the keyboard shortcut for the menu item, or null.
//! @param function The name of the class method to call, without any scope prefix.
//! @param stock_id The stock item, such as GTK_STOCK_NEW, GTK_STOCK_SAVE etc.

#define IFM_STOCK_ITEM(path, accelerator, function, stock_id)\
	{ path, accelerator, 0, 0, "<StockItem>", (gconstpointer)stock_id, (MyPMF)&MyClass::function }

//! Macro that creates a check item.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param accelerator A string indicating the keyboard shortcut for the menu item, or null.
//! @param function The name of the class method to call, without any scope prefix.

#define IFM_CHECK_ITEM(path, accelerator, function)\
	{ path, accelerator, 0, 0, "<CheckItem>", 0, (MyPMF)&MyClass::function }

//! Macro that creates a toggle item.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param accelerator A string indicating the keyboard shortcut for the menu item, or null.
//! @param function The name of the class method to call, without any scope prefix.

#define IFM_TOGGLE_ITEM(path, accelerator, function)\
	{ path, accelerator, 0, 0, "<CheckItem>", 0, (MyPMF)&MyClass::function }

//! Macro that creates a radio item.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param accelerator A string indicating the keyboard shortcut for the menu item, or null.
//! @param function The name of the class method to call, without any scope prefix.
//!
//! <BR>To declare a group of radio menu items in an item factory map the first item 
//! in the group is defined with the IFM_RADIO_ITEM macro. Other items in the group are
//! then defined with the IFM_RADIO_LINK_ITEM macro, specifying the first item's path
//! as the link_path.

#define IFM_RADIO_ITEM(path, accelerator, function)\
	{ path, accelerator, 0, 0, "<RadioItem>", 0, (MyPMF)&MyClass::function }

//! Macro that creates a radio item to link against.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param function The name of the class method to call, without any scope prefix.
//! @param link_path The path string of the radio item to link against; this is usually the first radio
//!                  item in the group, the one defined in the item factory map with IFM_RADIO_ITEM.

#define IFM_RADIO_ITEM_LINK(path, function, link_path)\
	{ path, 0, 0, 0, link_path, 0, (MyPMF)&MyClass::function }

//! Macro that creates a tearoff separator.
//! @param path A string indicating the item's level in the menu hierarchy.
//! @param function The name of the class method to call, without any scope prefix.

#define IFM_TEAROFF_ITEM(path, function)\
	{ path, 0, 0, 0, "<Tearoff>", 0, (MyPMF)&MyClass::function }

//! Macro that creates a separator.
//! @param path A string indicating the item's level in the menu hierarchy.

#define IFM_SEPARATOR(path)\
	{ path, 0, 0, 0, "<Separator>", 0, 0 }

//! Macro that creates an item to hold sub items.
//! @param path A string indicating the item's level in the menu hierarchy.

#define IFM_BRANCH(path)\
	{ path, 0, 0, 0, "<Branch>", 0, 0 }

//! Macro that creates a right justified item to hold sub items.
//! @param path A string indicating the item's level in the menu hierarchy.

#define IFM_LAST_BRANCH(path)\
	{ path, 0, 0, 0, "<LastBranch>", 0, 0 }

#endif // INTI_GTK_ITEM_FACTORY_H

