/* PIKA - Photo and Image Kooker Application
 * a rebranding of The GNU Image Manipulation Program (created with heckimp)
 * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
 *
 * Original copyright, applying to most contents (license remains unchanged): 
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * dialogs.c
 * Copyright (C) 2010 Martin Nordholts <martinn@src.gnome.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>

#include "libpikabase/pikabase.h"
#include "libpikaconfig/pikaconfig.h"
#include "libpikawidgets/pikawidgets.h"

#include "dialogs-types.h"

#include "config/pikaguiconfig.h"

#include "display/pikadisplay.h"
#include "display/pikadisplayshell.h"

#include "core/pika.h"
#include "core/pikacontext.h"
#include "core/pikalist.h"

#include "widgets/pikadialogfactory.h"
#include "widgets/pikadockwindow.h"
#include "widgets/pikahelp-ids.h"
#include "widgets/pikamenufactory.h"
#include "widgets/pikasessioninfo.h"
#include "widgets/pikasessioninfo-aux.h"
#include "widgets/pikasessionmanaged.h"
#include "widgets/pikatoolbox.h"

#include "dialogs.h"
#include "dialogs-constructors.h"

#include "pika-log.h"

#include "pika-intl.h"


PikaContainer *global_recent_docks = NULL;


#define FOREIGN(id, singleton, remember_size) \
  { id                     /* identifier       */, \
    NULL                   /* name             */, \
    NULL                   /* blurb            */, \
    NULL                   /* icon_name        */, \
    NULL                   /* help_id          */, \
    NULL                   /* new_func         */, \
    dialogs_restore_dialog /* restore_func     */, \
    0                      /* view_size        */, \
    singleton              /* singleton        */, \
    TRUE                   /* session_managed  */, \
    remember_size          /* remember_size    */, \
    FALSE                  /* remember_if_open */, \
    TRUE                   /* hideable         */, \
    FALSE                  /* image_window     */, \
    FALSE                  /* dockable         */}

#define IMAGE_WINDOW(id, singleton, remember_size) \
  { id                     /* identifier       */, \
    NULL                   /* name             */, \
    NULL                   /* blurb            */, \
    NULL                   /* icon_name        */, \
    NULL                   /* help_id          */, \
    NULL                   /* new_func         */, \
    dialogs_restore_window /* restore_func     */, \
    0                      /* view_size        */, \
    singleton              /* singleton        */, \
    TRUE                   /* session_managed  */, \
    remember_size          /* remember_size    */, \
    TRUE                   /* remember_if_open */, \
    FALSE                  /* hideable         */, \
    TRUE                   /* image_window     */, \
    FALSE                  /* dockable         */}

#define TOPLEVEL(id, new_func, singleton, session_managed, remember_size) \
  { id                     /* identifier       */, \
    NULL                   /* name             */, \
    NULL                   /* blurb            */, \
    NULL                   /* icon_name        */, \
    NULL                   /* help_id          */, \
    new_func               /* new_func         */, \
    dialogs_restore_dialog /* restore_func     */, \
    0                      /* view_size        */, \
    singleton              /* singleton        */, \
    session_managed        /* session_managed  */, \
    remember_size          /* remember_size    */, \
    FALSE                  /* remember_if_open */, \
    TRUE                   /* hideable         */, \
    FALSE                  /* image_window     */, \
    FALSE                  /* dockable         */}

#define DOCKABLE(id, name, blurb, icon_name, help_id, new_func, view_size, singleton) \
  { id                     /* identifier       */, \
    name                   /* name             */, \
    blurb                  /* blurb            */, \
    icon_name              /* icon_name        */, \
    help_id                /* help_id          */, \
    new_func               /* new_func         */, \
    NULL                   /* restore_func     */, \
    view_size              /* view_size        */, \
    singleton              /* singleton        */, \
    FALSE                  /* session_managed  */, \
    FALSE                  /* remember_size    */, \
    TRUE                   /* remember_if_open */, \
    TRUE                   /* hideable         */, \
    FALSE                  /* image_window     */, \
    TRUE                   /* dockable         */}

#define DOCK(id, new_func) \
  { id                     /* identifier       */, \
    NULL                   /* name             */, \
    NULL                   /* blurb            */, \
    NULL                   /* icon_name        */, \
    NULL                   /* help_id          */, \
    new_func               /* new_func         */, \
    dialogs_restore_dialog /* restore_func     */, \
    0                      /* view_size        */, \
    FALSE                  /* singleton        */, \
    FALSE                  /* session_managed  */, \
    FALSE                  /* remember_size    */, \
    FALSE                  /* remember_if_open */, \
    TRUE                   /* hideable         */, \
    FALSE                  /* image_window     */, \
    FALSE                  /* dockable         */}

#define DOCK_WINDOW(id, new_func) \
  { id                     /* identifier       */, \
    NULL                   /* name             */, \
    NULL                   /* blurb            */, \
    NULL                   /* icon_name        */, \
    NULL                   /* help_id          */, \
    new_func               /* new_func         */, \
    dialogs_restore_dialog /* restore_func     */, \
    0                      /* view_size        */, \
    FALSE                  /* singleton        */, \
    TRUE                   /* session_managed  */, \
    TRUE                   /* remember_size    */, \
    TRUE                   /* remember_if_open */, \
    TRUE                   /* hideable         */, \
    FALSE                  /* image_window     */, \
    FALSE                  /* dockable         */}

#define LISTGRID(id, new_func, name, blurb, icon_name, help_id, view_size) \
  { "pika-"#id"-list"             /* identifier       */,  \
    name                          /* name             */,  \
    blurb                         /* blurb            */,  \
    icon_name                     /* icon_name        */,  \
    help_id                       /* help_id          */,  \
    dialogs_##new_func##_list_view_new /* new_func         */,  \
    NULL                          /* restore_func     */,  \
    view_size                     /* view_size        */,  \
    FALSE                         /* singleton        */,  \
    FALSE                         /* session_managed  */,  \
    FALSE                         /* remember_size    */,  \
    TRUE                          /* remember_if_open */,  \
    TRUE                          /* hideable         */,  \
    FALSE                         /* image_window     */,  \
    TRUE                          /* dockable         */}, \
  { "pika-"#id"-grid"             /* identifier       */,  \
    name                          /* name             */,  \
    blurb                         /* blurb            */,  \
    icon_name                     /* icon_name        */,  \
    help_id                       /* help_id          */,  \
    dialogs_##new_func##_grid_view_new /* new_func         */,  \
    NULL                          /* restore_func     */,  \
    view_size                     /* view_size        */,  \
    FALSE                         /* singleton        */,  \
    FALSE                         /* session_managed  */,  \
    FALSE                         /* remember_size    */,  \
    TRUE                          /* remember_if_open */,  \
    TRUE                          /* hideable         */,  \
    FALSE                         /* image_window     */,  \
    TRUE                          /* dockable         */}

#define LIST(id, new_func, name, blurb, icon_name, help_id, view_size) \
  { "pika-"#id"-list"                   /* identifier       */, \
    name                                /* name             */, \
    blurb                               /* blurb            */, \
    icon_name                            /* icon_name         */, \
    help_id                             /* help_id          */, \
    dialogs_##new_func##_list_view_new  /* new_func         */, \
    NULL                                /* restore_func     */, \
    view_size                           /* view_size        */, \
    FALSE                               /* singleton        */, \
    FALSE                               /* session_managed  */, \
    FALSE                               /* remember_size    */, \
    TRUE                                /* remember_if_open */, \
    TRUE                                /* hideable         */, \
    FALSE                               /* image_window     */, \
    TRUE                                /* dockable         */}


static GtkWidget * dialogs_restore_dialog (PikaDialogFactory *factory,
                                           GdkMonitor        *monitor,
                                           PikaSessionInfo   *info);
static GtkWidget * dialogs_restore_window (PikaDialogFactory *factory,
                                           GdkMonitor        *monitor,
                                           PikaSessionInfo   *info);


static const PikaDialogFactoryEntry entries[] =
{
  /*  foreign toplevels without constructor  */
  FOREIGN ("pika-brightness-contrast-tool-dialog", TRUE,  FALSE),
  FOREIGN ("pika-color-balance-tool-dialog",       TRUE,  FALSE),
  FOREIGN ("pika-color-picker-tool-dialog",        TRUE,  TRUE),
  FOREIGN ("pika-colorize-tool-dialog",            TRUE,  FALSE),
  FOREIGN ("pika-crop-tool-dialog",                TRUE,  FALSE),
  FOREIGN ("pika-curves-tool-dialog",              TRUE,  TRUE),
  FOREIGN ("pika-desaturate-tool-dialog",          TRUE,  FALSE),
  FOREIGN ("pika-foreground-select-tool-dialog",   TRUE,  FALSE),
  FOREIGN ("pika-gegl-tool-dialog",                TRUE,  FALSE),
  FOREIGN ("pika-gradient-tool-dialog",            TRUE,  FALSE),
  FOREIGN ("pika-hue-saturation-tool-dialog",      TRUE,  FALSE),
  FOREIGN ("pika-levels-tool-dialog",              TRUE,  TRUE),
  FOREIGN ("pika-measure-tool-dialog",             TRUE,  FALSE),
  FOREIGN ("pika-offset-tool-dialog",              TRUE,  FALSE),
  FOREIGN ("pika-operation-tool-dialog",           TRUE,  FALSE),
  FOREIGN ("pika-posterize-tool-dialog",           TRUE,  FALSE),
  FOREIGN ("pika-rotate-tool-dialog",              TRUE,  FALSE),
  FOREIGN ("pika-scale-tool-dialog",               TRUE,  FALSE),
  FOREIGN ("pika-shear-tool-dialog",               TRUE,  FALSE),
  FOREIGN ("pika-text-tool-dialog",                TRUE,  TRUE),
  FOREIGN ("pika-threshold-tool-dialog",           TRUE,  FALSE),
  FOREIGN ("pika-transform-3d-tool-dialog",        TRUE,  FALSE),
  FOREIGN ("pika-perspective-tool-dialog",         TRUE,  FALSE),
  FOREIGN ("pika-unified-transform-tool-dialog",   TRUE,  FALSE),
  FOREIGN ("pika-handle-transform-tool-dialog",    TRUE,  FALSE),

  FOREIGN ("pika-toolbox-color-dialog",            TRUE,  FALSE),
  FOREIGN ("pika-gradient-editor-color-dialog",    TRUE,  FALSE),
  FOREIGN ("pika-palette-editor-color-dialog",     TRUE,  FALSE),
  FOREIGN ("pika-colormap-editor-color-dialog",    TRUE,  FALSE),
  FOREIGN ("pika-colormap-selection-color-dialog", TRUE,  FALSE),

  FOREIGN ("pika-controller-editor-dialog",        FALSE, TRUE),
  FOREIGN ("pika-controller-action-dialog",        FALSE, TRUE),

  /*  ordinary toplevels  */
  TOPLEVEL ("pika-image-new-dialog",
            dialogs_image_new_new,          FALSE, TRUE, FALSE),
  TOPLEVEL ("pika-file-open-dialog",
            dialogs_file_open_new,          TRUE,  TRUE, TRUE),
  TOPLEVEL ("pika-file-open-location-dialog",
            dialogs_file_open_location_new, FALSE, TRUE, FALSE),
  TOPLEVEL ("pika-file-save-dialog",
            dialogs_file_save_new,          FALSE, TRUE, TRUE),
  TOPLEVEL ("pika-file-export-dialog",
            dialogs_file_export_new,        FALSE, TRUE, TRUE),

  /*  singleton toplevels  */
  TOPLEVEL ("pika-preferences-dialog",
            dialogs_preferences_get,        TRUE, TRUE,  TRUE),
  TOPLEVEL ("pika-input-devices-dialog",
            dialogs_input_devices_get,      TRUE, TRUE,  FALSE),
  TOPLEVEL ("pika-keyboard-shortcuts-dialog",
            dialogs_keyboard_shortcuts_get, TRUE, TRUE,  TRUE),
  TOPLEVEL ("pika-module-dialog",
            dialogs_module_get,             TRUE, TRUE,  TRUE),
  TOPLEVEL ("pika-palette-import-dialog",
            dialogs_palette_import_get,     TRUE, TRUE,  TRUE),
  TOPLEVEL ("pika-tips-dialog",
            dialogs_tips_get,               TRUE, FALSE, FALSE),
  TOPLEVEL ("pika-welcome-dialog",
            dialogs_welcome_get,            TRUE, FALSE, FALSE),
  TOPLEVEL ("pika-about-dialog",
            dialogs_about_get,              TRUE, FALSE, FALSE),
  TOPLEVEL ("pika-action-search-dialog",
            dialogs_action_search_get,      TRUE, TRUE,  TRUE),
  TOPLEVEL ("pika-error-dialog",
            dialogs_error_get,              TRUE, FALSE, FALSE),
  TOPLEVEL ("pika-critical-dialog",
            dialogs_critical_get,           TRUE, FALSE, FALSE),
  TOPLEVEL ("pika-close-all-dialog",
            dialogs_close_all_get,          TRUE, FALSE, FALSE),
  TOPLEVEL ("pika-quit-dialog",
            dialogs_quit_get,               TRUE, FALSE, FALSE),
  TOPLEVEL ("pika-extensions-dialog",
            dialogs_extensions_get,         TRUE, TRUE,  TRUE),

  /*  docks  */
  DOCK ("pika-dock",
        dialogs_dock_new),
  DOCK ("pika-toolbox",
        dialogs_toolbox_new),

  /*  dock windows  */
  DOCK_WINDOW ("pika-dock-window",
               dialogs_dock_window_new),
  DOCK_WINDOW ("pika-toolbox-window",
               dialogs_toolbox_dock_window_new),

  /*  singleton dockables  */
  DOCKABLE ("pika-tool-options",
            N_("Tool Options"), NULL, PIKA_ICON_DIALOG_TOOL_OPTIONS,
            PIKA_HELP_TOOL_OPTIONS_DIALOG,
            dialogs_tool_options_new, 0, TRUE),
  DOCKABLE ("pika-device-status",
            N_("Devices"), N_("Device Status"), PIKA_ICON_DIALOG_DEVICE_STATUS,
            PIKA_HELP_DEVICE_STATUS_DIALOG,
            dialogs_device_status_new, 0, TRUE),
  DOCKABLE ("pika-error-console",
            N_("Errors"), N_("Error Console"), PIKA_ICON_DIALOG_WARNING,
            PIKA_HELP_ERRORS_DIALOG,
            dialogs_error_console_new, 0, TRUE),
  DOCKABLE ("pika-cursor-view",
            N_("Pointer"), N_("Pointer Information"), PIKA_ICON_CURSOR,
            PIKA_HELP_POINTER_INFO_DIALOG,
            dialogs_cursor_view_new, 0, TRUE),
  DOCKABLE ("pika-dashboard",
            N_("Dashboard"), N_("Dashboard"), PIKA_ICON_DIALOG_DASHBOARD,
            PIKA_HELP_DASHBOARD_DIALOG,
            dialogs_dashboard_new, 0, TRUE),

  /*  list & grid views  */
  LISTGRID (image, image,
            N_("Images"), NULL, PIKA_ICON_DIALOG_IMAGES,
            PIKA_HELP_IMAGE_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (brush, brush,
            N_("Brushes"), NULL, PIKA_ICON_BRUSH,
            PIKA_HELP_BRUSH_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (dynamics, dynamics,
            N_("Paint Dynamics"), NULL, PIKA_ICON_DYNAMICS,
            PIKA_HELP_DYNAMICS_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (mypaint-brush, mypaint_brush,
            N_("MyPaint Brushes"), NULL, PIKA_ICON_MYPAINT_BRUSH,
            PIKA_HELP_MYPAINT_BRUSH_DIALOG, PIKA_VIEW_SIZE_LARGE),
  LISTGRID (pattern, pattern,
            N_("Patterns"), NULL, PIKA_ICON_PATTERN,
            PIKA_HELP_PATTERN_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (gradient, gradient,
            N_("Gradients"), NULL, PIKA_ICON_GRADIENT,
            PIKA_HELP_GRADIENT_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (palette, palette,
            N_("Palettes"), NULL, PIKA_ICON_PALETTE,
            PIKA_HELP_PALETTE_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (font, font,
            N_("Fonts"), NULL, PIKA_ICON_FONT,
            PIKA_HELP_FONT_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (buffer, buffer,
            N_("Buffers"), NULL, PIKA_ICON_BUFFER,
            PIKA_HELP_BUFFER_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (tool-preset, tool_preset,
            N_("Tool Presets"), NULL, PIKA_ICON_TOOL_PRESET,
            PIKA_HELP_TOOL_PRESET_DIALOG, PIKA_VIEW_SIZE_MEDIUM),
  LISTGRID (document, document,
            N_("History"), N_("Document History"), PIKA_ICON_DOCUMENT_OPEN_RECENT,
            PIKA_HELP_DOCUMENT_DIALOG, PIKA_VIEW_SIZE_LARGE),
  LISTGRID (template, template,
            N_("Templates"), N_("Image Templates"), PIKA_ICON_TEMPLATE,
            PIKA_HELP_TEMPLATE_DIALOG, PIKA_VIEW_SIZE_SMALL),

  /*  image related  */
  DOCKABLE ("pika-layer-list",
            N_("Layers"), NULL, PIKA_ICON_DIALOG_LAYERS,
            PIKA_HELP_LAYER_DIALOG,
            dialogs_layer_list_view_new, 0, FALSE),
  DOCKABLE ("pika-channel-list",
            N_("Channels"), NULL, PIKA_ICON_DIALOG_CHANNELS,
            PIKA_HELP_CHANNEL_DIALOG,
            dialogs_channel_list_view_new, 0, FALSE),
  DOCKABLE ("pika-vectors-list",
            N_("Paths"), NULL, PIKA_ICON_DIALOG_PATHS,
            PIKA_HELP_PATH_DIALOG,
            dialogs_vectors_list_view_new, 0, FALSE),
  DOCKABLE ("pika-indexed-palette",
            N_("Colormap"), NULL, PIKA_ICON_COLORMAP,
            PIKA_HELP_INDEXED_PALETTE_DIALOG,
            dialogs_colormap_editor_new, 0, FALSE),
  DOCKABLE ("pika-histogram-editor",
            N_("Histogram"), NULL, PIKA_ICON_HISTOGRAM,
            PIKA_HELP_HISTOGRAM_DIALOG,
            dialogs_histogram_editor_new, 0, FALSE),
  DOCKABLE ("pika-selection-editor",
            N_("Selection"), N_("Selection Editor"), PIKA_ICON_SELECTION,
            PIKA_HELP_SELECTION_DIALOG,
            dialogs_selection_editor_new, 0, FALSE),
  DOCKABLE ("pika-symmetry-editor",
            N_("Symmetry Painting"), NULL, PIKA_ICON_SYMMETRY,
            PIKA_HELP_SYMMETRY_DIALOG,
            dialogs_symmetry_editor_new, 0, FALSE),
  DOCKABLE ("pika-undo-history",
            N_("Undo"), N_("Undo History"), PIKA_ICON_DIALOG_UNDO_HISTORY,
            PIKA_HELP_UNDO_DIALOG,
            dialogs_undo_editor_new, 0, FALSE),
  DOCKABLE ("pika-sample-point-editor",
            N_("Sample Points"), N_("Sample Points"), PIKA_ICON_SAMPLE_POINT,
            PIKA_HELP_SAMPLE_POINT_DIALOG,
            dialogs_sample_point_editor_new, 0, FALSE),

  /*  display related  */
  DOCKABLE ("pika-navigation-view",
            N_("Navigation"), N_("Display Navigation"), PIKA_ICON_DIALOG_NAVIGATION,
            PIKA_HELP_NAVIGATION_DIALOG,
            dialogs_navigation_editor_new, 0, FALSE),

  /*  editors  */
  DOCKABLE ("pika-color-editor",
            N_("FG/BG"), N_("FG/BG Color"), PIKA_ICON_COLORS_DEFAULT,
            PIKA_HELP_COLOR_DIALOG,
            dialogs_color_editor_new, 0, FALSE),

  /*  singleton editors  */
  DOCKABLE ("pika-brush-editor",
            N_("Brush Editor"), NULL, PIKA_ICON_BRUSH,
            PIKA_HELP_BRUSH_EDITOR_DIALOG,
            dialogs_brush_editor_get, 0, TRUE),
  DOCKABLE ("pika-dynamics-editor",
            N_("Paint Dynamics Editor"), NULL, PIKA_ICON_DYNAMICS,
            PIKA_HELP_DYNAMICS_EDITOR_DIALOG,
            dialogs_dynamics_editor_get, 0, TRUE),
  DOCKABLE ("pika-gradient-editor",
            N_("Gradient Editor"), NULL, PIKA_ICON_GRADIENT,
            PIKA_HELP_GRADIENT_EDITOR_DIALOG,
            dialogs_gradient_editor_get, 0, TRUE),
  DOCKABLE ("pika-palette-editor",
            N_("Palette Editor"), NULL, PIKA_ICON_PALETTE,
            PIKA_HELP_PALETTE_EDITOR_DIALOG,
            dialogs_palette_editor_get, 0, TRUE),
  DOCKABLE ("pika-tool-preset-editor",
            N_("Tool Preset Editor"), NULL, PIKA_ICON_TOOL_PRESET,
            PIKA_HELP_TOOL_PRESET_EDITOR_DIALOG,
            dialogs_tool_preset_editor_get, 0, TRUE),

  /*  image windows  */
  IMAGE_WINDOW ("pika-empty-image-window",
                TRUE, TRUE),
  IMAGE_WINDOW ("pika-single-image-window",
                TRUE, TRUE)
};

/**
 * dialogs_restore_dialog:
 * @factory:
 * @screen:
 * @info:
 *
 * Creates a top level widget based on the given session info object
 * in which other widgets later can be be put, typically also restored
 * from the same session info object.
 *
 * Returns:
 **/
static GtkWidget *
dialogs_restore_dialog (PikaDialogFactory *factory,
                        GdkMonitor        *monitor,
                        PikaSessionInfo   *info)
{
  GtkWidget        *dialog;
  Pika             *pika    = pika_dialog_factory_get_context (factory)->pika;
  PikaCoreConfig   *config  = pika->config;
  PikaDisplay      *display = pika_context_get_display (pika_get_user_context (pika));
  PikaDisplayShell *shell   = pika_display_get_shell (display);

  PIKA_LOG (DIALOG_FACTORY, "restoring toplevel \"%s\" (info %p)",
            pika_session_info_get_factory_entry (info)->identifier,
            info);

  dialog =
    pika_dialog_factory_dialog_new (factory, monitor,
                                    NULL /*ui_manager*/,
                                    GTK_WIDGET (pika_display_shell_get_window (shell)),
                                    pika_session_info_get_factory_entry (info)->identifier,
                                    pika_session_info_get_factory_entry (info)->view_size,
                                    ! PIKA_GUI_CONFIG (config)->hide_docks);

  g_object_set_data (G_OBJECT (dialog), PIKA_DIALOG_VISIBILITY_KEY,
                     GINT_TO_POINTER (PIKA_GUI_CONFIG (config)->hide_docks ?
                                      PIKA_DIALOG_VISIBILITY_HIDDEN :
                                      PIKA_DIALOG_VISIBILITY_VISIBLE));

  return dialog;
}

/**
 * dialogs_restore_window:
 * @factory:
 * @monitor:
 * @info:
 *
 * "restores" the image window. We don't really restore anything since
 * the image window is created earlier, so we just look for and return
 * the already-created image window.
 *
 * Returns:
 **/
static GtkWidget *
dialogs_restore_window (PikaDialogFactory *factory,
                        GdkMonitor        *monitor,
                        PikaSessionInfo   *info)
{
  Pika             *pika    = pika_dialog_factory_get_context (factory)->pika;
  PikaDisplay      *display = PIKA_DISPLAY (pika_get_empty_display (pika));
  PikaDisplayShell *shell   = pika_display_get_shell (display);
  GtkWidget        *dialog;

  dialog = GTK_WIDGET (pika_display_shell_get_window (shell));

  return dialog;
}


/*  public functions  */

void
dialogs_init (Pika *pika)
{
  PikaDialogFactory *factory = NULL;
  gint               i       = 0;

  g_return_if_fail (PIKA_IS_PIKA (pika));

  factory = pika_dialog_factory_new ("toplevel", pika_get_user_context (pika));
  pika_dialog_factory_set_singleton (factory);

  for (i = 0; i < G_N_ELEMENTS (entries); i++)
    pika_dialog_factory_register_entry (factory,
                                        entries[i].identifier,
                                        entries[i].name ? gettext(entries[i].name) : NULL,
                                        entries[i].blurb ? gettext(entries[i].blurb) : NULL,
                                        entries[i].icon_name,
                                        entries[i].help_id,
                                        entries[i].new_func,
                                        entries[i].restore_func,
                                        entries[i].view_size,
                                        entries[i].singleton,
                                        entries[i].session_managed,
                                        entries[i].remember_size,
                                        entries[i].remember_if_open,
                                        entries[i].hideable,
                                        entries[i].image_window,
                                        entries[i].dockable);

  global_recent_docks = pika_list_new (PIKA_TYPE_SESSION_INFO, FALSE);
}

void
dialogs_exit (Pika *pika)
{
  g_return_if_fail (PIKA_IS_PIKA (pika));

  if (pika_dialog_factory_get_singleton ())
    {
      /* run dispose manually so the factory destroys its dialogs, which
       * might in turn directly or indirectly ref the factory
       */
      g_object_run_dispose (G_OBJECT (pika_dialog_factory_get_singleton ()));

      g_object_unref (pika_dialog_factory_get_singleton ());
      pika_dialog_factory_set_singleton (NULL);
    }

  g_clear_object (&global_recent_docks);
}

static void
dialogs_ensure_factory_entry_on_recent_dock (PikaSessionInfo *info)
{
  if (! pika_session_info_get_factory_entry (info))
    {
      PikaDialogFactoryEntry *entry = NULL;

      /* The recent docks container only contains session infos for
       * dock windows
       */
      entry = pika_dialog_factory_find_entry (pika_dialog_factory_get_singleton (),
                                              "pika-dock-window");

      pika_session_info_set_factory_entry (info, entry);
    }
}

static GFile *
dialogs_get_dockrc_file (void)
{
  const gchar *basename;

  basename = g_getenv ("PIKA_TESTING_DOCKRC_NAME");
  if (! basename)
    basename = "dockrc";

  return pika_directory_file (basename, NULL);
}

void
dialogs_load_recent_docks (Pika *pika)
{
  GFile  *file;
  GError *error = NULL;

  g_return_if_fail (PIKA_IS_PIKA (pika));

  file = dialogs_get_dockrc_file ();

  if (pika->be_verbose)
    g_print ("Parsing '%s'\n", pika_file_get_utf8_name (file));

  if (! pika_config_deserialize_file (PIKA_CONFIG (global_recent_docks),
                                      file,
                                      NULL, &error))
    {
      if (error->code != PIKA_CONFIG_ERROR_OPEN_ENOENT)
        pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);

      g_clear_error (&error);
    }

  g_object_unref (file);

  /* In PIKA 2.6 dockrc did not contain the factory entries for the
   * session infos, so set that up manually if needed
   */
  pika_container_foreach (global_recent_docks,
                          (GFunc) dialogs_ensure_factory_entry_on_recent_dock,
                          NULL);

  pika_list_reverse (PIKA_LIST (global_recent_docks));
}

void
dialogs_save_recent_docks (Pika *pika)
{
  GFile  *file;
  GError *error = NULL;

  g_return_if_fail (PIKA_IS_PIKA (pika));

  file = dialogs_get_dockrc_file ();

  if (pika->be_verbose)
    g_print ("Writing '%s'\n", pika_file_get_utf8_name (file));

  if (! pika_config_serialize_to_file (PIKA_CONFIG (global_recent_docks),
                                       file,
                                       "recently closed docks",
                                       "end of recently closed docks",
                                       NULL, &error))
    {
      pika_message_literal (pika, NULL, PIKA_MESSAGE_ERROR, error->message);
      g_clear_error (&error);
    }

  g_object_unref (file);
}

GtkWidget *
dialogs_get_toolbox (void)
{
  GList *list;

  g_return_val_if_fail (PIKA_IS_DIALOG_FACTORY (pika_dialog_factory_get_singleton ()), NULL);

  for (list = pika_dialog_factory_get_open_dialogs (pika_dialog_factory_get_singleton ());
       list;
       list = g_list_next (list))
    {
      if (PIKA_IS_DOCK_WINDOW (list->data) &&
          pika_dock_window_has_toolbox (list->data))
        return list->data;
    }

  return NULL;
}

GtkWidget *
dialogs_get_dialog (GObject     *attach_object,
                    const gchar *attach_key)
{
  g_return_val_if_fail (G_IS_OBJECT (attach_object), NULL);
  g_return_val_if_fail (attach_key != NULL, NULL);

  return g_object_get_data (attach_object, attach_key);
}

void
dialogs_attach_dialog (GObject     *attach_object,
                       const gchar *attach_key,
                       GtkWidget   *dialog)
{
  g_return_if_fail (G_IS_OBJECT (attach_object));
  g_return_if_fail (attach_key != NULL);
  g_return_if_fail (GTK_IS_WIDGET (dialog));

  g_object_set_data (attach_object, attach_key, dialog);
  g_object_set_data (G_OBJECT (dialog), "pika-dialogs-attach-key",
                     (gpointer) attach_key);

  g_signal_connect_object (dialog, "destroy",
                           G_CALLBACK (dialogs_detach_dialog),
                           attach_object,
                           G_CONNECT_SWAPPED);
}

void
dialogs_detach_dialog (GObject   *attach_object,
                       GtkWidget *dialog)
{
  const gchar *attach_key;

  g_return_if_fail (G_IS_OBJECT (attach_object));
  g_return_if_fail (GTK_IS_WIDGET (dialog));

  attach_key = g_object_get_data (G_OBJECT (dialog),
                                  "pika-dialogs-attach-key");

  g_return_if_fail (attach_key != NULL);

  g_object_set_data (attach_object, attach_key, NULL);

  g_signal_handlers_disconnect_by_func (dialog,
                                        dialogs_detach_dialog,
                                        attach_object);
}

void
dialogs_destroy_dialog (GObject     *attach_object,
                        const gchar *attach_key)
{
  GtkWidget *dialog;

  g_return_if_fail (G_IS_OBJECT (attach_object));
  g_return_if_fail (attach_key != NULL);

  dialog = g_object_get_data (attach_object, attach_key);

  if (dialog)
    gtk_widget_destroy (dialog);
}
