gimpdialogfactory.c 45.1 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
Michael Natterer's avatar
Michael Natterer committed
4
 * gimpdialogfactory.c
5
 * Copyright (C) 2001 Michael Natterer <mitch@gimp.org>
Michael Natterer's avatar
Michael Natterer committed
6
 *
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"

24 25
#include <stdlib.h>
#include <string.h>
Hans Breuer's avatar
updated  
Hans Breuer committed
26

27 28 29 30
#include <gtk/gtk.h>

#include "libgimpwidgets/gimpwidgets.h"

31
#include "widgets-types.h"
32

33 34
#include "core/gimpcontext.h"

Michael Natterer's avatar
Michael Natterer committed
35
#include "gimpcursor.h"
36
#include "gimpdialogfactory.h"
37
#include "gimpdock.h"
38 39
#include "gimpdockbook.h"
#include "gimpdockable.h"
40
#include "gimpmenufactory.h"
41
#include "gimpsessioninfo.h"
42

43
#include "gimp-log.h"
Michael Natterer's avatar
Michael Natterer committed
44 45


46 47
static void   gimp_dialog_factory_dispose             (GObject           *object);
static void   gimp_dialog_factory_finalize            (GObject           *object);
Michael Natterer's avatar
Michael Natterer committed
48

49 50 51 52
static GtkWidget *
              gimp_dialog_factory_default_constructor (GimpDialogFactory *factory,
                                                       GimpDialogFactoryEntry *entry,
                                                       GimpContext       *context,
53
                                                       gint               view_size);
54 55 56
static void     gimp_dialog_factory_set_widget_data   (GtkWidget         *dialog,
                                                       GimpDialogFactory *factory,
                                                       GimpDialogFactoryEntry *entry);
57 58 59
static gboolean gimp_dialog_factory_set_user_pos      (GtkWidget         *dialog,
                                                       GdkEventConfigure *cevent,
                                                       gpointer           data);
60 61 62
static gboolean gimp_dialog_factory_dialog_configure  (GtkWidget         *dialog,
                                                       GdkEventConfigure *cevent,
                                                       GimpDialogFactory *factory);
63
static void   gimp_dialog_factories_save_foreach      (gconstpointer      key,
64 65
                                                       GimpDialogFactory *factory,
                                                       GimpConfigWriter  *writer);
66
static void   gimp_dialog_factories_restore_foreach   (gconstpointer      key,
67 68
                                                       GimpDialogFactory *factory,
                                                       gpointer           data);
69
static void   gimp_dialog_factories_clear_foreach     (gconstpointer      key,
70 71
                                                       GimpDialogFactory *factory,
                                                       gpointer           data);
72
static void   gimp_dialog_factories_hide_foreach      (gconstpointer      key,
73 74
                                                       GimpDialogFactory *factory,
                                                       gpointer           data);
75
static void   gimp_dialog_factories_show_foreach      (gconstpointer      key,
76 77
                                                       GimpDialogFactory *factory,
                                                       gpointer           data);
78
static void   gimp_dialog_factories_set_busy_foreach  (gconstpointer      key,
79 80
                                                       GimpDialogFactory *factory,
                                                       gpointer           data);
81
static void   gimp_dialog_factories_unset_busy_foreach(gconstpointer      key,
82 83
                                                       GimpDialogFactory *factory,
                                                       gpointer           data);
84

Sven Neumann's avatar
Sven Neumann committed
85 86 87
static GtkWidget *
              gimp_dialog_factory_get_toolbox     (GimpDialogFactory *toolbox_factory);

88

89
G_DEFINE_TYPE (GimpDialogFactory, gimp_dialog_factory, GIMP_TYPE_OBJECT)
90

91
#define parent_class gimp_dialog_factory_parent_class
92

93 94
static gboolean dialogs_shown = TRUE;  /* FIXME */

95 96 97 98

static void
gimp_dialog_factory_class_init (GimpDialogFactoryClass *klass)
{
99
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
100

101
  object_class->dispose  = gimp_dialog_factory_dispose;
Michael Natterer's avatar
Michael Natterer committed
102
  object_class->finalize = gimp_dialog_factory_finalize;
103 104

  klass->factories = g_hash_table_new (g_str_hash, g_str_equal);
105 106 107 108 109
}

static void
gimp_dialog_factory_init (GimpDialogFactory *factory)
{
110
  factory->menu_factory       = NULL;
Michael Natterer's avatar
Michael Natterer committed
111
  factory->new_dock_func      = NULL;
112
  factory->constructor        = gimp_dialog_factory_default_constructor;
113
  factory->registered_dialogs = NULL;
Michael Natterer's avatar
Michael Natterer committed
114
  factory->session_infos      = NULL;
115
  factory->open_dialogs       = NULL;
116 117
}

118 119 120
static void
gimp_dialog_factory_dispose (GObject *object)
{
121
  GimpDialogFactory *factory = GIMP_DIALOG_FACTORY (object);
122
  GList             *list;
123
  gpointer           key;
124

125 126 127 128
  /*  start iterating from the beginning each time we destroyed a
   *  toplevel because destroying a dock may cause lots of items
   *  to be removed from factory->open_dialogs
   */
129 130 131 132 133 134 135 136 137 138 139
  while (factory->open_dialogs)
    {
      for (list = factory->open_dialogs; list; list = g_list_next (list))
        {
          if (GTK_WIDGET_TOPLEVEL (list->data))
            {
              gtk_widget_destroy (GTK_WIDGET (list->data));
              break;
            }
        }

140 141 142
      /*  the list being non-empty without any toplevel is an error,
       *  so eek and chain up
       */
143 144
      if (! list)
        {
145
          g_warning ("%s: stale non-toplevel entries in factory->open_dialogs",
146
                     G_STRFUNC);
147 148 149 150
          break;
        }
    }

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
  if (factory->open_dialogs)
    {
      g_list_free (factory->open_dialogs);
      factory->open_dialogs = NULL;
    }

  if (factory->session_infos)
    {
      g_list_foreach (factory->session_infos, (GFunc) gimp_session_info_free,
                      NULL);
      g_list_free (factory->session_infos);
      factory->session_infos = NULL;
    }

  if (strcmp (GIMP_OBJECT (factory)->name, "toolbox") == 0)
    key = "";
  else
    key = GIMP_OBJECT (factory)->name;

  g_hash_table_remove (GIMP_DIALOG_FACTORY_GET_CLASS (object)->factories,
                       key);

173 174 175
  G_OBJECT_CLASS (parent_class)->dispose (object);
}

176
static void
Michael Natterer's avatar
Michael Natterer committed
177
gimp_dialog_factory_finalize (GObject *object)
178
{
179
  GimpDialogFactory *factory = GIMP_DIALOG_FACTORY (object);
180 181 182 183
  GList             *list;

  for (list = factory->registered_dialogs; list; list = g_list_next (list))
    {
184
      GimpDialogFactoryEntry *entry = list->data;
185 186

      g_free (entry->identifier);
187 188 189 190
      g_free (entry->name);
      g_free (entry->blurb);
      g_free (entry->stock_id);
      g_free (entry->help_id);
191 192

      g_slice_free (GimpDialogFactoryEntry, entry);
193 194
    }

Michael Natterer's avatar
Michael Natterer committed
195 196 197 198 199
  if (factory->registered_dialogs)
    {
      g_list_free (factory->registered_dialogs);
      factory->registered_dialogs = NULL;
    }
200

Michael Natterer's avatar
Michael Natterer committed
201
  G_OBJECT_CLASS (parent_class)->finalize (object);
202 203 204
}

GimpDialogFactory *
Michael Natterer's avatar
Michael Natterer committed
205
gimp_dialog_factory_new (const gchar       *name,
206 207 208
                         GimpContext       *context,
                         GimpMenuFactory   *menu_factory,
                         GimpDialogNewFunc  new_dock_func)
209
{
210 211
  GimpDialogFactory *factory;
  gpointer           key;
212 213

  g_return_val_if_fail (name != NULL, NULL);
214
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
215
  g_return_val_if_fail (! menu_factory || GIMP_IS_MENU_FACTORY (menu_factory),
216
                        NULL);
217

218
  if (gimp_dialog_factory_from_name (name))
219
    {
220
      g_warning ("%s: dialog factory \"%s\" already exists",
221
                 G_STRFUNC, name);
222 223
      return NULL;
    }
224

Michael Natterer's avatar
Michael Natterer committed
225
  factory = g_object_new (GIMP_TYPE_DIALOG_FACTORY, NULL);
226

227 228
  gimp_object_set_name (GIMP_OBJECT (factory), name);

229 230 231 232 233 234 235
  /*  hack to keep the toolbox on the pool position  */
  if (strcmp (name, "toolbox") == 0)
    key = "";
  else
    key = GIMP_OBJECT (factory)->name;

  g_hash_table_insert (GIMP_DIALOG_FACTORY_GET_CLASS (factory)->factories,
236
                       key, factory);
237

Michael Natterer's avatar
Michael Natterer committed
238
  factory->context       = context;
239
  factory->menu_factory  = menu_factory;
Michael Natterer's avatar
Michael Natterer committed
240
  factory->new_dock_func = new_dock_func;
241 242 243 244

  return factory;
}

245 246 247 248 249 250 251 252
GimpDialogFactory *
gimp_dialog_factory_from_name (const gchar *name)
{
  GimpDialogFactoryClass *factory_class;
  GimpDialogFactory      *factory;

  g_return_val_if_fail (name != NULL, NULL);

253
  factory_class = g_type_class_peek (GIMP_TYPE_DIALOG_FACTORY);
254 255
  if (! factory_class)
    return NULL;
256

257 258 259
  /*  hack to keep the toolbox on the pool position  */
  if (strcmp (name, "toolbox") == 0)
    name = "";
260

261 262
  factory =
    (GimpDialogFactory *) g_hash_table_lookup (factory_class->factories, name);
263 264 265 266

  return factory;
}

267 268 269 270 271 272 273 274 275 276 277 278
void
gimp_dialog_factory_set_constructor (GimpDialogFactory     *factory,
                                     GimpDialogConstructor  constructor)
{
  g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));

  if (! constructor)
    constructor = gimp_dialog_factory_default_constructor;

  factory->constructor = constructor;
}

279
void
280
gimp_dialog_factory_register_entry (GimpDialogFactory *factory,
281
                                    const gchar       *identifier,
282 283 284 285
                                    const gchar       *name,
                                    const gchar       *blurb,
                                    const gchar       *stock_id,
                                    const gchar       *help_id,
286
                                    GimpDialogNewFunc  new_func,
287
                                    gint               view_size,
288 289 290 291
                                    gboolean           singleton,
                                    gboolean           session_managed,
                                    gboolean           remember_size,
                                    gboolean           remember_if_open)
292 293 294 295 296 297
{
  GimpDialogFactoryEntry *entry;

  g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
  g_return_if_fail (identifier != NULL);

298
  entry = g_slice_new0 (GimpDialogFactoryEntry);
299

300
  entry->identifier       = g_strdup (identifier);
301 302 303 304
  entry->name             = g_strdup (name);
  entry->blurb            = g_strdup (blurb);
  entry->stock_id         = g_strdup (stock_id);
  entry->help_id          = g_strdup (help_id);
305
  entry->new_func         = new_func;
306
  entry->view_size        = view_size;
307 308 309 310
  entry->singleton        = singleton ? TRUE : FALSE;
  entry->session_managed  = session_managed ? TRUE : FALSE;
  entry->remember_size    = remember_size ? TRUE : FALSE;
  entry->remember_if_open = remember_if_open ? TRUE : FALSE;
311 312

  factory->registered_dialogs = g_list_prepend (factory->registered_dialogs,
313
                                                entry);
314 315
}

316 317
GimpDialogFactoryEntry *
gimp_dialog_factory_find_entry (GimpDialogFactory *factory,
318
                                const gchar       *identifier)
319 320 321 322 323 324 325 326
{
  GList *list;

  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
  g_return_val_if_fail (identifier != NULL, NULL);

  for (list = factory->registered_dialogs; list; list = g_list_next (list))
    {
327
      GimpDialogFactoryEntry *entry = list->data;
328 329

      if (! strcmp (identifier, entry->identifier))
330
        return entry;
331 332 333 334 335 336 337
    }

  return NULL;
}

GimpSessionInfo *
gimp_dialog_factory_find_session_info (GimpDialogFactory *factory,
338
                                       const gchar       *identifier)
339 340 341 342 343 344 345 346
{
  GList *list;

  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
  g_return_val_if_fail (identifier != NULL, NULL);

  for (list = factory->session_infos; list; list = g_list_next (list))
    {
347
      GimpSessionInfo *info = list->data;
348

Michael Natterer's avatar
Michael Natterer committed
349
      if ((info->toplevel_entry &&
350 351 352 353 354 355
           ! strcmp (identifier, info->toplevel_entry->identifier)) ||
          (info->dockable_entry &&
           ! strcmp (identifier, info->dockable_entry->identifier)))
        {
          return info;
        }
356 357 358 359 360
    }

  return NULL;
}

Michael Natterer's avatar
Michael Natterer committed
361 362
static GtkWidget *
gimp_dialog_factory_dialog_new_internal (GimpDialogFactory *factory,
363
                                         GdkScreen         *screen,
364 365
                                         GimpContext       *context,
                                         const gchar       *identifier,
366
                                         gint               view_size,
367 368
                                         gboolean           return_existing,
                                         gboolean           present)
369 370 371 372 373 374 375 376 377 378 379
{
  GimpDialogFactoryEntry *entry;
  GtkWidget              *dialog = NULL;

  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
  g_return_val_if_fail (identifier != NULL, NULL);

  entry = gimp_dialog_factory_find_entry (factory, identifier);

  if (! entry)
    {
380
      g_warning ("%s: no entry registered for \"%s\"",
381
                 G_STRFUNC, identifier);
382 383 384 385 386
      return NULL;
    }

  if (! entry->new_func)
    {
387
      g_warning ("%s: entry for \"%s\" has no constructor",
388
                 G_STRFUNC, identifier);
389 390 391
      return NULL;
    }

392 393
  /*  a singleton dialog is always returned if it already exisits  */
  if (return_existing || entry->singleton)
394 395 396 397 398 399
    {
      GimpSessionInfo *info;

      info = gimp_dialog_factory_find_session_info (factory, identifier);

      if (info)
400
        dialog = info->widget;
401
    }
402

403
  /*  create the dialog if it was not found  */
404 405
  if (! dialog)
    {
Michael Natterer's avatar
Michael Natterer committed
406 407
      GtkWidget *dock = NULL;

408 409 410 411 412 413
      /*  If the dialog will be a dockable (factory->new_dock_func) and
       *  we are called from gimp_dialog_factory_dialog_raise() (! context),
       *  create a new dock _before_ creating the dialog.
       *  We do this because the new dockable needs to be created in it's
       *  dock's context.
       */
Michael Natterer's avatar
Michael Natterer committed
414
      if (factory->new_dock_func && ! context)
415
        {
416 417
          GtkWidget *dockbook;

418
          dock     = gimp_dialog_factory_dock_new (factory, screen);
419
          dockbook = gimp_dockbook_new (factory->menu_factory);
Michael Natterer's avatar
Michael Natterer committed
420

421 422
          gimp_dock_add_book (GIMP_DOCK (dock),
                              GIMP_DOCKBOOK (dockbook),
423
                              0);
424
        }
Michael Natterer's avatar
Michael Natterer committed
425

426 427 428 429 430 431
      /*  Create the new dialog in the appropriate context which is
       *  - the passed context if not NULL
       *  - the newly created dock's context if we just created it
       *  - the factory's context, which happens when raising a toplevel
       *    dialog was the original request.
       */
432 433
      if (view_size < GIMP_VIEW_SIZE_TINY)
        view_size = entry->view_size;
434

Michael Natterer's avatar
Michael Natterer committed
435
      if (context)
436 437
        dialog = factory->constructor (factory, entry,
                                       context,
438
                                       view_size);
Michael Natterer's avatar
Michael Natterer committed
439
      else if (dock)
440 441
        dialog = factory->constructor (factory, entry,
                                       GIMP_DOCK (dock)->context,
442
                                       view_size);
Michael Natterer's avatar
Michael Natterer committed
443
      else
444 445
        dialog = factory->constructor (factory, entry,
                                       factory->context,
446
                                       view_size);
447

448
      if (dialog)
449
        {
450
          gimp_dialog_factory_set_widget_data (dialog, factory, entry);
451

452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
          /*  If we created a dock before, the newly created dialog is
           *  supposed to be a GimpDockable.
           */
          if (dock)
            {
              if (GIMP_IS_DOCKABLE (dialog))
                {
                  gimp_dock_add (GIMP_DOCK (dock), GIMP_DOCKABLE (dialog),
                                 0, 0);

                  gtk_widget_show (dock);
                }
              else
                {
                  g_warning ("%s: GimpDialogFactory is a dockable factory "
                             "but constructor for \"%s\" did not return a "
                             "GimpDockable",
                             G_STRFUNC, identifier);

                  gtk_widget_destroy (dialog);
472
                  gtk_widget_destroy (dock);
Michael Natterer's avatar
Michael Natterer committed
473

474
                  dialog = NULL;
475
                  dock   = NULL;
476 477 478
                }
            }
        }
Michael Natterer's avatar
Michael Natterer committed
479
      else if (dock)
480 481 482
        {
          g_warning ("%s: constructor for \"%s\" returned NULL",
                     G_STRFUNC, identifier);
483

484
          gtk_widget_destroy (dock);
485

486 487
          dock = NULL;
        }
488 489

      if (dialog)
490
        gimp_dialog_factory_add_dialog (factory, dialog);
491 492
    }

493
  /*  Finally, if we found an existing dialog or created a new one, raise it.
494
   */
495 496 497 498
  if (! dialog)
    return NULL;

  if (GTK_WIDGET_TOPLEVEL (dialog))
499
    {
500
      gtk_window_set_screen (GTK_WINDOW (dialog), screen);
501

502 503 504 505 506 507
      if (present)
        gtk_window_present (GTK_WINDOW (dialog));
    }
  else if (GIMP_IS_DOCKABLE (dialog))
    {
      GimpDockable *dockable = GIMP_DOCKABLE (dialog);
Michael Natterer's avatar
Michael Natterer committed
508

509 510 511 512
      if (dockable->dockbook && dockable->dockbook->dock)
        {
          GtkNotebook *notebook = GTK_NOTEBOOK (dockable->dockbook);
          gint         num      = gtk_notebook_page_num (notebook, dialog);
Michael Natterer's avatar
Michael Natterer committed
513

514
          if (num != -1)
515
            {
516
              gtk_notebook_set_current_page (notebook, num);
Michael Natterer's avatar
Michael Natterer committed
517

518 519 520
              gimp_dockable_blink (dockable);
            }
        }
Michael Natterer's avatar
Michael Natterer committed
521

522 523 524
      if (present)
        {
          GtkWidget *toplevel = gtk_widget_get_toplevel (dialog);
525

526 527
          if (GTK_IS_WINDOW (toplevel))
            gtk_window_present (GTK_WINDOW (toplevel));
528
        }
529 530 531
    }

  return dialog;
532
}
533

534 535
/**
 * gimp_dialog_factory_dialog_new:
536 537 538 539
 * @factory:      a #GimpDialogFactory
 * @screen:       the #GdkScreen the dialog should appear on
 * @identifier:   the identifier of the dialog as registered with
 *                gimp_dialog_factory_register_entry()
540
 * @view_size:
541
 * @present:      whether gtk_window_present() should be called
542
 *
543 544 545 546 547 548
 * Creates a new toplevel dialog or a #GimpDockable, depending on whether
 * %factory is a toplevel of dockable factory.
 *
 * Return value: the newly created dialog or an already existing singleton
 *               dialog.
 **/
Michael Natterer's avatar
Michael Natterer committed
549 550
GtkWidget *
gimp_dialog_factory_dialog_new (GimpDialogFactory *factory,
551
                                GdkScreen         *screen,
552
                                const gchar       *identifier,
553
                                gint               view_size,
554
                                gboolean           present)
Michael Natterer's avatar
Michael Natterer committed
555 556
{
  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
557
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
Michael Natterer's avatar
Michael Natterer committed
558 559 560
  g_return_val_if_fail (identifier != NULL, NULL);

  return gimp_dialog_factory_dialog_new_internal (factory,
561
                                                  screen,
562 563
                                                  factory->context,
                                                  identifier,
564
                                                  view_size,
565
                                                  FALSE,
566
                                                  present);
Michael Natterer's avatar
Michael Natterer committed
567 568
}

569 570
/**
 * gimp_dialog_factory_dialog_raise:
571 572 573 574
 * @factory:      a #GimpDialogFactory
 * @screen:       the #GdkScreen the dialog should appear on
 * @identifiers:  a '|' separated list of identifiers of dialogs as
 *                registered with gimp_dialog_factory_register_entry()
575
 * @view_size:
576
 *
577 578
 * Raises any of a list of already existing toplevel dialog or
 * #GimpDockable if it was already created by this %facory.
579
 *
580 581
 * Implicitly creates the first dialog in the list if none of the dialogs
 * were found.
582
 *
583 584
 * Return value: the raised or newly created dialog.
 **/
Michael Natterer's avatar
Michael Natterer committed
585 586
GtkWidget *
gimp_dialog_factory_dialog_raise (GimpDialogFactory *factory,
587
                                  GdkScreen         *screen,
588
                                  const gchar       *identifiers,
589
                                  gint               view_size)
Michael Natterer's avatar
Michael Natterer committed
590
{
591 592
  GtkWidget *dialog;

Michael Natterer's avatar
Michael Natterer committed
593
  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
594
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
595
  g_return_val_if_fail (identifiers != NULL, NULL);
Michael Natterer's avatar
Michael Natterer committed
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
  /*  If the identifier is a list, try to find a matching dialog and
   *  raise it. If there's no match, use the first list item.
   */
  if (strchr (identifiers, '|'))
    {
      gchar **ids = g_strsplit (identifiers, "|", 0);
      gint    i;

      for (i = 0; ids[i]; i++)
        {
          GimpSessionInfo *info;

          info = gimp_dialog_factory_find_session_info (factory, ids[i]);
          if (info && info->widget)
            break;
        }

      dialog = gimp_dialog_factory_dialog_new_internal (factory,
                                                        screen,
                                                        NULL,
                                                        ids[i] ? ids[i] : ids[0],
618
                                                        view_size,
619
                                                        TRUE,
620 621 622 623 624 625 626 627 628
                                                        TRUE);
      g_strfreev (ids);
    }
  else
    {
      dialog = gimp_dialog_factory_dialog_new_internal (factory,
                                                        screen,
                                                        NULL,
                                                        identifiers,
629
                                                        view_size,
630
                                                        TRUE,
631 632 633 634
                                                        TRUE);
    }

  return dialog;
Michael Natterer's avatar
Michael Natterer committed
635 636
}

637 638
/**
 * gimp_dialog_factory_dockable_new:
639 640 641 642
 * @factory:      a #GimpDialogFactory
 * @dock:         a #GimpDock crated by this %factory.
 * @identifier:   the identifier of the dialog as registered with
 *                gimp_dialog_factory_register_entry()
643
 * @view_size:
644
 *
645 646 647 648 649 650
 * Creates a new #GimpDockable in the context of the #GimpDock it will be
 * added to.
 *
 * Implicitly raises & returns an already existing singleton dockable,
 * so callers should check that dockable->dockbook is NULL before trying
 * to add it to it's #GimpDockbook.
651
 *
652 653 654
 * Return value: the newly created #GimpDockable or an already existing
 *               singleton dockable.
 **/
Michael Natterer's avatar
Michael Natterer committed
655 656
GtkWidget *
gimp_dialog_factory_dockable_new (GimpDialogFactory *factory,
657 658
                                  GimpDock          *dock,
                                  const gchar       *identifier,
659
                                  gint               view_size)
Michael Natterer's avatar
Michael Natterer committed
660 661 662 663 664 665
{
  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
  g_return_val_if_fail (GIMP_IS_DOCK (dock), NULL);
  g_return_val_if_fail (identifier != NULL, NULL);

  return gimp_dialog_factory_dialog_new_internal (factory,
666
                                                  gtk_widget_get_screen (GTK_WIDGET (dock)),
667 668
                                                  dock->context,
                                                  identifier,
669
                                                  view_size,
670
                                                  FALSE,
671
                                                  FALSE);
Michael Natterer's avatar
Michael Natterer committed
672 673
}

674 675 676
/**
 * gimp_dialog_factory_dock_new:
 * @factory: a #GimpDialogFacotry
677
 * @screen:  the #GdkScreen the dock should appear on
678
 *
679 680 681 682
 * Returns a new #GimpDock in this %factory's context. We use a function
 * pointer passed to this %factory's constructor instead of simply
 * gimp_dock_new() because we may want different instances of
 * #GimpDialogFactory create different subclasses of #GimpDock.
683
 *
684 685
 * Return value: the newly created #GimpDock.
 **/
Michael Natterer's avatar
Michael Natterer committed
686
GtkWidget *
687 688
gimp_dialog_factory_dock_new (GimpDialogFactory *factory,
                              GdkScreen         *screen)
Michael Natterer's avatar
Michael Natterer committed
689 690 691 692
{
  GtkWidget *dock;

  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
693
  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
Michael Natterer's avatar
Michael Natterer committed
694 695
  g_return_val_if_fail (factory->new_dock_func != NULL, NULL);

696
  dock = factory->new_dock_func (factory, factory->context, 0);
Michael Natterer's avatar
Michael Natterer committed
697 698

  if (dock)
699
    {
700 701
      gtk_window_set_screen (GTK_WINDOW (dock), screen);

702
      gimp_dialog_factory_set_widget_data (dock, factory, NULL);
703 704 705

      gimp_dialog_factory_add_dialog (factory, dock);
    }
Michael Natterer's avatar
Michael Natterer committed
706 707 708 709

  return dock;
}

710
void
Michael Natterer's avatar
Michael Natterer committed
711
gimp_dialog_factory_add_dialog (GimpDialogFactory *factory,
712
                                GtkWidget         *dialog)
713
{
Michael Natterer's avatar
Michael Natterer committed
714
  GimpDialogFactory      *dialog_factory;
715 716 717
  GimpDialogFactoryEntry *entry;
  GimpSessionInfo        *info;
  GList                  *list;
718
  gboolean                toplevel;
719

720
  g_return_if_fail (GIMP_IS_DIALOG_FACTORY (factory));
Michael Natterer's avatar
Michael Natterer committed
721
  g_return_if_fail (GTK_IS_WIDGET (dialog));
722

Michael Natterer's avatar
Michael Natterer committed
723
  if (g_list_find (factory->open_dialogs, dialog))
724
    {
725
      g_warning ("%s: dialog already registered", G_STRFUNC);
726 727 728
      return;
    }

729
  dialog_factory = gimp_dialog_factory_from_widget (dialog, &entry);
730

731
  if (! (dialog_factory && (entry || GIMP_IS_DOCK (dialog))))
732
    {
733
      g_warning ("%s: dialog was not created by a GimpDialogFactory",
734
                 G_STRFUNC);
735 736 737 738
      return;
    }

  if (dialog_factory != factory)
739
    {
740
      g_warning ("%s: dialog was created by a different GimpDialogFactory",
741
                 G_STRFUNC);
742 743 744
      return;
    }

745 746
  toplevel = GTK_WIDGET_TOPLEVEL (dialog);

747
  if (entry) /* dialog is a toplevel (but not a GimpDock) or a GimpDockable */
748
    {
749 750 751
      GIMP_LOG (DIALOG_FACTORY, "adding %s \"%s\"",
                toplevel ? "toplevel" : "dockable",
                entry->identifier);
752

753
      for (list = factory->session_infos; list; list = g_list_next (list))
754
        {
755
          info = list->data;
756 757 758 759 760 761 762 763 764 765

          if ((info->toplevel_entry == entry) ||
              (info->dockable_entry == entry))
            {
              if (info->widget)
                {
                  if (entry->singleton)
                    {
                      g_warning ("%s: singleton dialog \"%s\" created twice",
                                 G_STRFUNC, entry->identifier);
766

767 768 769
                      GIMP_LOG (DIALOG_FACTORY,
                                "corrupt session info: %p (widget %p)",
                                info, info->widget);
770

771 772
                      return;
                    }
773

774 775
                  continue;
                }
776

777 778
              info->widget = dialog;

779 780 781 782 783
              GIMP_LOG (DIALOG_FACTORY,
                        "updating session info %p (widget %p) for %s \"%s\"",
                        info, info->widget,
                        toplevel ? "toplevel" : "dockable",
                        entry->identifier);
784

785
              if (toplevel && entry->session_managed)
786
                gimp_session_info_set_geometry (info);
787

788 789 790
              break;
            }
        }
791 792

      if (! list) /*  didn't find a session info  */
793
        {
Michael Natterer's avatar
Michael Natterer committed
794
          info = gimp_session_info_new ();
795

796
          info->widget = dialog;
Michael Natterer's avatar
Michael Natterer committed
797

798 799 800 801 802
          GIMP_LOG (DIALOG_FACTORY,
                    "creating session info %p (widget %p) for %s \"%s\"",
                    info, info->widget,
                    toplevel ? "toplevel" : "dockable",
                    entry->identifier);
803

804
          if (toplevel)
805 806 807 808
            {
              info->toplevel_entry = entry;

              /*  if we create a new session info, we never call
809 810
               *  gimp_session_info_set_geometry(), but still the
               *  dialog needs GDK_HINT_USER_POS so it keeps its
811 812 813
               *  position when hidden/shown within this(!) session.
               */
              if (entry->session_managed)
814
                g_signal_connect (dialog, "configure-event",
815 816 817
                                  G_CALLBACK (gimp_dialog_factory_set_user_pos),
                                  NULL);
            }
818
          else
819 820 821
            {
              info->dockable_entry = entry;
            }
822

823 824
          factory->session_infos = g_list_append (factory->session_infos, info);
        }
825
    }
826
  else /*  dialog is a GimpDock  */
827
    {
828
      GIMP_LOG (DIALOG_FACTORY, "adding dock");
829

830
      for (list = factory->session_infos; list; list = g_list_next (list))
831
        {
832
          info = list->data;
833

834 835 836 837
          /*  take the first empty slot  */
          if (! info->toplevel_entry &&
              ! info->dockable_entry &&
              ! info->widget)
838 839
            {
              info->widget = dialog;