/*
 * Copyright (C) 1998-2013 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 <glibmm/object.h>
#include <gdkmm/surface.h>
#include <gdkmm/texture.h>
#include <gtkmm/widget.h>
#include <gtkmm/native.h>
#include <gtkmm/root.h>
#include <gtkmm/shortcutmanager.h>
#include <gtkmm/windowgroup.h>

_DEFS(gtkmm,gtk)
_PINCLUDE(gtkmm/private/widget_p.h)

namespace Gio
{
class GTKMM_API ListModel;
}

namespace Gtk
{

class GTKMM_API Application;
class GTKMM_API WindowGroup;

/** Toplevel %Window.
 * This represents all widgets which are physical windows controlled
 * by the window manager.
 *
 * The window will be destroyed when the window manager's close button is clicked.
 * Call set_hide_on_close() if you want it to be hidden instead.
 *
 * When a window is closed (hidden), it's removed from its application.
 * This can cause Application::run() to return, if it is the last window.
 *
 * @ingroup Widgets
 */
class GTKMM_API Window : public Widget, public Native, public ShortcutManager, public Root
{
  _CLASS_GTKOBJECT(Window, GtkWindow, GTK_WINDOW, Gtk::Widget, GtkWidget, , , GTKMM_API)
  _IMPLEMENTS_INTERFACE(Native)
  _IMPLEMENTS_INTERFACE(ShortcutManager)
  _IMPLEMENTS_INTERFACE(Root)
  _UNMANAGEABLE

public:
  // Disambiguate calls to get_display(). Use Root::get_display(), not Widget::get_display().
  // gtk_widget_get_display() calls gtk_root_get_display(). There is no reason
  // to call gtk_widget_get_display() from a class that implements GtkRoot.
  using Root::get_display;

  // All constructors are hand-coded, because they connect a signal handler.
  _CUSTOM_CTOR_CAST()
  _CUSTOM_MOVE_OPERATIONS()
  Window(Window&& src) noexcept;
  Window& operator=(Window&& src) noexcept;

  explicit Window();
  _IGNORE(gtk_window_new)

  _WRAP_PROPERTY("title", Glib::ustring)
  _WRAP_PROPERTY("startup-id", Glib::ustring)
  _WRAP_PROPERTY("resizable", bool)
  _WRAP_PROPERTY("modal", bool)
  _WRAP_PROPERTY("default_width", int)
  _WRAP_PROPERTY("default_height", int)
  _WRAP_PROPERTY("destroy_with_parent", bool)
  _WRAP_PROPERTY("hide-on-close", bool)
  _WRAP_PROPERTY("mnemonics-visible", bool)
  _WRAP_PROPERTY("icon-name", Glib::ustring)
  _WRAP_PROPERTY("display", Glib::RefPtr<Gdk::Display>)
  _WRAP_PROPERTY("is_active", bool)
  _WRAP_PROPERTY("decorated", bool)
  _WRAP_PROPERTY("transient_for", Window*)

  _WRAP_PROPERTY("deletable", bool)
  _WRAP_PROPERTY("application", Glib::RefPtr<Application>)
  _WRAP_PROPERTY("focus-visible", bool)
  _WRAP_PROPERTY("maximized", bool)
  _WRAP_PROPERTY("fullscreened", bool)
  _WRAP_PROPERTY("default-widget", Widget*)
  _WRAP_PROPERTY("focus-widget", Widget*)
  _WRAP_PROPERTY("child", Widget*)
  _WRAP_PROPERTY("titlebar", Widget*)
  _WRAP_PROPERTY("handle-menubar-accel", bool)

  _WRAP_SIGNAL(void keys_changed(), "keys_changed")
  _WRAP_SIGNAL(bool close_request(), "close-request")

  //Keybinding signals:
  _IGNORE_SIGNAL("activate_focus")
  _IGNORE_SIGNAL("activate_default")
  _IGNORE_SIGNAL("enable-debugging")

#m4begin
dnl// Hook in special code to catch explicit uses of gtk_object_destroy() by
dnl// GTK+ code, e.g. when receiving the GDK_DELETE event.  For details, look
dnl// at Gtk::Window_Class::dispose_vfunc_callback() in window.ccg.
dnl
  _PUSH(SECTION_PCC_CLASS_INIT_VFUNCS)
    reinterpret_cast<GObjectClass*>(klass)->dispose = &dispose_vfunc_callback;
  _SECTION(SECTION_PH_VFUNCS)
    static void dispose_vfunc_callback(GObject* self);
  _POP()
#m4end

  _WRAP_METHOD(void set_title(const Glib::ustring& title),
               gtk_window_set_title)

  _WRAP_METHOD(Glib::ustring get_title() const, gtk_window_get_title)

  _WRAP_METHOD(void set_startup_id(const Glib::ustring& startup_id), gtk_window_set_startup_id)

  _WRAP_METHOD(void set_focus(Gtk::Widget& focus), gtk_window_set_focus)
  void unset_focus();

  _WRAP_METHOD(Widget* get_focus(), gtk_window_get_focus)
  _WRAP_METHOD(const Widget* get_focus() const, gtk_window_get_focus, constversion)
  _WRAP_METHOD(void set_default_widget(Gtk::Widget& default_widget), gtk_window_set_default_widget)
  void unset_default_widget();

  _WRAP_METHOD(Widget* get_default_widget(), gtk_window_get_default_widget)
  _WRAP_METHOD(const Widget* get_default_widget() const, gtk_window_get_default_widget, constversion)

  _WRAP_METHOD(void set_transient_for(Window& parent), gtk_window_set_transient_for)

  /** Unsets the current transient window.
   * @see set_transient_for().
   * @newin{2,20}
   */
  void unset_transient_for();

  _WRAP_METHOD(Window* get_transient_for(), gtk_window_get_transient_for)
  _WRAP_METHOD(const Window* get_transient_for() const, gtk_window_get_transient_for, constversion)

  _WRAP_METHOD(void set_destroy_with_parent(bool setting = true),
    gtk_window_set_destroy_with_parent, newin "4,8")
  _WRAP_METHOD(bool get_destroy_with_parent() const, gtk_window_get_destroy_with_parent)

  /** Drop the internal reference GTK holds on toplevel windows.
   *
   * If this window is managed, this C++ wrapper will be deleted when the
   * underlying C instance is destroyed.
   *
   * After a call to %destroy(), don't call any method that accesses the
   * underlying C instance.
   *
   * If the C++ wrapper is deleted, the underlying C instance will be destroyed.
   * If the C instance is destroyed and the C++ wrapper is managed, the wrapper
   * will be deleted. The difference is in the order in which actions are taken.
   * That may or may not be important. If the C instance is destroyed before
   * the wrapper is deleted, C++ signal handlers can be called during the destruction.
   * For instance, if you connect to Gtk::Widget::signal_unrealize() or override
   * Gtk::Widget::on_unrealize(), those signal handlers can be called only if
   * the wrapper still exists when the signal is emitted.
   *
   * @newin{4,8}
   */
  void destroy();
  _IGNORE(gtk_window_destroy)

  _WRAP_METHOD(void set_hide_on_close(bool setting = true), gtk_window_set_hide_on_close)
  _WRAP_METHOD(bool get_hide_on_close() const, gtk_window_get_hide_on_close)

  _WRAP_METHOD(void set_mnemonics_visible(bool setting = true), gtk_window_set_mnemonics_visible)
  _WRAP_METHOD(bool get_mnemonics_visible() const, gtk_window_get_mnemonics_visible)

  _WRAP_METHOD(void set_focus_visible(bool setting = true), gtk_window_set_focus_visible)
  _WRAP_METHOD(bool get_focus_visible() const, gtk_window_get_focus_visible)

  _WRAP_METHOD(void set_resizable(bool resizable = true), gtk_window_set_resizable)

  _WRAP_METHOD(bool get_resizable() const, gtk_window_get_resizable)

  _WRAP_METHOD(void set_display(const Glib::RefPtr<Gdk::Display>& display), gtk_window_set_display)

  _WRAP_METHOD(bool is_active() const, gtk_window_is_active)

  _WRAP_METHOD(void set_decorated(bool setting = true), gtk_window_set_decorated)
  _WRAP_METHOD(bool get_decorated() const, gtk_window_get_decorated)

  _WRAP_METHOD(void set_deletable(bool setting = true), gtk_window_set_deletable)
  _WRAP_METHOD(bool get_deletable() const, gtk_window_get_deletable)

  _WRAP_METHOD(void set_icon_name(const Glib::ustring& name), gtk_window_set_icon_name, newin "2,18")
  _WRAP_METHOD(Glib::ustring get_icon_name() const, gtk_window_get_icon_name)

  _WRAP_METHOD(static void set_default_icon_name(const Glib::ustring& name), gtk_window_set_default_icon_name)
  _WRAP_METHOD(static Glib::ustring get_default_icon_name(), gtk_window_get_default_icon_name)
  _WRAP_METHOD(static void set_auto_startup_notification(bool setting = true), gtk_window_set_auto_startup_notification)

  _WRAP_METHOD(void set_modal(bool modal = true), gtk_window_set_modal)
  _WRAP_METHOD(bool get_modal() const, gtk_window_get_modal)

  _WRAP_METHOD(static Glib::RefPtr<Gio::ListModel> get_toplevels(), gtk_window_get_toplevels, refreturn)
#m4 _CONVERSION(`GList*',`std::vector<Window*>',`Glib::ListHandler<Window*>::list_to_vector($3, Glib::OWNERSHIP_SHALLOW)')
  _WRAP_METHOD(static std::vector<Window*> list_toplevels(), gtk_window_list_toplevels)

  _WRAP_METHOD(void present(), gtk_window_present)
  _WRAP_METHOD(void present(guint32 timestamp), gtk_window_present_with_time)

  _WRAP_METHOD(void minimize(), gtk_window_minimize)
  _WRAP_METHOD(void unminimize(), gtk_window_unminimize)

  _WRAP_METHOD(void maximize(), gtk_window_maximize)
  _WRAP_METHOD(void unmaximize(), gtk_window_unmaximize)

  _WRAP_METHOD(void fullscreen(), gtk_window_fullscreen)
  _WRAP_METHOD(void unfullscreen(), gtk_window_unfullscreen)
  _WRAP_METHOD(void fullscreen_on_monitor(const Glib::RefPtr<Gdk::Monitor>& monitor), gtk_window_fullscreen_on_monitor)
  _WRAP_METHOD(void close(), gtk_window_close)

  _WRAP_METHOD(void set_default_size(int width, int height),
               gtk_window_set_default_size)

  _WRAP_METHOD(void get_default_size(int& width, int& height) const,
               gtk_window_get_default_size)

  _WRAP_METHOD(Glib::RefPtr<WindowGroup> get_group(), gtk_window_get_group, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const WindowGroup> get_group() const, gtk_window_get_group, refreturn, constversion)

  _WRAP_METHOD(bool has_group() const, gtk_window_has_group)

  _WRAP_METHOD(Glib::RefPtr<Application> get_application(), gtk_window_get_application, refreturn)
  _WRAP_METHOD(Glib::RefPtr<const Application> get_application() const, gtk_window_get_application, refreturn)

  // Concerning the removal of a hidden window from the application, see
  // https://bugzilla.gnome.org/show_bug.cgi?id=639931 and Gtk::Widget_Class::hide_callback().
  /** Sets the Gtk::Application associated with the window.
   *
   * The application will be kept alive for at least as long as it has any windows
   * associated with it. (See Gio::Application::hold() for a way to keep it alive
   * without windows.)
   *
   * Normally, the connection between the application and the window will remain
   * until the window is closed (hidden), but you can explicitly remove it by
   * calling unset_application().
   *
   * This is equivalent to calling Gtk::Application::remove_window() and/or
   * Gtk::Application::add_window() on the old/new applications as relevant.
   *
   * @param application A Gtk::Application.
   */
  _WRAP_METHOD(void set_application(const Glib::RefPtr<Application>& application), gtk_window_set_application)

  /** Unsets the Application associated with the window.
   * This could cause the application to exit if it is the application's last window.
   */
  void unset_application();

  _WRAP_METHOD(void set_child(Widget& child), gtk_window_set_child)
  void unset_child();
  _WRAP_METHOD(Widget* get_child(), gtk_window_get_child)
  _WRAP_METHOD(const Widget* get_child() const, gtk_window_get_child, constversion)

  _WRAP_METHOD(void set_titlebar(Widget& titlebar), gtk_window_set_titlebar)

  /** Unsets the titlebar.
   * @see set_titlebar()
   */
  void unset_titlebar();

  _WRAP_METHOD(Widget* get_titlebar(), gtk_window_get_titlebar)
  _WRAP_METHOD(const Widget* get_titlebar() const, gtk_window_get_titlebar, constversion)

  _WRAP_METHOD(bool is_maximized() const, gtk_window_is_maximized)
  _WRAP_METHOD(bool is_fullscreen() const, gtk_window_is_fullscreen)

  _WRAP_METHOD(void set_handle_menubar_accel(bool handle_menubar_accel), gtk_window_set_handle_menubar_accel)
  _WRAP_METHOD(bool get_handle_menubar_accel() const, gtk_window_get_handle_menubar_accel)

  // from gtk/gtkshow.h:
  //TODO: _WRAP_METHOD(void show_uri(const Glib::ustring& uri, guint32 timestamp), gtk_show_uri)
  // gtk_show_uri_full[_finish]()

  /** Used by Gtk::manage() and Gtk::make_managed(). You should not need to use this directly.
   * Overridden because a %Gtk::Window is not managed by a container.
   * Beginning with gtkmm 4.8, a %Gtk::Window can be managed. If managed, it's
   * deleted when its underlying C instance is destroyed.
   */
  void set_manage() override;

protected:
  void on_window_hide();

  //See comments in the implementations:
  void destroy_();
  void _release_c_instance();
};

} // namespace Gtk
