// -*- c++ -*-
/* $Id: widget.ccg,v 1.23 2006/07/19 16:58:50 murrayc Exp $ */

/* Copyright 1998-2002 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <gtkmm/adjustment.h>
#include <gtkmm/window.h>
#include <gtkmm/accelgroup.h>
#include <gtkmm/settings.h>
#include <gtkmm/style.h>
#include <gtkmm/container.h>
#include <gtkmm/selectiondata_private.h>
#include <gtkmm/action.h>
#include <gtkmm/tooltip.h>
#include <gtk/gtkdnd.h>
#include <gtk/gtkselection.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtkcontainer.h>
#include <gtk/gtkmenu.h>
#include <gtk/gtkmenuitem.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkaction.h> //For gtk_widget_get_action().


namespace //anonymous
{

//These signal callbacks are custom implemented, so that we can create a temporary SelectionData instance.
//To do this, we used the optional custom_c_callback paramater to _WRAP_SIGNAL() in the .hg file.
static void Widget_signal_drag_data_get_callback(GtkWidget* self, GdkDragContext* p0,GtkSelectionData* p1,guint p2,guint p3,void* data)
{
  using namespace Gtk;
  typedef sigc::slot< void,const Glib::RefPtr<Gdk::DragContext>&,SelectionData&,guint,guint > SlotType;

  // Do not try to call a signal on a disassociated wrapper.
  if(Glib::ObjectBase::_get_current_wrapper((GObject*) self))
  {
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    try
    {
    #endif //GLIBMM_EXCEPTIONS_ENABLED
      if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
      {
        SelectionData_WithoutOwnership temp_instance(p1);
        (*static_cast<SlotType*>(slot))( Glib::wrap(p0, true), temp_instance, p2, p3 );
      }
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    }
    catch(...)
    {
      Glib::exception_handlers_invoke();
    }
    #endif //GLIBMM_EXCEPTIONS_ENABLED
  }
}

static void Widget_signal_selection_get_callback(GtkWidget* self, GtkSelectionData* p0,guint p1,guint p2,void* data)
{
  using namespace Gtk;
  typedef sigc::slot< void, SelectionData&, guint, guint > SlotType;

  // Do not try to call a signal on a disassociated wrapper.
  if(Glib::ObjectBase::_get_current_wrapper((GObject*) self))
  {
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    try
    {
    #endif //GLIBMM_EXCEPTIONS_ENABLED
      if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
      {
        SelectionData_WithoutOwnership temp_instance(p0);
        (*static_cast<SlotType*>(slot))( temp_instance, p1, p2 );
      }
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    }
    catch(...)
    {
      Glib::exception_handlers_invoke();
    }
    #endif //GLIBMM_EXCEPTIONS_ENABLED
  }
}


} //anonymous

namespace Gtk
{

#ifdef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
//These default handler callbacks are custom implemented, so that we can create a temporary SelectionData instance.
//To do this, we used the optional custom_c_callback paramater to _WRAP_SIGNAL() in the .hg file.
void Widget_Class::selection_get_callback(GtkWidget* self, GtkSelectionData* p0, guint p1, guint p2)
{
  CppObjectType *const obj = dynamic_cast<CppObjectType*>(
      Glib::ObjectBase::_get_current_wrapper((GObject*)self));

  // Non-gtkmmproc-generated custom classes implicitly call the default
  // Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
  // generated classes can use this optimisation, which avoids the unnecessary
  // parameter conversions if there is no possibility of the virtual function
  // being overridden:
  if(obj && obj->is_derived_())
  {
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    try // Trap C++ exceptions which would normally be lost because this is a C callback.
    {
    #endif //GLIBMM_EXCEPTIONS_ENABLED
      // Call the virtual member method, which derived classes might override.
      SelectionData_WithoutOwnership temp_instance(p0);
      obj->on_selection_get(temp_instance, p1, p2);
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    }
    catch(...)
    {
      Glib::exception_handlers_invoke();
    }
    #endif //GLIBMM_EXCEPTIONS_ENABLED
  }
  else
  {
    BaseClassType *const base = static_cast<BaseClassType*>(
        g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class).
    );

    // Call the original underlying C function:
    if(base && base->selection_get)
      (*base->selection_get)(self, p0, p1, p2);
  }
}

void Widget_Class::drag_data_get_callback(GtkWidget* self, GdkDragContext* p0, GtkSelectionData* p1, guint p2, guint p3)
{
  CppObjectType *const obj = dynamic_cast<CppObjectType*>(
      Glib::ObjectBase::_get_current_wrapper((GObject*)self));

  // Non-gtkmmproc-generated custom classes implicitly call the default
  // Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
  // generated classes can use this optimisation, which avoids the unnecessary
  // parameter conversions if there is no possibility of the virtual function
  // being overridden:
  if(obj && obj->is_derived_())
  {
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    try // Trap C++ exceptions which would normally be lost because this is a C callback.
    {
    #endif //GLIBMM_EXCEPTIONS_ENABLED
      // Call the virtual member method, which derived classes might override.
      SelectionData_WithoutOwnership temp_instance(p1);
      obj->on_drag_data_get(Glib::wrap(p0, true), temp_instance, p2, p3);
    #ifdef GLIBMM_EXCEPTIONS_ENABLED
    }
    catch(...)
    {
      Glib::exception_handlers_invoke();
    }
    #endif //GLIBMM_EXCEPTIONS_ENABLED
  }
  else
  {
    BaseClassType *const base = static_cast<BaseClassType*>(
        g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class).
    );

    // Call the original underlying C function:
    if(base && base->drag_data_get)
      (*base->drag_data_get)(self, p0, p1, p2, p3);
  }
}
#endif //GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED

Widget::~Widget()
{}

bool Widget::intersect(const Gdk::Rectangle& area) const
{
  return gtk_widget_intersect(
      const_cast<GtkWidget*>(gobj()), const_cast<GdkRectangle*>(area.gobj()), 0);
}

void Widget::unset_name()
{
  gtk_widget_set_name(gobj(), 0);
}

void Widget::unset_composite_name()
{
  gtk_widget_set_composite_name(gobj(), 0);
}

void Widget::realize_if_needed()
{
  if(!is_realized())
    realize();
}

void Widget::add_modal_grab()
{
  gtk_grab_add(gobj());
}

void Widget::remove_modal_grab()
{
  gtk_grab_remove(gobj());
}

Widget* Widget::get_current_modal_grab()
{
  return Glib::wrap(gtk_grab_get_current());
}

void Widget::path(Glib::ustring& path_arg, Glib::ustring& path_reversed)
{
  guint path_length = 0;
  Glib::ScopedPtr<gchar> path_ptr;
  Glib::ScopedPtr<gchar> rpath_ptr;

  gtk_widget_path(gobj(), &path_length, path_ptr.addr(), rpath_ptr.addr());

  path_arg.assign(path_ptr.get(), path_ptr.get() + path_length);
  path_reversed.assign(rpath_ptr.get(), rpath_ptr.get() + path_length);
}

void Widget::class_path(Glib::ustring& path_arg, Glib::ustring& path_reversed)
{
  guint path_length = 0;
  Glib::ScopedPtr<gchar> path_ptr;
  Glib::ScopedPtr<gchar> rpath_ptr;

  gtk_widget_class_path(gobj(), &path_length, path_ptr.addr(), rpath_ptr.addr());

  path_arg.assign(path_ptr.get(), path_ptr.get() + path_length);
  path_reversed.assign(rpath_ptr.get(), rpath_ptr.get() + path_length);
}

void Widget::modify_bg_pixmap(StateType state, const Glib::ustring& pixmap_name)
{
  const Glib::RefPtr<RcStyle> modifier_style = get_modifier_style();
  modifier_style->set_bg_pixmap_name(state, pixmap_name);
  modify_style(modifier_style);
}

void Widget::unset_fg(StateType state)
{
  gtk_widget_modify_fg(gobj(), static_cast<GtkStateType>(int(state)), 0);
}

void Widget::unset_bg(StateType state)
{
  gtk_widget_modify_bg(gobj(), static_cast<GtkStateType>(int(state)), 0);
}

void Widget::unset_text(StateType state)
{
  gtk_widget_modify_text(gobj(), static_cast<GtkStateType>(int(state)), 0);
}

void Widget::unset_base(StateType state)
{
  gtk_widget_modify_base(gobj(), static_cast<GtkStateType>(int(state)), 0);
}

void Widget::unset_font()
{
  gtk_widget_modify_font(gobj(), 0);
}

void Widget::unset_cursor()
{
  gtk_widget_modify_cursor(gobj(), 0, 0);
}

bool Widget::is_toplevel() const
  { return GTK_WIDGET_TOPLEVEL(gobj()); }

bool Widget::has_no_window() const
  { return GTK_WIDGET_NO_WINDOW(gobj()); }

bool Widget::is_realized() const
  { return GTK_WIDGET_REALIZED(gobj()); }

bool Widget::is_mapped() const
  { return GTK_WIDGET_MAPPED(gobj()); }

bool Widget::is_visible() const
  { return GTK_WIDGET_VISIBLE(gobj()); }

bool Widget::is_drawable() const
  { return GTK_WIDGET_DRAWABLE(gobj()); }

bool Widget::sensitive() const
  { return GTK_WIDGET_SENSITIVE(gobj()); }

bool Widget::parent_sensitive() const
  { return GTK_WIDGET_PARENT_SENSITIVE(gobj()); }

bool Widget::is_sensitive() const
  { return GTK_WIDGET_IS_SENSITIVE(gobj()); }

bool Widget::can_focus() const
  { return GTK_WIDGET_CAN_FOCUS(gobj()); }

bool Widget::has_focus() const
  { return GTK_WIDGET_HAS_FOCUS(gobj()); }

bool Widget::can_default() const
  { return GTK_WIDGET_CAN_DEFAULT(gobj()); }

bool Widget::has_default() const
  { return GTK_WIDGET_HAS_DEFAULT(gobj()); }

bool Widget::has_grab() const
  { return GTK_WIDGET_HAS_GRAB(gobj()); }

bool Widget::rc_style() const
  { return GTK_WIDGET_RC_STYLE(gobj()); }

bool Widget::is_composite_child() const
  { return GTK_WIDGET_COMPOSITE_CHILD(gobj()); }

bool Widget::app_paintable() const
  { return GTK_WIDGET_APP_PAINTABLE(gobj()); }

bool Widget::receives_default() const
  { return GTK_WIDGET_RECEIVES_DEFAULT(gobj()); }

bool Widget::double_buffered() const
  { return GTK_WIDGET_DOUBLE_BUFFERED(gobj()); }

WidgetFlags Widget::get_flags() const
  { return static_cast<WidgetFlags>(GTK_WIDGET_FLAGS(gobj())); }

void Widget::set_flags(WidgetFlags flags)
  { GTK_WIDGET_SET_FLAGS(gobj(), static_cast<guint32>(flags)); }

void Widget::unset_flags(WidgetFlags flags)
  { GTK_WIDGET_UNSET_FLAGS(gobj(), static_cast<guint32>(flags)); }

int Widget::get_width() const
  { return gobj()->allocation.width; }

int Widget::get_height() const
  { return gobj()->allocation.height; }


void Widget::drag_dest_set(DestDefaults flags, Gdk::DragAction actions)
{
  gtk_drag_dest_set(gobj(), (GtkDestDefaults)flags, 0, 0, (GdkDragAction)actions);
}

void Widget::drag_dest_set(const ArrayHandle_TargetEntry& targets,
                           DestDefaults flags, Gdk::DragAction actions)
{
  // I've used Gdk::ACTION_COPY as the default, because Gdk::ACTION_DEFAULT means that
  // it's never a drag destination, so it would seem like this method didn't work. murrayc.
  gtk_drag_dest_set(
      gobj(), (GtkDestDefaults)flags,
      targets.data(), targets.size(), (GdkDragAction)actions);
}

void Widget::drag_source_set(const ArrayHandle_TargetEntry& targets,
                             Gdk::ModifierType start_button_mask, Gdk::DragAction actions)
{
  // I've used Gdk::MODIFIER_MASK as the default, because it seems
  // to mean 'whatever is possible in the context'. murrayc.
  gtk_drag_source_set(
      gobj(), (GdkModifierType)start_button_mask,
      targets.data(), targets.size(), (GdkDragAction)actions);
}

Widget* Widget::drag_get_source_widget(const Glib::RefPtr<Gdk::DragContext>& context) //static
{
  return Glib::wrap( gtk_drag_get_source_widget(Glib::unwrap(context)) );
}

void Widget::drag_set_as_icon(const Glib::RefPtr<Gdk::DragContext>& context, int hot_x, int hot_y)
{
  gtk_drag_set_icon_widget(Glib::unwrap(context), gobj(), hot_x, hot_y);
}

#ifdef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
void Widget_Class::hierarchy_changed_callback_custom(GtkWidget* self, GtkWidget* p0)
{
  //GTKMM_LIFECYCLE
  //Don't call wrap() on a GTK+ instance whose gtkmm instance has been deleted - just call the original C callback.
  bool gtkmm_child_already_deleted = Glib::_gobject_cppinstance_already_deleted((GObject*)p0);

  if(!gtkmm_child_already_deleted)
  {
    //Call the regular, generated callback:
    Widget_Class::hierarchy_changed_callback(self, p0);
  }
  else
  {
    BaseClassType *const base = static_cast<BaseClassType*>(
        g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class).
    );

    //Call the original underlying C function:
    if(base && base->hierarchy_changed)
      (*base->hierarchy_changed)(self, p0);
  }
}

void Widget_Class::parent_set_callback_custom(GtkWidget* self, GtkWidget* p0)
{
  //GTKMM_LIFECYCLE
  //Don't call wrap() on a GTK+ instance whose gtkmm instance has been deleted - just call the original C callback.
  bool gtkmm_p0_already_deleted = Glib::_gobject_cppinstance_already_deleted((GObject*)p0);

  if(!gtkmm_p0_already_deleted)
  {
    //Call the regular, generated callback:
    Widget_Class::parent_set_callback(self, p0);
  }
  else
  {
    BaseClassType *const base = static_cast<BaseClassType*>(
        g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class).
    );

    //Call the original underlying C function:
    if(base && base->parent_set)
      (*base->parent_set)(self, p0);
  }
}
#endif //#GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED

void Widget_Class::dispose_vfunc_callback(GObject* self)
{
  //Avoid disposal. See also Window_Class::dispose_vfunc_callback().

  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Widget_Class::dispose_vfunc_callback(): gobject_: %p\n", (void*)self);
  //if(self)
   //g_warning("  gtypename: %s\n", G_OBJECT_TYPE_NAME(self));
  #endif
  
  Widget *const obj = dynamic_cast<Widget*>(
      Glib::ObjectBase::_get_current_wrapper(self));

  // This function might be invoked recursively because we're triggering
  // several signal emissions, particularly signal_hide().  Therefore we
  // have to test for cpp_destruction_in_progress_ at this point.

  if(obj && !obj->_cpp_destruction_is_in_progress()) //When it should really be destroyed, we zero gobj_.
  { 
    GtkWidget *const pWidget = obj->gobj();
    g_return_if_fail(pWidget == GTK_WIDGET(self));

    // Abort dispose if the widget isn't managed, in order to prevent
    // the nasty self-destroying behaviour of GTK+.  This applies to
    // any widget inside a GtkContainer on gtk_container_destroy()
    // See also Window_Class::dispose_vfunc_callback().
  
    if(obj->referenced_) //Not managed
    { 
      // GTKMM_LIFECYCLE
      // Remove the widget from its parent container so that it
      // won't be destroyed later by gtk_container_destroy().
      if(pWidget->parent)
      {
        // Normally, we would have to ref the child widget because
        // gtk_container_remove() unrefs it.  But since we only remove
        // non-managed objects here, the container just releases the
        // reference it has acquired before in gtk_container_add().

        #ifdef GLIBMM_DEBUG_REFCOUNTING
        g_warning("Widget_Class::dispose_vfunc_callback(): removing gobject_: %p from parent: %p", (void*)self, (void*)pWidget->parent);
        #endif
  
        gtk_container_remove(GTK_CONTAINER(pWidget->parent), pWidget);

        #ifdef GLIBMM_DEBUG_REFCOUNTING
        g_warning("Widget_Class::dispose_vfunc_callback(): after removing from parent.");
        #endif
      }
      // Special case for GtkMenu because gtk_menu_attach_to_widget does
      // not set widget->parent but gtk_menu_item_destroy() destroys the menu
      // like gtk_container_destroy()
      // Gtk::Menu does not use a parent widget because it must 
      // be contained in its Gtk::Window so that it can be displayed as a popup.
      else if (GTK_IS_MENU(pWidget) && GTK_IS_MENU_ITEM(gtk_menu_get_attach_widget(GTK_MENU(pWidget))))
      {
         gtk_menu_detach(GTK_MENU(pWidget));
      }

      #ifdef GLIBMM_DEBUG_REFCOUNTING
      g_warning("Widget_Class::dispose_vfunc_callback(): before gtk_widget_hide().");
      #endif
        
      // Now hide the widget.  The C++ object must _not_ be accessed anymore
      // after this call, because a signal_hide() handler might delete it.
      gtk_widget_hide(pWidget);

      #ifdef GLIBMM_DEBUG_REFCOUNTING
      g_warning("Widget_Class::dispose_vfunc_callback(): after gtk_widget_hide().");
      #endif
      
      // GTKMM_LIFECYCLE
      return; // Prevent calling of normal C dispose vfunc (see below)
    }

    #ifdef GLIBMM_DEBUG_REFCOUNTING
    g_warning("Widget_Class::dispose_vfunc_callback(): unreferenced: before gtk_widget_hide().");
    #endif
    
    // Always hide widgets on gtk_object_destroy(), regardless of whether
    // the widget is managed or not.  This is done for consistency so that
    // connecting to signal_hide() is guaranteed to work.
    gtk_widget_hide(pWidget);

    #ifdef GLIBMM_DEBUG_REFCOUNTING
    g_warning("Widget_Class::dispose_vfunc_callback(): unreferenced:  after gtk_widget_hide().");
    #endif
  }

  GObjectClass *const base = static_cast<GObjectClass*>(
      g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)));

  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Widget_Class::dispose_vfunc_callback(): before calling base->dispose.");
  #endif
        
  if(base->dispose)
    (*base->dispose)(self);

  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Widget_Class::dispose_vfunc_callback(): after calling base->dispose.");
  #endif
}


void Widget::unset_style()
{
  gtk_widget_set_style(gobj(), 0 /* See GTK+ docs */);
}

Glib::ustring Widget::drag_dest_find_target(const Glib::RefPtr<Gdk::DragContext>& context) const
{
  return Gdk::AtomString::to_cpp_type(gtk_drag_dest_find_target(const_cast<GtkWidget*>(gobj()), Glib::unwrap(context), 0 /* See GTK+ docs */));
}

void Widget::unset_shape_combine_mask()
{            
  gtk_widget_shape_combine_mask(gobj(), 0, 0, 0); /* See GTK+ docs */
}

void Widget::unset_input_shape_combine_mask()
{            
  gtk_widget_input_shape_combine_mask(gobj(), 0, 0, 0); /* See GTK+ docs */
}

void Widget::draw_insertion_cursor(Glib::RefPtr<Gdk::Drawable> drawable, const Gdk::Rectangle& area, const Gdk::Rectangle& location, bool is_primary, TextDirection direction, bool draw_arrow)
{
  gtk_draw_insertion_cursor(gobj(), drawable->gobj(), const_cast<GdkRectangle*>(area.gobj()), const_cast<GdkRectangle*>(location.gobj()), is_primary, (GtkTextDirection)direction, draw_arrow);
}

Requisition Widget::size_request() const
{
  Requisition requisition;
  gtk_widget_size_request(const_cast<GtkWidget*>(gobj()), (GtkRequisition*)(&requisition));
  return requisition;
}



} // namespace Gtk

