/* Copyright (C) 2007 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 <giomm/resource.h> //For ResourceError, thrown by create_from_resource().

_DEFS(gtkmm,gtk)
_PINCLUDE(glibmm/private/object_p.h)


namespace Gtk
{
class Application;

/** Exception class for Gdk::Builder errors.
 */
_WRAP_GERROR(BuilderError,GtkBuilderError,GTK_BUILDER_ERROR)

/** Build an interface from an XML UI definition description.
 *
 * This object represents an `instantiation' of an UI definition description.
 * When one of these objects is created, the XML file is read, and the user interface
 * is created. The Gtk::Builder object then provides an interface for accessing the
 * widgets in the user interface by the names assigned to them inside the UI description.
 *
 * A Gtk::Builder holds a reference to all objects that it has constructed
 * and drops these references when it is deleted. This deletion can
 * cause the destruction of non-widget objects or widgets which are not
 * contained in a toplevel window. For toplevel windows constructed by a
 * builder, it is the responsibility of the user to get them with get_widget()
 * or get_widget_derived() and delete them to get rid of them and all
 * the widgets they contain.
 *
 * The methods get_widget() and get_widget_derived()
 * can be used to access the widgets in the interface by the names assigned
 * to them inside the UI description. Toplevel windows returned by these
 * methods will stay around until the user explicitly deletes them.
 * Other widgets will either be part of a
 * larger hierarchy constructed by the builder (in which case you should
 * not have to worry about their lifecycle), or without a parent, in which
 * case they have to be added to some container before the builder is deleted
 * to make use of them.
 * Non-widget objects need to be fetched with get_object() or get_objects()
 * to keep them beyond the lifespan of the builder.
 *
 * @newin{2,12}
 */
class Builder : public Glib::Object
{
  _CLASS_GOBJECT(Builder, GtkBuilder, GTK_BUILDER, Glib::Object, GObject)

protected:
 _CTOR_DEFAULT
 _IGNORE(gtk_builder_new)

public:
  /** Creates a new builder object.
   */
  _WRAP_CREATE()

  /** Parses a file containing a GtkBuilder UI definition.
   *
   * @param filename the name of the file to parse.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Glib::FileError
   *
   * @newin{2,12}
   */
  static Glib::RefPtr<Builder> create_from_file(const std::string& filename);

  /** Parses a file containing a GtkBuilder UI definition, building only the requested object.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param filename the name of the file to parse.
   * @param object_id The object to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Glib::FileError
   *
   * @newin{2,14}
   */
  static Glib::RefPtr<Builder> create_from_file(const std::string& filename, const Glib::ustring& object_id);

  //This is just to avoid the ambiguous call when using a string literal,
  //caused by the overload that takes a std::vector<Glib::ustring>.
  /** Parses a file containing a GtkBuilder UI definition, building only the requested object.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param filename the name of the file to parse.
   * @param object_id The object to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Glib::FileError
   *
   * @newin{2,16}
   */
  static Glib::RefPtr<Builder> create_from_file(const std::string& filename, const char* object_id);

  /** Parses a file containing a GtkBuilder UI definition, building only the requested objects.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param filename the name of the file to parse.
   * @param object_ids The objects to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Glib::FileError
   *
   * @newin{2,14}
   */
  static Glib::RefPtr<Builder> create_from_file(const std::string& filename, const std::vector<Glib::ustring>& object_ids);


  /** Parses a resource file containing a GtkBuilder UI definition.
   *
   * @param resource_path The path of the resource file to parse.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Gio::ResourceError
   *
   * @newin{3,6}
   */
  static Glib::RefPtr<Builder> create_from_resource(const std::string& resource_path);

  /** Parses a resource file containing a GtkBuilder UI definition, building only the requested object.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param resource_path The path of the resource file to parse.
   * @param object_id The object to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Gio::ResourceError
   *
   * @newin{3,6}
   */
  static Glib::RefPtr<Builder> create_from_resource(const std::string& resource_path, const Glib::ustring& object_id);

  //This is just to avoid the ambiguous call when using a string literal,
  //caused by the overload that takes a std::vector<Glib::ustring>.
  /** Parses a resource file containing a GtkBuilder UI definition, building only the requested object.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param resource_path The path of the resource file to parse.
   * @param object_id The object to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Gio::ResourceError
   *
   * @newin{3,6}
   */
  static Glib::RefPtr<Builder> create_from_resource(const std::string& resource_path, const char* object_id);

  /** Parses a resource file containing a GtkBuilder UI definition, building only the requested objects.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param resource_path The path of the resource file to parse.
   * @param object_ids The objects to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Gio::ResourceError
   *
   * @newin{3,6}
   */
  static Glib::RefPtr<Builder> create_from_resource(const std::string& resource_path, const std::vector<Glib::ustring>& object_ids);


  /** Parses a string containing a GtkBuilder UI definition.
   *
   * @param buffer: the string to parse
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,12}
   */
  static Glib::RefPtr<Builder> create_from_string(const Glib::ustring& buffer);

  //This is just to avoid the ambiguous call when using a string literal,
  //caused by the overload that takes a std::vector<Glib::ustring>.
  /** Parses a string containing a GtkBuilder UI definition building only the requested object.
   *
   * @param buffer The string to parse.
   * @param object_id The object to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,16}
   */
  static Glib::RefPtr<Builder> create_from_string(const Glib::ustring& buffer, const char* object_id);

  /** Parses a string containing a GtkBuilder UI definition building only the requested object.
   *
   * @param buffer The string to parse.
   * @param object_id The object to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,14}
   */
  static Glib::RefPtr<Builder> create_from_string(const Glib::ustring& buffer, const Glib::ustring& object_id);

  /** Parses a string containing a GtkBuilder UI definition building only the requested objects.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param buffer the string to parse
   * @param object_ids The objects to build.
   * @result A new Builder object, or a null pointer if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,14}
   */
  static Glib::RefPtr<Builder> create_from_string(const Glib::ustring& buffer, const std::vector<Glib::ustring>& object_ids);
  _IGNORE(gtk_builder_add_objects_from_file)

  /** Parses a file containing a GtkBuilder UI definition,
   * and merges it with the current contents of the builder.
   *
   * @param filename The file to parse.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Glib::MarkupError
   *
   * @newin{2,14}
   */
  _WRAP_METHOD(bool add_from_file(const std::string& filename), gtk_builder_add_from_file, errthrow)

  //This is just to avoid the ambiguous call when using a string literal,
  //caused by the overload that takes a std::vector<Glib::ustring>.
  /** Parses a file containing a GtkBuilder UI definition,
   * building only the requested object,
   * and merges it with the current contents of the builder.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param filename The file to parse.
   * @param object_id The object to build.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Glib::MarkupError
   *
   * @newin{2,16}
   */
  bool add_from_file(const std::string& filename, const char* object_id);

  /** Parses a file containing a GtkBuilder UI definition,
   * building only the requested object,
   * and merges it with the current contents of the builder.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param filename The file to parse.
   * @param object_id The object to build.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Glib::MarkupError
   *
   * @newin{2,14}
   */
  bool add_from_file(const std::string& filename, const Glib::ustring& object_id);

#m4 _CONVERSION(`const std::vector<Glib::ustring>&',`gchar**',`const_cast<char**>(Glib::ArrayHandler<Glib::ustring>::vector_to_array($3).data())')
  _WRAP_METHOD(bool add_from_file(const std::string& filename, const std::vector<Glib::ustring>& object_ids),  gtk_builder_add_objects_from_file, errthrow)


  /** Parses a resource file containing a GtkBuilder UI definition,
   * and merges it with the current contents of the builder.
   *
   * @param resource_path The path of the resource file to parse.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Gio::ResourceError
   *
   * @newin{3,6}
   */
  _WRAP_METHOD(bool add_from_resource(const std::string& resource_path), gtk_builder_add_from_resource, errthrow)

  //This is just to avoid the ambiguous call when using a string literal,
  //caused by the overload that takes a std::vector<Glib::ustring>.
  /** Parses a resource file containing a GtkBuilder UI definition,
   * building only the requested object,
   * and merges it with the current contents of the builder.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param resource_path The path of the resource file to parse.
   * @param object_id The object to build.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Gio::ResourceError
   *
   * @newin{3,6}
   */
  bool add_from_resource(const std::string& resource_path, const char* object_id);

  /** Parses a resource file containing a GtkBuilder UI definition,
   * building only the requested object,
   * and merges it with the current contents of the builder.
   *
   * If you are adding an object that depends on an object that is not
   * its child (for instance a GtkTreeView that depends on its
   * GtkTreeModel), you have to explicitely list all of them.
   *
   * @param resource_path The path of the resource file to parse.
   * @param object_id The object to build.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError, Gio::ResourceError
   *
   * @newin{3,6}
   */
  bool add_from_resource(const std::string& resource_path, const Glib::ustring& object_id);

#m4 _CONVERSION(`const std::vector<Glib::ustring>&',`gchar**',`const_cast<char**>(Glib::ArrayHandler<Glib::ustring>::vector_to_array($3).data())')
  _WRAP_METHOD(bool add_from_resource(const std::string& resource_path, const std::vector<Glib::ustring>& object_ids),  gtk_builder_add_objects_from_resource, errthrow)


  //This is just to avoid the ambiguous call when using a string literal,
  //caused by the overload that takes a std::vector<Glib::ustring>.
  /** Parses a string containing a GtkBuilder UI definition
   * and merges it with the current contents of the builder.
   *
   * @param buffer The string to parse.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,12}
   */
  bool add_from_string(const Glib::ustring& buffer);

  /** Parses a string containing a GtkBuilder UI definition,
   * building only the requested object,
   * and merges it with the current contents of the builder.
   *
   * @param buffer The string to parse.
   * @param object_id The object to build.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,16}
   */
  bool add_from_string(const Glib::ustring& buffer, const char* object_id);

  /** Parses a string containing a GtkBuilder UI definition,
   * building only the requested object,
   * and merges it with the current contents of the builder.
   *
   * @param buffer The string to parse.
   * @param object_id The object to build.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,14}
   */
  bool add_from_string(const Glib::ustring& buffer, const Glib::ustring& object_id);

  /** Parses a string containing a GtkBuilder UI definition,
   * building only the requested objects,
   * and merges it with the current contents of the builder.
   *
   * @param buffer The string to parse.
   * @param object_ids The objects to build.
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,14}
   */
  bool add_from_string(const Glib::ustring& buffer, const std::vector<Glib::ustring>& object_ids);
  _IGNORE(gtk_builder_add_objects_from_string)

  /** Parses a string containing a GtkBuilder UI definition
   * and merges it with the current contents of the builder.
   *
   * @param buffer The string to parse.
   * @param length The length of @a buffer (may be -1 if @a buffer is nul-terminated).
   * @result true on success or false if an error occurred.
   * @throws BuilderError, Glib::MarkupError
   *
   * @newin{2,12}
   */
  _WRAP_METHOD(bool add_from_string(const char* buffer, gsize length), gtk_builder_add_from_string, errthrow)

  _WRAP_METHOD(void expose_object(const Glib::ustring& name, const Glib::RefPtr<Glib::Object>& object), gtk_builder_expose_object)
#m4 _CONVERSION(`Widget&',`GObject*',`G_OBJECT(($3).gobj())')

  /** Add @a widget to the builder object pool so it can be referenced just like any
   * other object built by builder.
   * 
   * @newin{3,12}
   *
   * @param name The name of the widget exposed to the builder.
   * @param widget The widget to expose.
   */
  _WRAP_METHOD(void expose_widget(const Glib::ustring& name, Widget& widget{object}), gtk_builder_expose_object)

  //TODO: Custom-implement this and prevent it from being used with GTK_WIDGET-derived types?
  // A Builder can contain objects that don't derive from Buildable,
  // for instance if objects have been added with expose_object().
  /** Gets the object named @a name.
   * 
   * @newin{2,12}
   *
   * @param name Name of object to get.
   * @return The object named @a name or <tt>0</tt> if it could not be found in the object tree.
   */
  _WRAP_METHOD(Glib::RefPtr<Glib::Object> get_object(const Glib::ustring& name), gtk_builder_get_object, refreturn)
  /** Gets the object named @a name.
   * 
   * @newin{3,8}
   *
   * @param name Name of object to get.
   * @return The object named @a name or <tt>0</tt> if it could not be found in the object tree.
   */
  _WRAP_METHOD(Glib::RefPtr<const Glib::Object> get_object(const Glib::ustring& name) const, gtk_builder_get_object, refreturn, constversion)

  /** Get a widget from the Builder file.
   * For instance:
   * @code
   * Gtk::Grid* pGrid = 0;
   * refXml->get_widget("mygrid", pGrid);
   * @endcode
   * This method prints a warning message to the console if the widget
   * doesn't exist or has the wrong type, so you don't need to check that
   * manually.
   *
   * Note that you are responsible for deleting top-level widgets (windows and
   * dialogs) instantiated by the Builder object. Other widgets are instantiated as
   * managed so they will be deleted automatically if you add them to a
   * container widget.
   *
   * @param name The name of the widget.
   * @retval widget A pointer to the widget, or <tt>0</tt> on failure.
   */
  template <class T_Widget> inline
  void get_widget(const Glib::ustring& name, T_Widget*& widget)
  {
    // Initialize output parameter:
    widget = 0;

    // The dynamic_cast<> should never fail if get_widget_checked() succeeded.
    widget = dynamic_cast<T_Widget*>(this->get_widget_checked(name, T_Widget::get_base_type()));

    if(!widget)
      g_critical("Gtk::Builder::get_widget(): dynamic_cast<> failed.");
  }

  /** See the non-const version.
   * @newin{3,8}
   */
  template <class T_Widget> inline
  void get_widget(const Glib::ustring& name, const T_Widget*& widget) const
  {
    const_cast<Builder*>(this)->get_widget(name, widget);
  }

  /** This provides a pointer to a widget whose details are specified in the GtkBuilder file, but which is implemented
   * by your own derived class. Your class must have a constructor like so:
   * @code
   * DerivedDialog::DerivedDialog(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& refBuilder)
   * : Gtk::Dialog(cobject) //Calls the base class constructor
   * @endcode
   *
   * For instance:
   * @code
   * Gtk::DerivedBox* pBox = 0;
   * refXml->get_widget_derived("mybox", pBox);
   * @endcode
   *
   * @param name The name of the widget.
   * @retval widget A pointer to the widget, or <tt>0</tt> on failure.
   */
  template <class T_Widget> inline
  void get_widget_derived(const Glib::ustring& name, T_Widget*& widget)
  {
     // Initialize output parameter:
     widget = 0;

     // Get the widget from the GtkBuilder file.
     typedef typename T_Widget::BaseObjectType cwidget_type;
     cwidget_type* pCWidget = (cwidget_type*)get_cwidget(name);

     //The error was already reported by get_cwidget().
     if(!pCWidget)
       return;

     //Check whether there is already a C++ wrapper instance associated with this C instance:
     Glib::ObjectBase* pObjectBase = ObjectBase::_get_current_wrapper((GObject*)pCWidget);

     //If there is already a C++ instance, then return it again:
     if(pObjectBase)
     {
       widget = dynamic_cast<T_Widget*>( Glib::wrap((GtkWidget*)pCWidget) );
       //Newer, more spec-complaint, versions of g++ cannot resolve a specific wrap() function in a template.

       //The dynamic cast checks that it is of the correct type.
       //Somebody might be trying to call get_widget_derived() after already calling get_widget(),
       //or after already calling get_widget_derived() with a different derived C++ type.
       if(!widget)
        g_critical("Gtk::Builder::get_widget_derived(): dynamic_cast<> failed. An existing C++ instance, of a different type, seems to exist.");
     }
     else
     {
       //Create a new C++ instance to wrap the existing C instance:

       //Set the output variable. We needed to do this because we can not template the return type.
       Glib::RefPtr<Gtk::Builder> refThis(this);
       refThis->reference(); //take a copy.
       widget = new T_Widget(pCWidget, refThis);
       // Don't add reference to widget. That's done only in methods that return
       // a Glib::RefPtr<Something>.
     }
  }

  /** See the non-const version.
   * @newin{3,8}
   */
  template <class T_Widget> inline
  void get_widget_derived(const Glib::ustring& name, const T_Widget*& widget) const
  {
    const_cast<Builder*>(this)->get_widget_derived(name, widget);
  }

#m4 _CONVERSION(`GSList*',`std::vector<Glib::RefPtr<Glib::Object> >',`Glib::SListHandler<Glib::RefPtr<Glib::Object> >::slist_to_vector($3, Glib::OWNERSHIP_SHALLOW)')
  _WRAP_METHOD(std::vector<Glib::RefPtr<Glib::Object> > get_objects(), gtk_builder_get_objects)

#m4 _CONVERSION(`GSList*',`std::vector<Glib::RefPtr<const Glib::Object> >',`Glib::SListHandler<Glib::RefPtr<const Glib::Object> >::slist_to_vector($3, Glib::OWNERSHIP_SHALLOW)')
  _WRAP_METHOD(std::vector<Glib::RefPtr<const Glib::Object> > get_objects() const, gtk_builder_get_objects)

  //_WRAP_METHOD(void connect_signals(gpointer       user_data), gtk_builder_connect_signals)
  //_WRAP_METHOD(void connect_signals_full(GtkBuilderConnectFunc func, gpointer       user_data), gtk_builder_connect_signals_full)
  _WRAP_METHOD(void set_translation_domain(const Glib::ustring& domain), gtk_builder_set_translation_domain)
  _WRAP_METHOD(Glib::ustring get_translation_domain() const, gtk_builder_get_translation_domain)

  _WRAP_METHOD(void set_application(const Glib::RefPtr<Application>& application), gtk_builder_set_application)
  _WRAP_METHOD(Glib::RefPtr<Application> get_application(), gtk_builder_get_application, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Application> get_application() const, gtk_builder_get_application, refreturn, constversion)

  //We ignore gtk_builder_get_type_from_name() because it only seems useful when implementing GtkBuildable for widgets.
  _IGNORE(gtk_builder_get_type_from_name)

  _WRAP_PROPERTY("translation-domain", Glib::ustring)

protected:
  Gtk::Widget* get_widget_checked(const Glib::ustring& name, GType type);
  GtkWidget* get_cwidget(const Glib::ustring& name);

private:
  void set_no_gtkmm_derived_types(bool status);
  friend class Buildable_Class;

#m4begin
dnl Custom-coded vfunc:
dnl
  _PUSH(SECTION_PCC_CLASS_INIT_VFUNCS)
  klass->get_type_from_name = &get_type_from_name_vfunc_callback;
  _POP()
#m4end
};

} // namespace Gtk

