/*
 * 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 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 <glibmm/vectorutils.h>

#include <gtkmm/accelgroup.h>
#include <gdkmm/monitor.h>
#include <gdkmm/cairoutils.h>
#include <gtk/gtk.h>


namespace Gtk
{

Glib::RefPtr<AccelGroup> Window::get_accel_group()
{
  //There doesn't seem to be any gtk_window_get_accel_group().
  //I think that you're supposed to remember what you added yourself.
  //And that's what we do here.

  if(!accel_group_)
  {
    accel_group_ = AccelGroup::create();
    add_accel_group(accel_group_);
  }

  return accel_group_;
}

void Window::raise()
{
  get_window()->raise();
}

void Window::set_manage()
{
  g_warning("gtkmm: Attempt to call Gtk::manage() on a Gtk::Window, but a Gtk::Window has no parent container to manage its lifetime.\n");
}

void Window::destroy_()
{
  //Called from destructors.
  //overridden so that the correct _release_c_instance() ends up being called by the destructor.
  //The destructors that call destroy_() are virtual, but _release_c_instance() is not.

  //GTKMM_LIFECYCLE

  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Gtk::Window::destroy_(): gobject_: %p\n", (void*) gobject_);
  if(gobject_)
   g_warning("  gtypename: %s\n", G_OBJECT_TYPE_NAME(gobject_));
  #endif

  if ( !cpp_destruction_in_progress_ )
  {
    cpp_destruction_in_progress_ = true;

    //destroy the C instance:
    _release_c_instance();
  }

  //The C++ destructor will be reached later. This function was called by a destructor.
}

void Window::_release_c_instance()
{
  //We override this, (though it's not virtual - we just call it from this class),
  //because top-level windows can only be destroyed with gtk_widget_destroy, according to Owen Taylor. murrayc.
  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Gtk::Window::_release_c_instance() gobject_=%p\n", (void*) gobject_);
  #endif

  cpp_destruction_in_progress_ = true;

  // remove our hook.
  GtkWidget* object = GTK_WIDGET(gobj());
  if (object)
  {
    //Windows can not be unrefed. They are "self-owning".
    gtk_widget_destroy(object);

    //If the GObject still exists, disconnect the C++ wrapper from it.
    //The C++ wrapper is being deleted right now.
    disconnect_cpp_wrapper();

    //Glib::Object::~Object() will not g_object_unref() it too, because gobject_ is now 0.
  }
}

void Window_Class::dispose_vfunc_callback(GObject* self)
{
  //Avoid disposal of Windows on delete_event (window close) signal.

  #ifdef GLIBMM_DEBUG_REFCOUNTING
  g_warning("Window_Class::dispose_vfunc_callback(): gobject_: %p\n", (void*)self);
  #endif

  const auto 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_.
  {
    const auto 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:
    //
    // - GtkWindow, if it received "delete_event"
    // - GtkDialog, which destroys on "response" by default

    #ifdef GLIBMM_DEBUG_REFCOUNTING
    g_warning("Window_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("Window_Class::dispose_vfunc_callback(): after gtk_widget_hide().");
    #endif

    // GTKMM_LIFECYCLE
    return; // Prevent calling of normal C dispose vfunc (see below)
  }
  else
  {
    #ifdef GLIBMM_DEBUG_REFCOUNTING
    //g_warning("Window_Class::dispose_vfunc_callback(): unreferenced: before gtk_widget_hide().");
    #endif

    // Always hide widgets on object destruction, 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("Window_Class::dispose_vfunc_callback(): unreferenced:  after gtk_widget_hide().");
    #endif

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

    #ifdef GLIBMM_DEBUG_REFCOUNTING
    g_warning("Window_Class::dispose_vfunc_callback(): before calling base->dispose.");
    #endif

    if(base->dispose)
      (*base->dispose)(self);

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

void Window::unset_focus()
{
  gtk_window_set_focus(gobj(), nullptr /* See GTK+ docs */);
}

void Window::unset_default()
{
  gtk_window_set_default(gobj(), nullptr /* See GTK+ docs */);
}

void Window::unset_transient_for()
{
  gtk_window_set_transient_for(gobj(), nullptr /* See GTK+ docs */);
}

void Window::unset_attached_to()
{
  gtk_window_set_attached_to(gobj(), nullptr /* See GTK+ docs */);
}

void Window::unset_application()
{
  gtk_window_set_application(gobj(), nullptr /* See GTK+ docs */);
}

} // namespace Gtk
