preferences-dialog.c 71.9 KB
Newer Older
1
/* The GIMP -- an image manipulation program
2
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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.
 */
18

19 20
#include "config.h"

21 22
#include <string.h>

23
#include <gtk/gtk.h>
Sven Neumann's avatar
Sven Neumann committed
24

25
#include "libgimpmath/gimpmath.h"
26
#include "libgimpbase/gimpbase.h"
27
#include "libgimpwidgets/gimpwidgets.h"
28

29
#include "gui-types.h"
Sven Neumann's avatar
Sven Neumann committed
30

31 32
#include "config/gimpconfig.h"
#include "config/gimpconfig-params.h"
33
#include "config/gimpconfig-utils.h"
34 35
#include "config/gimprc.h"

36
#include "core/gimp.h"
37
#include "core/gimptemplate.h"
38

39
#include "widgets/gimpcolorpanel.h"
40 41
#include "widgets/gimpcontainercombobox.h"
#include "widgets/gimpcontainerview.h"
42 43
#include "widgets/gimpdeviceinfo.h"
#include "widgets/gimpdevices.h"
44
#include "widgets/gimpdialogfactory.h"
45
#include "widgets/gimpgrideditor.h"
46
#include "widgets/gimphelp-ids.h"
47
#include "widgets/gimppropwidgets.h"
48
#include "widgets/gimptemplateeditor.h"
49
#include "widgets/gimpwidgets-utils.h"
50

51 52
#include "menus/menus.h"

Michael Natterer's avatar
Michael Natterer committed
53
#include "resolution-calibrate-dialog.h"
54
#include "session.h"
55
#include "themes.h"
56

57
#include "gimp-intl.h"
58

59

60
/*  preferences local functions  */
61 62

static GtkWidget * prefs_dialog_new               (Gimp       *gimp,
63
                                                   GimpConfig *config);
64 65 66 67 68 69
static void        prefs_config_notify            (GObject    *config,
                                                   GParamSpec *param_spec,
                                                   GObject    *config_copy);
static void        prefs_config_copy_notify       (GObject    *config_copy,
                                                   GParamSpec *param_spec,
                                                   GObject    *config);
70 71
static void        prefs_response                 (GtkWidget  *widget,
                                                   gint        response_id,
72
                                                   GtkWidget  *dialog);
73

74
static void   prefs_resolution_source_callback    (GtkWidget  *widget,
75 76
                                                   GObject    *config);
static void   prefs_resolution_calibrate_callback (GtkWidget  *widget,
77
                                                   GtkWidget  *sizeentry);
78 79
static void   prefs_input_devices_dialog          (GtkWidget  *widget,
                                                   gpointer    user_data);
80 81 82 83 84
static void   prefs_input_dialog_able_callback    (GtkWidget  *widget,
                                                   GdkDevice  *device,
                                                   gpointer    data);


85 86 87 88 89
/*  private variables  */

static GtkWidget *prefs_dialog = NULL;


90 91
/*  public function  */

92 93 94
GtkWidget *
preferences_dialog_create (Gimp *gimp)
{
95 96 97
  GimpConfig *config;
  GimpConfig *config_copy;
  GimpConfig *config_orig;
98 99 100

  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);

101 102 103
  if (prefs_dialog)
    return prefs_dialog;

104
  /*  turn off autosaving while the prefs dialog is open  */
105 106
  gimp_rc_set_autosave (GIMP_RC (gimp->edit_config), FALSE);

107
  config       = GIMP_CONFIG (gimp->edit_config);
108 109 110 111 112 113 114 115 116 117 118 119
  config_copy  = gimp_config_duplicate (config);
  config_orig  = gimp_config_duplicate (config);

  g_signal_connect_object (config, "notify",
                           G_CALLBACK (prefs_config_notify),
                           config_copy, 0);
  g_signal_connect_object (config_copy, "notify",
                           G_CALLBACK (prefs_config_copy_notify),
                           config, 0);

  prefs_dialog = prefs_dialog_new (gimp, config_copy);

120
  g_object_add_weak_pointer (G_OBJECT (prefs_dialog),
121
                             (gpointer) &prefs_dialog);
122

123
  g_object_set_data (G_OBJECT (prefs_dialog), "gimp", gimp);
124

125 126 127 128
  g_object_set_data_full (G_OBJECT (prefs_dialog), "config-copy", config_copy,
                          (GDestroyNotify) g_object_unref);
  g_object_set_data_full (G_OBJECT (prefs_dialog), "config-orig", config_orig,
                          (GDestroyNotify) g_object_unref);
129 130 131 132 133 134 135

  return prefs_dialog;
}


/*  private functions  */

136
static void
137 138 139
prefs_config_notify (GObject    *config,
                     GParamSpec *param_spec,
                     GObject    *config_copy)
140
{
141
  GValue global_value = { 0, };
142
  GValue copy_value   = { 0, };
143

144 145
  g_value_init (&global_value, param_spec->value_type);
  g_value_init (&copy_value,   param_spec->value_type);
146

147 148
  g_object_get_property (config,      param_spec->name, &global_value);
  g_object_get_property (config_copy, param_spec->name, &copy_value);
149

150 151 152 153 154
  if (g_param_values_cmp (param_spec, &global_value, &copy_value))
    {
      g_signal_handlers_block_by_func (config_copy,
                                       prefs_config_copy_notify,
                                       config);
155

156
      g_object_set_property (config_copy, param_spec->name, &global_value);
157

158 159 160 161
      g_signal_handlers_unblock_by_func (config_copy,
                                         prefs_config_copy_notify,
                                         config);
    }
162

163 164
  g_value_unset (&global_value);
  g_value_unset (&copy_value);
165 166
}

167 168 169 170
static void
prefs_config_copy_notify (GObject    *config_copy,
                          GParamSpec *param_spec,
                          GObject    *config)
171
{
172 173
  GValue copy_value   = { 0, };
  GValue global_value = { 0, };
174

175 176
  g_value_init (&copy_value,   param_spec->value_type);
  g_value_init (&global_value, param_spec->value_type);
177

178 179
  g_object_get_property (config_copy, param_spec->name, &copy_value);
  g_object_get_property (config,      param_spec->name, &global_value);
180

181
  if (g_param_values_cmp (param_spec, &copy_value, &global_value))
182
    {
183
      if (param_spec->flags & GIMP_PARAM_CONFIRM)
184
        {
Sven Neumann's avatar
Sven Neumann committed
185
#ifdef GIMP_CONFIG_DEBUG
186
          g_print ("NOT Applying prefs change of '%s' to edit_config "
187 188
                   "because it needs confirmation\n",
                   param_spec->name);
Sven Neumann's avatar
Sven Neumann committed
189
#endif
190 191 192
        }
      else
        {
Sven Neumann's avatar
Sven Neumann committed
193
#ifdef GIMP_CONFIG_DEBUG
194
          g_print ("Applying prefs change of '%s' to edit_config\n",
195
                   param_spec->name);
Sven Neumann's avatar
Sven Neumann committed
196
#endif
197 198 199
          g_signal_handlers_block_by_func (config,
                                           prefs_config_notify,
                                           config_copy);
200

201
          g_object_set_property (config, param_spec->name, &copy_value);
202

203 204 205 206
          g_signal_handlers_unblock_by_func (config,
                                             prefs_config_notify,
                                             config_copy);
        }
207 208
    }

209 210
  g_value_unset (&copy_value);
  g_value_unset (&global_value);
211 212
}

213
static void
214 215 216
prefs_response (GtkWidget *widget,
                gint       response_id,
                GtkWidget *dialog)
217
{
218
  Gimp *gimp = g_object_get_data (G_OBJECT (dialog), "gimp");
Michael Natterer's avatar
Michael Natterer committed
219

220 221 222 223 224 225
  if (response_id == GTK_RESPONSE_OK)
    {
      GimpConfig *config_copy;
      GList      *restart_diff;
      GList      *confirm_diff;
      GList      *list;
Michael Natterer's avatar
Michael Natterer committed
226

227
      config_copy = g_object_get_data (G_OBJECT (dialog), "config-copy");
228

229 230
      /*  destroy config_orig  */
      g_object_set_data (G_OBJECT (dialog), "config-orig", NULL);
231

232
      gtk_widget_set_sensitive (GTK_WIDGET (dialog), FALSE);
233

234 235 236
      confirm_diff = gimp_config_diff (GIMP_CONFIG (gimp->edit_config),
                                       config_copy,
                                       GIMP_PARAM_CONFIRM);
237

238
      g_object_freeze_notify (G_OBJECT (gimp->edit_config));
239

240 241 242 243
      for (list = confirm_diff; list; list = g_list_next (list))
        {
          GParamSpec *param_spec;
          GValue      value = { 0, };
244

245
          param_spec = (GParamSpec *) list->data;
246

247
          g_value_init (&value, param_spec->value_type);
248

249 250 251 252
          g_object_get_property (G_OBJECT (config_copy),
                                 param_spec->name, &value);
          g_object_set_property (G_OBJECT (gimp->edit_config),
                                 param_spec->name, &value);
253

254 255
          g_value_unset (&value);
        }
256

257
      g_object_thaw_notify (G_OBJECT (gimp->edit_config));
258

259
      g_list_free (confirm_diff);
260

261
      gimp_rc_save (GIMP_RC (gimp->edit_config));
262

263 264 265 266 267 268
      /*  spit out a solely informational warning about changed values
       *  which need restart
       */
      restart_diff = gimp_config_diff (GIMP_CONFIG (gimp->edit_config),
                                       GIMP_CONFIG (gimp->config),
                                       GIMP_PARAM_RESTART);
269

270 271 272
      if (restart_diff)
        {
          GString *string;
273

274
          string = g_string_new (_("You will have to restart GIMP for "
275 276
                                   "the following changes to take effect:"));
          g_string_append (string, "\n\n");
277

278 279 280
          for (list = restart_diff; list; list = g_list_next (list))
            {
              GParamSpec *param_spec = list->data;
281

282 283
              g_string_append_printf (string, "%s\n", param_spec->name);
            }
284

285
          g_message (string->str);
286

287 288
          g_string_free (string, TRUE);
        }
289

290
      g_list_free (restart_diff);
291
    }
292 293 294 295 296
  else  /* cancel */
    {
      GimpConfig *config_orig;
      GList      *diff;
      GList      *list;
297

298
      config_orig = g_object_get_data (G_OBJECT (dialog), "config-orig");
299

300 301
      /*  destroy config_copy  */
      g_object_set_data (G_OBJECT (dialog), "config-copy", NULL);
302

303
      gtk_widget_set_sensitive (GTK_WIDGET (dialog), FALSE);
304

305 306
      diff = gimp_config_diff (GIMP_CONFIG (gimp->edit_config), config_orig,
                               GIMP_PARAM_SERIALIZE);
307

308
      g_object_freeze_notify (G_OBJECT (gimp->edit_config));
309

310
      for (list = diff; list; list = g_list_next (list))
311
        {
312
          GParamSpec *param_spec;
313
          GValue      value = { 0, };
314 315 316

          param_spec = (GParamSpec *) list->data;

317 318 319 320 321 322 323 324 325 326
          g_value_init (&value, param_spec->value_type);

          g_object_get_property (G_OBJECT (config_orig),
                                 param_spec->name,
                                 &value);
          g_object_set_property (G_OBJECT (gimp->edit_config),
                                 param_spec->name,
                                 &value);

          g_value_unset (&value);
327
        }
328

329
      g_object_thaw_notify (G_OBJECT (gimp->edit_config));
330

331 332
      g_list_free (diff);
    }
333

334 335 336
  /*  enable autosaving again  */
  gimp_rc_set_autosave (GIMP_RC (gimp->edit_config), TRUE);

337
  gtk_widget_destroy (dialog);
338 339
}

340
static void
341
prefs_template_select_callback (GimpContainerView *view,
342 343 344
                                GimpTemplate      *template,
                                gpointer           insert_data,
                                GimpTemplate      *edit_template)
345
{
346 347
  if (template)
    gimp_config_sync (GIMP_CONFIG (template), GIMP_CONFIG (edit_template), 0);
348 349
}

350
static void
351 352
prefs_resolution_source_callback (GtkWidget *widget,
                                  GObject   *config)
353
{
354 355 356
  gdouble  xres;
  gdouble  yres;
  gboolean from_gdk;
357

358
  gimp_toggle_button_sensitive_update (GTK_TOGGLE_BUTTON (widget));
359

360
  from_gdk = GTK_TOGGLE_BUTTON (widget)->active;
361

362 363
  if (from_gdk)
    {
364
      gimp_get_screen_resolution (NULL, &xres, &yres);
365 366
    }
  else
367
    {
368
      GimpSizeEntry *sizeentry;
369

370 371
      sizeentry = g_object_get_data (G_OBJECT (widget),
                                     "monitor_resolution_sizeentry");
372

373
      if (sizeentry)
374
	{
375 376
	  xres = gimp_size_entry_get_refval (sizeentry, 0);
	  yres = gimp_size_entry_get_refval (sizeentry, 1);
377 378 379
	}
    }

380 381 382
  g_object_set (config,
                "monitor-xresolution",                      xres,
                "monitor-yresolution",                      yres,
383
                "monitor-resolution-from-windowing-system", from_gdk,
384
                NULL);
385
}
386

387 388
static void
prefs_resolution_calibrate_callback (GtkWidget *widget,
389
				     GtkWidget *sizeentry)
390
{
391 392 393 394
  GtkWidget *dialog;
  GtkWidget *notebook;
  GtkWidget *image;

395
  dialog = gtk_widget_get_toplevel (sizeentry);
396

397 398
  notebook = g_object_get_data (G_OBJECT (dialog),   "notebook");
  image    = g_object_get_data (G_OBJECT (notebook), "image");
399

400
  resolution_calibrate_dialog (sizeentry,
401 402
                               gtk_image_get_pixbuf (GTK_IMAGE (image)),
                               NULL, NULL, NULL);
403 404
}

405
static void
406 407
prefs_input_devices_dialog (GtkWidget *widget,
                            gpointer   user_data)
408 409 410
{
  static GtkWidget *input_dialog = NULL;

411 412
  Gimp *gimp = GIMP (user_data);

413 414 415 416 417 418 419 420 421
  if (input_dialog)
    {
      gtk_window_present (GTK_WINDOW (input_dialog));
      return;
    }

  input_dialog = gtk_input_dialog_new ();

  g_object_add_weak_pointer (G_OBJECT (input_dialog),
422
                             (gpointer) &input_dialog);
423 424 425 426

  gtk_window_set_transient_for (GTK_WINDOW (input_dialog),
                                GTK_WINDOW (prefs_dialog));
  gtk_window_set_destroy_with_parent (GTK_WINDOW (input_dialog), TRUE);
427

428 429 430 431
  g_signal_connect_swapped (GTK_INPUT_DIALOG (input_dialog)->save_button,
                            "clicked",
                            G_CALLBACK (gimp_devices_save),
                            gimp);
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

  g_signal_connect_swapped (GTK_INPUT_DIALOG (input_dialog)->close_button,
                            "clicked",
                            G_CALLBACK (gtk_widget_destroy),
                            input_dialog);

  g_signal_connect (input_dialog, "enable_device",
                    G_CALLBACK (prefs_input_dialog_able_callback),
                    NULL);
  g_signal_connect (input_dialog, "disable_device",
                    G_CALLBACK (prefs_input_dialog_able_callback),
                    NULL);

  gtk_widget_show (input_dialog);
}

448 449 450 451 452 453 454 455
static void
prefs_input_dialog_able_callback (GtkWidget *widget,
                                  GdkDevice *device,
                                  gpointer   data)
{
  gimp_device_info_changed_by_device (device);
}

456
static GtkWidget *
457 458
prefs_notebook_append_page (Gimp          *gimp,
                            GtkNotebook   *notebook,
459
                            const gchar   *notebook_label,
460
                            const gchar   *notebook_icon,
461 462
                            GtkTreeStore  *tree,
                            const gchar   *tree_label,
463
                            const gchar   *help_id,
464 465 466
                            GtkTreeIter   *parent,
                            GtkTreeIter   *iter,
                            gint           page_index)
467
{
468 469 470 471
  GtkWidget *event_box;
  GtkWidget *vbox;
  GdkPixbuf *pixbuf       = NULL;
  GdkPixbuf *small_pixbuf = NULL;
472

473
  event_box = gtk_event_box_new ();
474
  gtk_notebook_append_page (notebook, event_box, NULL);
475 476
  gtk_widget_show (event_box);

477
  gimp_help_set_help_data (event_box, NULL, help_id);
478

479
  vbox = gtk_vbox_new (FALSE, 12);
480
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
481 482
  gtk_container_add (GTK_CONTAINER (event_box), vbox);
  gtk_widget_show (vbox);
483

484 485
  if (notebook_icon)
    {
486
      gchar *filename;
487

488 489
      filename = themes_get_theme_file (gimp, "images", "preferences",
                                        notebook_icon, NULL);
490

491 492 493 494
      if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
        pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
      else
        pixbuf = NULL;
495 496 497 498

      g_free (filename);

      if (pixbuf)
499 500 501
        small_pixbuf = gdk_pixbuf_scale_simple (pixbuf,
                                                18, 18,
                                                GDK_INTERP_BILINEAR);
502 503
    }

Manish Singh's avatar
Manish Singh committed
504
  gtk_tree_store_append (tree, iter, parent);
505 506 507 508
  gtk_tree_store_set (tree, iter,
                      0, small_pixbuf,
                      1, tree_label,
                      2, page_index,
509
                      3, notebook_label,
510 511 512
                      4, pixbuf,
                      -1);

513 514
  if (pixbuf)
    g_object_unref (pixbuf);
Manish Singh's avatar
Manish Singh committed
515

516 517 518
  if (small_pixbuf)
    g_object_unref (small_pixbuf);

519 520
  return vbox;
}
521

522
static void
Manish Singh's avatar
Manish Singh committed
523 524
prefs_tree_select_callback (GtkTreeSelection *sel,
			    GtkNotebook      *notebook)
525
{
526 527
  GtkWidget    *label;
  GtkWidget    *image;
Manish Singh's avatar
Manish Singh committed
528 529 530
  GtkTreeModel *model;
  GtkTreeIter   iter;
  GValue        val = { 0, };
531

Manish Singh's avatar
Manish Singh committed
532
  if (! gtk_tree_selection_get_selected (sel, &model, &iter))
533
    return;
534

535 536 537 538 539
  label = g_object_get_data (G_OBJECT (notebook), "label");
  image = g_object_get_data (G_OBJECT (notebook), "image");

  gtk_tree_model_get_value (model, &iter, 3, &val);

540 541
  gtk_label_set_text (GTK_LABEL (label),
                      g_value_get_string (&val));
542 543 544 545 546 547 548 549 550 551

  g_value_unset (&val);

  gtk_tree_model_get_value (model, &iter, 4, &val);

  gtk_image_set_from_pixbuf (GTK_IMAGE (image),
                             g_value_get_object (&val));

  g_value_unset (&val);

552
  gtk_tree_model_get_value (model, &iter, 2, &val);
553

Manish Singh's avatar
Manish Singh committed
554 555
  gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
				 g_value_get_int (&val));
556 557

  g_value_unset (&val);
558
}
559

560 561 562 563 564 565 566
static void
prefs_format_string_select_callback (GtkTreeSelection *sel,
                                     GtkEntry         *entry)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;

567 568 569
  if (gtk_tree_selection_get_selected (sel, &model, &iter))
    {
      GValue val = { 0, };
570

571 572 573 574
      gtk_tree_model_get_value (model, &iter, 1, &val);
      gtk_entry_set_text (entry, g_value_get_string (&val));
      g_value_unset (&val);
    }
575 576
}

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
static void
prefs_theme_select_callback (GtkTreeSelection *sel,
                             Gimp             *gimp)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;

  if (gtk_tree_selection_get_selected (sel, &model, &iter))
    {
      GValue val = { 0, };

      gtk_tree_model_get_value (model, &iter, 0, &val);
      g_object_set (gimp->config, "theme", g_value_get_string (&val), NULL);
      g_value_unset (&val);
    }
}

594 595 596 597 598 599 600
static void
prefs_theme_reload_callback (GtkWidget *button,
                             Gimp      *gimp)
{
  g_object_notify (G_OBJECT (gimp->config), "theme");
}

601
static GtkWidget *
602
prefs_frame_new (const gchar  *label,
603 604
		 GtkContainer *parent,
                 gboolean      expand)
605
{
606 607
  GtkWidget *frame;
  GtkWidget *vbox;
608

609
  frame = gimp_frame_new (label);
610

611 612 613
  vbox = gtk_vbox_new (FALSE, 6);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  gtk_widget_show (vbox);
614

615
  if (GTK_IS_BOX (parent))
616
    gtk_box_pack_start (GTK_BOX (parent), frame, expand, expand, 0);
617 618 619
  else
    gtk_container_add (parent, frame);

620 621
  gtk_widget_show (frame);

622
  return vbox;
623 624
}

625 626
static GtkWidget *
prefs_table_new (gint          rows,
627
                 GtkContainer *parent)
628 629 630 631 632
{
  GtkWidget *table;

  table = gtk_table_new (rows, 2, FALSE);

633 634
  gtk_table_set_row_spacings (GTK_TABLE (table), 6);
  gtk_table_set_col_spacings (GTK_TABLE (table), 6);
635 636 637 638 639 640 641 642 643 644 645 646

  if (GTK_IS_BOX (parent))
    gtk_box_pack_start (GTK_BOX (parent), table, FALSE, FALSE, 0);
  else
    gtk_container_add (parent, table);

  gtk_widget_show (table);

  return table;
}

static GtkWidget *
647 648 649
prefs_check_button_add (GObject     *config,
                        const gchar *property_name,
                        const gchar *label,
650 651 652 653
                        GtkBox      *vbox)
{
  GtkWidget *button;

654
  button = gimp_prop_check_button_new (config, property_name, label);
655

656 657 658 659 660
  if (button)
    {
      gtk_box_pack_start (vbox, button, FALSE, FALSE, 0);
      gtk_widget_show (button);
    }
661 662 663 664

  return button;
}

665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
static GtkWidget *
prefs_check_button_add_with_icon (GObject     *config,
                                  const gchar *property_name,
                                  const gchar *label,
                                  const gchar *stock_id,
                                  GtkBox      *vbox)
{
  GtkWidget *button;
  GtkWidget *hbox;
  GtkWidget *image;

  button = gimp_prop_check_button_new (config, property_name, label);
  if (!button)
    return NULL;

  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_start (vbox, hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
  gtk_misc_set_padding (GTK_MISC (image), 2, 2);
  gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
  gtk_widget_show (image);

  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  gtk_widget_show (button);

  return button;
}

695
static GtkWidget *
696 697 698 699 700 701
prefs_widget_add_aligned (GtkWidget    *widget,
                          const gchar  *text,
                          GtkTable     *table,
                          gint          table_row,
                          gboolean      left_align,
                          GtkSizeGroup *group)
702
{
703 704 705 706 707
  GtkWidget *label = gimp_table_attach_aligned (table, 0, table_row,
                                                text, 0.0, 0.5,
                                                widget, 1, left_align);
  if (group)
    gtk_size_group_add_widget (group, label);
708

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
  return label;
}

static GtkWidget *
prefs_color_button_add (GObject      *config,
                        const gchar  *property_name,
                        const gchar  *label,
                        const gchar  *title,
                        GtkTable     *table,
                        gint          table_row,
                        GtkSizeGroup *group)
{
  GtkWidget *button = gimp_prop_color_button_new (config, property_name, title,
                                                  60, 20,
                                                  GIMP_COLOR_AREA_FLAT);
724 725

  if (button)
726
    prefs_widget_add_aligned (button, label, table, table_row, TRUE, group);
727 728 729 730

  return button;
}

731
static GtkWidget *
732 733 734 735 736 737 738 739
prefs_enum_combo_box_add (GObject      *config,
                          const gchar  *property_name,
                          gint          minimum,
                          gint          maximum,
                          const gchar  *label,
                          GtkTable     *table,
                          gint          table_row,
                          GtkSizeGroup *group)
740
{
741 742
  GtkWidget *combo = gimp_prop_enum_combo_box_new (config, property_name,
                                                   minimum, maximum);
743

744 745
  if (combo)
    prefs_widget_add_aligned (combo, label, table, table_row, FALSE, group);
746

747
  return combo;
748 749 750
}

static GtkWidget *
751 752 753 754 755 756 757 758
prefs_boolean_combo_box_add (GObject      *config,
                             const gchar  *property_name,
                             const gchar  *true_text,
                             const gchar  *false_text,
                             const gchar  *label,
                             GtkTable     *table,
                             gint          table_row,
                             GtkSizeGroup *group)
759
{
760 761
  GtkWidget *combo = gimp_prop_boolean_combo_box_new (config, property_name,
                                                      true_text, false_text);
762

763 764
  if (combo)
    prefs_widget_add_aligned (combo, label, table, table_row, FALSE, group);
765

766
  return combo;
767 768 769
}

static GtkWidget *
770 771 772 773 774 775 776 777 778
prefs_spin_button_add (GObject      *config,
                       const gchar  *property_name,
                       gdouble       step_increment,
                       gdouble       page_increment,
                       gint          digits,
                       const gchar  *label,
                       GtkTable     *table,
                       gint          table_row,
                       GtkSizeGroup *group)
779
{
780 781 782 783
  GtkWidget *button = gimp_prop_spin_button_new (config, property_name,
                                                 step_increment,
                                                 page_increment,
                                                 digits);
784

785 786
  if (button)
    prefs_widget_add_aligned (button, label, table, table_row, TRUE, group);
787

788
  return button;
789 790 791
}

static GtkWidget *
792 793 794 795 796 797
prefs_memsize_entry_add (GObject      *config,
                         const gchar  *property_name,
                         const gchar  *label,
                         GtkTable     *table,
                         gint          table_row,
                         GtkSizeGroup *group)
798
{
799
  GtkWidget *entry = gimp_prop_memsize_entry_new (config, property_name);
800 801

  if (entry)
802
    prefs_widget_add_aligned (entry, label, table, table_row, TRUE, group);
803 804 805 806

  return entry;
}

807 808 809 810 811 812
static void
prefs_display_options_frame_add (Gimp         *gimp,
                                 GObject      *object,
                                 const gchar  *label,
                                 GtkContainer *parent)
{
813 814
  GtkWidget *vbox;
  GtkWidget *hbox;
815
  GtkWidget *checks_vbox;
816 817 818
  GtkWidget *table;
  GtkWidget *button;

819 820 821 822 823
  vbox = prefs_frame_new (label, parent, FALSE);

  hbox = gtk_hbox_new (FALSE, 4);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
824

825 826
  checks_vbox = gtk_vbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0);
827 828
  gtk_widget_show (checks_vbox);

829 830
  prefs_check_button_add (object, "show-menubar",
                          _("Show _Menubar"),
831
                          GTK_BOX (checks_vbox));
832 833
  prefs_check_button_add (object, "show-rulers",
                          _("Show _Rulers"),
834
                          GTK_BOX (checks_vbox));
835 836
  prefs_check_button_add (object, "show-scrollbars",
                          _("Show Scroll_bars"),
837
                          GTK_BOX (checks_vbox));
838 839
  prefs_check_button_add (object, "show-statusbar",
                          _("Show S_tatusbar"),
840 841
                          GTK_BOX (checks_vbox));

842 843
  checks_vbox = gtk_vbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), checks_vbox, TRUE, TRUE, 0);
844
  gtk_widget_show (checks_vbox);
845

846 847
  prefs_check_button_add (object, "show-selection",
                          _("Show S_election"),
848
                          GTK_BOX (checks_vbox));
849 850
  prefs_check_button_add (object, "show-layer-boundary",
                          _("Show _Layer Boundary"),
851
                          GTK_BOX (checks_vbox));
852 853
  prefs_check_button_add (object, "show-guides",
                          _("Show _Guides"),
854
                          GTK_BOX (checks_vbox));
855 856
  prefs_check_button_add (object, "show-grid",
                          _("Show Gri_d"),
857
                          GTK_BOX (checks_vbox));
858

859
  table = prefs_table_new (2, GTK_CONTAINER (vbox));
860

861
  prefs_enum_combo_box_add (object, "padding-mode", 0, 0,
862 863
                            _("Canvas Padding Mode:"), GTK_TABLE (table), 0,
                            NULL);
864 865 866 867

  button = prefs_color_button_add (object, "padding-color",
                                   _("Custom Padding Color:"),
                                   _("Select Custom Canvas Padding Color"),
868
                                   GTK_TABLE (table), 1, NULL);
869 870 871 872
  gimp_color_panel_set_context (GIMP_COLOR_PANEL (button),
                                gimp_get_user_context (gimp));
}

873
static void
874 875
prefs_help_func (const gchar *help_id,
                 gpointer     help_data)
876
{
877 878 879
  GtkWidget *notebook;
  GtkWidget *event_box;
  gint       page_num;
880

881 882 883
  notebook  = g_object_get_data (G_OBJECT (help_data), "notebook");
  page_num  = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
  event_box = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), page_num);
884

885 886
  help_id = g_object_get_data (G_OBJECT (event_box), "gimp-help-id");
  gimp_standard_help_func (help_id, NULL);
887 888
}

889
static GtkWidget *
890 891
prefs_dialog_new (Gimp       *gimp,
                  GimpConfig *config)
892
{
893
  GtkWidget         *dialog;
894 895 896 897 898 899 900
  GtkWidget         *tv;
  GtkTreeStore      *tree;
  GtkTreeViewColumn *column;
  GtkCellRenderer   *cell;
  GtkTreeSelection  *sel;
  GtkTreeIter        top_iter;
  GtkTreeIter        child_iter;
901
  GtkTreeIter        grandchild_iter;