/* $Id: uimanager.hg,v 1.17 2006/05/11 11:40:24 murrayc Exp $ */

/* Copyright (C) 2003 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <vector>

#include <gtkmm/widget.h>
#include <gtkmm/action.h>
#include <gtkmm/actiongroup.h>
 
_DEFS(gtkmm,gtk)
_PINCLUDE(glibmm/private/object_p.h)

#m4 _PUSH(SECTION_CC_PRE_INCLUDES)
#undef GTK_DISABLE_DEPRECATED
#define GDK_DISABLE_DEPRECATION_WARNINGS 1
#m4 _POP()

namespace Gtk
{
	
_WRAP_ENUM(UIManagerItemType, GtkUIManagerItemType)

/** Constructing menus and toolbars from an XML description.
 *
 * A Gtk::UIManager constructs a user interface (menus and toolbars) from one or more UI definitions,
 * which reference actions from one or more action groups.
 *
 * \par UI Definitions
 *
 * The UI definitions are specified in an XML format which can be roughly described by the following DTD.
 * \code
 * <!ELEMENT ui          (menubar|toolbar|popup|accelerator)* >
 * <!ELEMENT menubar     (menuitem|separator|placeholder|menu)* >
 * <!ELEMENT menu        (menuitem|separator|placeholder|menu)* >
 * <!ELEMENT popup       (menuitem|separator|placeholder|menu)* >
 * <!ELEMENT toolbar     (toolitem|separator|placeholder)* >
 * <!ELEMENT placeholder (menuitem|toolitem|separator|placeholder|menu)* >
 * <!ELEMENT menuitem     EMPTY >
 * <!ELEMENT toolitem     (menu?) >
 * <!ELEMENT separator    EMPTY >
 * <!ELEMENT accelerator  EMPTY >
 * <!ATTLIST menubar      name                  #IMPLIED 
 *                      action                #IMPLIED >
 * <!ATTLIST toolbar      name                  #IMPLIED 
 *                      action                #IMPLIED >
 * <!ATTLIST popup        name                  #IMPLIED 
 *                      action                #IMPLIED >
 * <!ATTLIST placeholder  name                  #IMPLIED
 *                       action                #IMPLIED >
 * <!ATTLIST separator    name                  #IMPLIED
 *                       action                #IMPLIED
 *                       expand   (true|false) #IMPLIED >
 * <!ATTLIST menu         name                  #IMPLIED
 *                       action                #REQUIRED
 *                       position (top|bot)    #IMPLIED >
 * <!ATTLIST menuitem     name                  #IMPLIED
 *                      action                #REQUIRED
 *                      position (top|bot)    #IMPLIED >
 * <!ATTLIST toolitem     name                  #IMPLIED
 *                      action                #REQUIRED
 *                      position (top|bot)    #IMPLIED >
 * <!ATTLIST accelerator  name                  #IMPLIED
 *                       action                #REQUIRED >
 * \endcode
 *
 * There are some additional restrictions beyond those specified in the DTD, e.g. 
 * every toolitem must have a toolbar in its anchestry and every menuitem must have a +
 * menubar or popup in its anchestry. Since a GMarkup parser is used to parse the UI description, 
 * it must not only be valid XML, but valid GMarkup.
 *
 * If a name is not specified, it defaults to the action. If an action is not specified either,
 * the element name is used. The name and action attributes must not contain '/' characters after 
 * parsing (since that would mess up path lookup) and must be usable as XML attributes when 
 * enclosed in doublequotes, thus they must not '"' characters or references to the &quot; entity.
 *
 * \par Example: UI Definition
 * \code
 * <ui>
 * <menubar>
 *   <menu name="FileMenu" action="FileMenuAction">
 *     <menuitem name="New" action="New2Action" />
 *     <placeholder name="FileMenuAdditions" />
 *   </menu>
 *   <menu name="JustifyMenu" action="JustifyMenuAction">
 *     <menuitem name="Left" action="justify-left"/>
 *     <menuitem name="Centre" action="justify-center"/>
 *     <menuitem name="Right" action="justify-right"/>
 *     <menuitem name="Fill" action="justify-fill"/>
 *   </menu>
 * </menubar>
 * <toolbar action="toolbar1">
 *   <placeholder name="JustifyToolItems">
 *     <separator/>
 *     <toolitem name="Left" action="justify-left"/>
 *     <toolitem name="Centre" action="justify-center"/>
 *     <toolitem name="Right" action="justify-right"/>
 *     <toolitem name="Fill" action="justify-fill"/>
 *     <separator/>
 *   </placeholder>
 * </toolbar>
 * </ui>
 * \endcode
 * 
 * The constructed widget hierarchy is very similar to the element tree of the XML,
 * with the exception that placeholders are merged into their parents. The correspondence
 * of XML elements to widgets should be almost obvious:
 * - menubar 	a Gtk::MenuBar
 * - toolbar 	a Gtk::Toolbar
 * - popup  a toplevel Gtk::Menu
 * - menu  a Gtk::Menu attached to a menuitem
 * - menuitem  a Gtk::MenuItem subclass, the exact type depends on the action
 * - toolitem  a Gtk::ToolItem subclass, the exact type depends on the action. 
 * Note that toolitem elements may contain a menu element, but only if their 
 * associated action specifies a Gtk::MenuToolButton as proxy.
 * - separator  a Gtk::SeparatorMenuItem or Gtk::SeparatorToolItem
 * - accelerator 	a keyboard accelerator
 * 
 * The "position" attribute determines where a constructed widget is positioned wrt. 
 * to its siblings in the partially constructed tree. If it is "top", the widget is 
 * prepended, otherwise it is appended. 
 *
 * \par UI Merging
 *
 * The most remarkable feature of Gtk::UIManager is that it can overlay a set of menuitems 
 * and toolitems over another one, and demerge them later.
 *
 * Merging is done based on the names of the XML elements. Each element is identified by
 * a path which consists of the names of its anchestors, separated by slashes. For example, 
 * the menuitem named "Left" in the example above has the path /ui/menubar/JustifyMenu/Left 
 * and the toolitem with the same name has path /ui/toolbar1/JustifyToolItems/Left.
 *
 * \par Accelerators
 *
 * Every action has an accelerator path. Accelerators are installed together with menuitem 
 * proxies, but they can also be explicitly added with <accelerator> elements in the 
 * UI definition. This makes it possible to have accelerators for actions even 
 * if they have no visible proxies.
 * 
 * \par Smart Separators
 *
 * The separators created by Gtk::UIManager are "smart", i.e. they do not show up in 
 * the UI unless they end up between two visible menu or tool items. Separators which are 
 * located at the very beginning or end of the menu or toolbar containing them, or multiple 
 * separators next to each other, are hidden. This is a useful feature, since the merging 
 * of UI elements from multiple sources can make it hard or impossible to determine in 
 * advance whether a separator will end up in such an unfortunate position.
 *
 * For separators in toolbars, you can set expand="true" to turn them from a small,
 * visible separator to an expanding, invisible one. Toolitems following an expanding 
 * separator are effectively right-aligned.
 *
 * \par Empty Menus
 *
 * Submenus pose similar problems to separators inconnection with merging. It is impossible 
 * to know in advance whether they will end up empty after merging. Gtk::UIManager offers 
 * two ways to treat empty submenus:
 *
 * - make them disappear by hiding the menu item they're attached to
 * - add an insensitive "Empty" item
 *
 * The behaviour is chosen based on the "hide_if_empty" property of the action to which the submenu is associated.
 *
 * @newin{2,4}
 */
class UIManager : public Glib::Object
{
  _CLASS_GOBJECT(UIManager, GtkUIManager, GTK_UI_MANAGER, Glib::Object, GObject)
protected:
  _CTOR_DEFAULT()

public:
  _WRAP_CREATE()

  _WRAP_METHOD(void set_add_tearoffs(bool add_tearoffs = true), gtk_ui_manager_set_add_tearoffs, deprecated "Tearoff menus are deprecated and should not be used in newly written code.")
  _WRAP_METHOD(bool get_add_tearoffs() const, gtk_ui_manager_get_add_tearoffs, deprecated "Tearoff menus are deprecated and should not be used in newly written code.")
  _WRAP_METHOD(void insert_action_group(const Glib::RefPtr<ActionGroup>& action_group, int pos = 0), gtk_ui_manager_insert_action_group)
  _WRAP_METHOD(void remove_action_group(const Glib::RefPtr<ActionGroup>& action_group), gtk_ui_manager_remove_action_group)

#m4 dnl // Override the conversion in convert_gtk.m4, because the default behaviour is wrong for this method:
#m4 _CONVERSION(`GList*', `std::vector< Glib::RefPtr<ActionGroup> >', `Glib::ListHandler<Glib::RefPtr<ActionGroup> >::list_to_vector($3, Glib::OWNERSHIP_NONE)')
  _WRAP_METHOD(std::vector< Glib::RefPtr<ActionGroup> > get_action_groups(), gtk_ui_manager_get_action_groups)
#m4 _CONVERSION(`GList*', `std::vector< Glib::RefPtr<const ActionGroup> >', `Glib::ListHandler<Glib::RefPtr<const ActionGroup> >::list_to_vector($3, Glib::OWNERSHIP_NONE)')
  _WRAP_METHOD(std::vector< Glib::RefPtr<const ActionGroup> > get_action_groups() const, gtk_ui_manager_get_action_groups)
  
  _WRAP_METHOD(Glib::RefPtr<AccelGroup> get_accel_group(), gtk_ui_manager_get_accel_group, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const AccelGroup> get_accel_group() const, gtk_ui_manager_get_accel_group, refreturn, constversion)
  _WRAP_METHOD(Widget* get_widget(const Glib::ustring& path), gtk_ui_manager_get_widget)
  _WRAP_METHOD(const Widget* get_widget(const Glib::ustring& path) const, gtk_ui_manager_get_widget, constversion)

#m4 _CONVERSION(`GSList*',`std::vector<Widget*>',`Glib::SListHandler<Widget*>::slist_to_vector($3, Glib::OWNERSHIP_SHALLOW)')
  _WRAP_METHOD(std::vector<Widget*> get_toplevels(UIManagerItemType types), gtk_ui_manager_get_toplevels)
#m4 _CONVERSION(`GSList*',`std::vector<const Widget*>',`Glib::SListHandler<const Widget*>::slist_to_vector($3, Glib::OWNERSHIP_SHALLOW)')
  _WRAP_METHOD(std::vector<const Widget*> get_toplevels(UIManagerItemType types) const, gtk_ui_manager_get_toplevels)
                 
  _WRAP_METHOD(Glib::RefPtr<Action> get_action(const Glib::ustring& path), gtk_ui_manager_get_action, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Action> get_action(const Glib::ustring& path) const, gtk_ui_manager_get_action, refreturn, constversion)

  typedef guint ui_merge_id;
    
  /** Parses a string containing a UI definition and
   * merges it with the current contents. An enclosing &lt;ui&gt;
   * element is added if it is missing.
   *
   * @param buffer the string to parse
   * @result  The merge id for the merged UI. The merge id can be used to unmerge the UI with remove_ui(). If an error occurred, the return value is 0.
   * @throws exception
   * @newin{2,4}
   */ 
  ui_merge_id add_ui_from_string(const Glib::ustring& buffer);
  _IGNORE(gtk_ui_manager_add_ui_from_string)
  _WRAP_METHOD(ui_merge_id add_ui_from_file(const Glib::ustring& filename), gtk_ui_manager_add_ui_from_file, errthrow)

//TODO: Is top=true a good default?

  _WRAP_METHOD(void add_ui(ui_merge_id merge_id, const Glib::ustring& path, const Glib::ustring& name, const Glib::ustring& action, UIManagerItemType type = Gtk::UI_MANAGER_AUTO, bool top = true), gtk_ui_manager_add_ui)

  /** Adds a separator UI element to the current contents.
   *
   * If @a type  is Gtk::UI_MANAGER_AUTO, GTK+ inserts a menuitem, toolitem or
   * separator if such an element can be inserted at the place determined by
   *  @a path . Otherwise @a type  must indicate an element that can be inserted at
   * the place determined by @a path.
   *
   * @see add_ui().
   *
   * @newin{2,4}
   * @param merge_id The merge id for the merged UI, see gtk_ui_manager_new_merge_id().
   * @param path A path.
   * @param name The name for the added UI element.
   * @param type The type of UI element to add.
   * @param top If <tt>true</tt>, the UI element is added before its siblings, otherwise it
   * is added after its siblings.
   */
  void add_ui_separator(ui_merge_id merge_id, const Glib::ustring& path, const Glib::ustring& name = "", UIManagerItemType type = Gtk::UI_MANAGER_AUTO, bool top = true);
  
  _WRAP_METHOD(void remove_ui(ui_merge_id merge_id), gtk_ui_manager_remove_ui)
  _WRAP_METHOD(Glib::ustring get_ui() const, gtk_ui_manager_get_ui)
  _WRAP_METHOD(void ensure_update(), gtk_ui_manager_ensure_update)
  _WRAP_METHOD(ui_merge_id new_merge_id(), gtk_ui_manager_new_merge_id)

  /** The add_widget signal is emitted for each generated menubar and toolbar.
   * It is not emitted for generated popup menus, which can be obtained by 
   * get_widget().
   * 
   * @param widget the added widget
   */
  _WRAP_SIGNAL(void add_widget(Widget* widget), add_widget)
  
  /** The "actions-changed" signal is emitted whenever the set of actions
   * changes.
   */
  _WRAP_SIGNAL(void actions_changed(), actions_changed)
  
  /** The connect_proxy signal is emitted after connecting a proxy to 
   * an action in the group. 
   *
   * This is intended for simple customizations for which a custom action
   * class would be too clumsy, e.g. showing tooltips for menuitems in the
   * statusbar.
   *
   * @param action the action
   * @param widget the proxy
   */
  _WRAP_SIGNAL(void connect_proxy(const Glib::RefPtr<Action>& action, Widget* widget), connect_proxy)
  
  /** The disconnect_proxy signal is emitted after disconnecting a proxy 
   * from an action in the group. 
   *
   * @param action the action
   * @param widget the proxy
   */
  _WRAP_SIGNAL(void disconnect_proxy(const Glib::RefPtr<Action>& action, Widget* widget), disconnect_proxy)
  
  /** The pre_activate signal is emitted just before the @a action
   * is activated.
   *
   * This is intended for applications to get notification
   * just before any action is activated.
   *
   * @param action the action
   */
  _WRAP_SIGNAL(void pre_activate(const Glib::RefPtr<Action>& action), pre_activate)
  
  /** The post_activate signal is emitted just after the @a action
   * is activated.
   *
   * This is intended for applications to get notification
   * just after any action is activated.
   *
   * @param action the action
   */
  _WRAP_SIGNAL(void post_activate(const Glib::RefPtr<Action>& action), post_activate)

  _WRAP_PROPERTY("add_tearoffs", bool)
  _WRAP_PROPERTY("ui", Glib::ustring)
             
};

} // namespace Gtk

