/* 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, see <https://www.gnu.org/licenses/>.
 */

#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;

_WRAP_ENUM(WindowGravity, GtkWindowGravity, decl_prefix GTKMM_API)

/** 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.
 * Call @link set_remove_on_hide() set_remove_on_hide(false) @endlink
 * if you want to avoid that.
 *
 * @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("suspended", 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_PROPERTY("gravity", WindowGravity)

  _WRAP_SIGNAL(void keys_changed(), "keys_changed",
    deprecated "Use Gtk::Shortcut and Gtk::EventController to implement keyboard shortcuts.")
  _WRAP_SIGNAL(bool close_request(), "close-request")
  // TODO: Remove no_default_handler when we can break ABI.
  _WRAP_SIGNAL(void force_close(), "force-close", no_default_handler)

  //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_window_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 = &Window_dispose_vfunc_c_callback;
    Window_dispose_vfunc_funcptr = &dispose_vfunc_callback;
  _SECTION(SECTION_PH_VFUNCS)
    static void dispose_vfunc_callback(GObject* self);
  _POP()
#m4end

  /** Sets whether hiding a window shall remove it from the application.
   *
   * Not calling it has the same effect as calling it with @a remove = <tt>true</tt>.
   *
   * Unlike set_hide_on_close(), this is a static function. It affects all windows.
   *
   * @newin{4,22}
   *
   * @param remove If <tt>true</tt>, windows that have been added to an application
   *        will be removed from the application when they are hidden.
   *        If <tt>false</tt>, the connection between a window and an application
   *        will not be affected when the window is hidden.
   */
  static void set_remove_on_hide(bool remove = true);

  /** Returns whether hiding a window will remove it from the application.
   *
   * @newin{4,22}
   */
  static bool get_remove_on_hide();

  _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().
   */
  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)
  _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,
    deprecated "Use the present() overload without a parameter.")

  _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(bool is_suspended() const, gtk_window_is_suspended)

  /** Opens or closes the [interactive debugger](https://docs.gtk.org/gtk4/running.html#interactive-debugging).
   *
   * The debugger offers access to the widget hierarchy of the application
   * and to useful debugging tools.
   *
   * This function allows applications that already use
   * <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> or
   * <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>D</kbd> for their own key shortcuts
   * to add a different shortcut to open the Inspector.
   *
   * If you are not overriding the default key shortcuts for the Inspector,
   * you should not use this function.
   *
   * @newin{4,16}
   *
   * @param enable <tt>true</tt> to enable interactive debugging.
   */
  _WRAP_METHOD(static void set_interactive_debugging(bool enable = true),
    gtk_window_set_interactive_debugging, newin "4,16")

  _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)

  _WRAP_METHOD(WindowGravity get_gravity() const, gtk_window_get_gravity)
  _WRAP_METHOD(void set_gravity(WindowGravity gravity), gtk_window_set_gravity)

  /** 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
