gimpactionview.c 30 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
2 3 4
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * gimpactionview.c
5
 * Copyright (C) 2004-2005  Michael Natterer <mitch@gimp.org>
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10 11 12 13 14 15 16 17
 * (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
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 20 21 22 23 24
 */

#include "config.h"

#include <string.h>

25
#include <gegl.h>
26
#include <gtk/gtk.h>
27
#include <gdk/gdkkeysyms.h>
28 29

#include "libgimpbase/gimpbase.h"
30
#include "libgimpwidgets/gimpwidgets.h"
31 32 33

#include "widgets-types.h"

34 35
#include "core/gimp.h"

36 37 38
#include "gimpaction.h"
#include "gimpactiongroup.h"
#include "gimpactionview.h"
39 40
#include "gimpmessagebox.h"
#include "gimpmessagedialog.h"
41 42 43 44 45 46 47
#include "gimpuimanager.h"

#include "gimp-intl.h"


/*  local function prototypes  */

48
static void     gimp_action_view_dispose         (GObject         *object);
49
static void     gimp_action_view_finalize        (GObject         *object);
50

51 52
static void     gimp_action_view_select_path     (GimpActionView  *view,
                                                  GtkTreePath     *path);
53 54 55 56 57 58 59 60
static gboolean gimp_action_view_accel_find_func (GtkAccelKey     *key,
                                                  GClosure        *closure,
                                                  gpointer         data);
static void     gimp_action_view_accel_changed   (GtkAccelGroup   *accel_group,
                                                  guint            unused1,
                                                  GdkModifierType  unused2,
                                                  GClosure        *accel_closure,
                                                  GimpActionView  *view);
61
static void     gimp_action_view_accel_edited    (GtkCellRendererAccel *accel,
62 63 64
                                                  const char      *path_string,
                                                  guint            accel_key,
                                                  GdkModifierType  accel_mask,
65 66 67 68
                                                  guint            hardware_keycode,
                                                  GimpActionView  *view);
static void     gimp_action_view_accel_cleared   (GtkCellRendererAccel *accel,
                                                  const char      *path_string,
69 70
                                                  GimpActionView  *view);

71

72
G_DEFINE_TYPE (GimpActionView, gimp_action_view, GTK_TYPE_TREE_VIEW)
73

74
#define parent_class gimp_action_view_parent_class
75 76 77 78 79


static void
gimp_action_view_class_init (GimpActionViewClass *klass)
{
80 81
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

82 83
  object_class->dispose  = gimp_action_view_dispose;
  object_class->finalize = gimp_action_view_finalize;
84 85 86 87 88 89 90
}

static void
gimp_action_view_init (GimpActionView *view)
{
}

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
static void
gimp_action_view_dispose (GObject *object)
{
  GimpActionView *view = GIMP_ACTION_VIEW (object);

  if (view->manager)
    {
      if (view->show_shortcuts)
        {
          GtkAccelGroup *group;

          group = gtk_ui_manager_get_accel_group (GTK_UI_MANAGER (view->manager));

          g_signal_handlers_disconnect_by_func (group,
                                                gimp_action_view_accel_changed,
                                                view);
        }

109
      g_clear_object (&view->manager);
110 111 112 113 114
    }

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

115 116 117 118 119
static void
gimp_action_view_finalize (GObject *object)
{
  GimpActionView *view = GIMP_ACTION_VIEW (object);

120
  g_clear_pointer (&view->filter, g_free);
121 122 123 124

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

125 126 127 128 129
static gboolean
idle_start_editing (GtkTreeView *tree_view)
{
  GtkTreePath *path;

130
  path = g_object_get_data (G_OBJECT (tree_view), "start-editing-path");
131 132 133 134 135 136 137 138 139

  if (path)
    {
      gtk_widget_grab_focus (GTK_WIDGET (tree_view));

      gtk_tree_view_set_cursor (tree_view, path,
                                gtk_tree_view_get_column (tree_view, 1),
                                TRUE);

140
      g_object_set_data (G_OBJECT (tree_view), "start-editing-path", NULL);
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
    }

  return FALSE;
}

static gboolean
gimp_action_view_button_press (GtkWidget      *widget,
                               GdkEventButton *event)
{
  GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
  GtkTreePath *path;

  if (event->window != gtk_tree_view_get_bin_window (tree_view))
    return FALSE;

  if (gtk_tree_view_get_path_at_pos (tree_view,
                                     (gint) event->x,
158 159
                                     (gint) event->y,
                                     &path, NULL,
160 161 162 163 164 165 166
                                     NULL, NULL))
    {
      GClosure *closure;
      GSource  *source;

      if (gtk_tree_path_get_depth (path) == 1)
        {
167 168 169
          gtk_tree_path_free (path);
          return FALSE;
        }
170

171
      g_object_set_data_full (G_OBJECT (tree_view), "start-editing-path",
172 173
                              path, (GDestroyNotify) gtk_tree_path_free);

174
      g_signal_stop_emission_by_name (tree_view, "button-press-event");
175 176 177 178 179 180 181

      closure = g_cclosure_new_object (G_CALLBACK (idle_start_editing),
                                       G_OBJECT (tree_view));

      source = g_idle_source_new ();
      g_source_set_closure (source, closure);
      g_source_attach (source, NULL);
182
      g_source_unref (source);
183 184 185 186 187
    }

  return TRUE;
}

188 189
GtkWidget *
gimp_action_view_new (GimpUIManager *manager,
190
                      const gchar   *select_action,
191 192 193 194 195 196
                      gboolean       show_shortcuts)
{
  GtkTreeView       *view;
  GtkTreeViewColumn *column;
  GtkCellRenderer   *cell;
  GtkTreeStore      *store;
197
  GtkTreeModel      *filter;
198 199
  GtkAccelGroup     *accel_group;
  GList             *list;
200
  GtkTreePath       *select_path = NULL;
201 202 203

  g_return_val_if_fail (GIMP_IS_UI_MANAGER (manager), NULL);

204
  store = gtk_tree_store_new (GIMP_ACTION_VIEW_N_COLUMNS,
205 206
                              G_TYPE_BOOLEAN,         /* COLUMN_VISIBLE        */
                              GTK_TYPE_ACTION,        /* COLUMN_ACTION         */
207
                              G_TYPE_STRING,          /* COLUMN_ICON_NAME      */
208 209 210 211 212 213
                              G_TYPE_STRING,          /* COLUMN_LABEL          */
                              G_TYPE_STRING,          /* COLUMN_LABEL_CASEFOLD */
                              G_TYPE_STRING,          /* COLUMN_NAME           */
                              G_TYPE_UINT,            /* COLUMN_ACCEL_KEY      */
                              GDK_TYPE_MODIFIER_TYPE, /* COLUMN_ACCEL_MASK     */
                              G_TYPE_CLOSURE);        /* COLUMN_ACCEL_CLOSURE  */
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228

  accel_group = gtk_ui_manager_get_accel_group (GTK_UI_MANAGER (manager));

  for (list = gtk_ui_manager_get_action_groups (GTK_UI_MANAGER (manager));
       list;
       list = g_list_next (list))
    {
      GimpActionGroup *group = list->data;
      GList           *actions;
      GList           *list2;
      GtkTreeIter      group_iter;

      gtk_tree_store_append (store, &group_iter, NULL);

      gtk_tree_store_set (store, &group_iter,
229 230
                          GIMP_ACTION_VIEW_COLUMN_ICON_NAME, group->icon_name,
                          GIMP_ACTION_VIEW_COLUMN_LABEL,     group->label,
231 232 233 234 235 236 237 238
                          -1);

      actions = gtk_action_group_list_actions (GTK_ACTION_GROUP (group));

      actions = g_list_sort (actions, (GCompareFunc) gimp_action_name_compare);

      for (list2 = actions; list2; list2 = g_list_next (list2))
        {
239 240
          GtkAction       *action        = list2->data;
          const gchar     *name          = gtk_action_get_name (action);
241
          const gchar     *icon_name     = gtk_action_get_icon_name (action);
242
          gchar           *label;
243
          gchar           *label_casefold;
244 245 246 247 248
          guint            accel_key     = 0;
          GdkModifierType  accel_mask    = 0;
          GClosure        *accel_closure = NULL;
          GtkTreeIter      action_iter;

249
          if (gimp_action_is_gui_blacklisted (name))
250 251
            continue;

252
          label = gimp_strip_uline (gtk_action_get_label (action));
253

254 255 256 257 258 259
          if (! (label && strlen (label)))
            {
              g_free (label);
              label = g_strdup (name);
            }

260 261
          label_casefold = g_utf8_casefold (label, -1);

262 263
          if (show_shortcuts)
            {
264
              accel_closure = gtk_action_get_accel_closure (action);
265

266
              if (accel_closure)
267
                {
268
                  GtkAccelKey *key;
269

270 271 272 273 274 275 276
                  key = gtk_accel_group_find (accel_group,
                                              gimp_action_view_accel_find_func,
                                              accel_closure);

                  if (key            &&
                      key->accel_key &&
                      key->accel_flags & GTK_ACCEL_VISIBLE)
277
                    {
278 279
                      accel_key  = key->accel_key;
                      accel_mask = key->accel_mods;
280 281
                    }
                }
282
            }
283

284
          gtk_tree_store_append (store, &action_iter, &group_iter);
285

286
          gtk_tree_store_set (store, &action_iter,
287 288
                              GIMP_ACTION_VIEW_COLUMN_VISIBLE,        TRUE,
                              GIMP_ACTION_VIEW_COLUMN_ACTION,         action,
289
                              GIMP_ACTION_VIEW_COLUMN_ICON_NAME,      icon_name,
290 291 292 293 294 295
                              GIMP_ACTION_VIEW_COLUMN_LABEL,          label,
                              GIMP_ACTION_VIEW_COLUMN_LABEL_CASEFOLD, label_casefold,
                              GIMP_ACTION_VIEW_COLUMN_NAME,           name,
                              GIMP_ACTION_VIEW_COLUMN_ACCEL_KEY,      accel_key,
                              GIMP_ACTION_VIEW_COLUMN_ACCEL_MASK,     accel_mask,
                              GIMP_ACTION_VIEW_COLUMN_ACCEL_CLOSURE,  accel_closure,
296
                              -1);
297

298
          g_free (label);
299
          g_free (label_casefold);
300

301 302 303 304
          if (select_action && ! strcmp (select_action, name))
            {
              select_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
                                                     &action_iter);
305 306 307 308 309 310
            }
        }

      g_list_free (actions);
    }

311 312 313 314
  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);

  g_object_unref (store);

315
  view = g_object_new (GIMP_TYPE_ACTION_VIEW,
316
                       "model",      filter,
317 318 319
                       "rules-hint", TRUE,
                       NULL);

320 321 322 323
  g_object_unref (filter);

  gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter),
                                            GIMP_ACTION_VIEW_COLUMN_VISIBLE);
324

325 326 327
  GIMP_ACTION_VIEW (view)->manager        = g_object_ref (manager);
  GIMP_ACTION_VIEW (view)->show_shortcuts = show_shortcuts;

Michael Natterer's avatar
Michael Natterer committed
328 329 330
  gtk_tree_view_set_search_column (GTK_TREE_VIEW (view),
                                   GIMP_ACTION_VIEW_COLUMN_LABEL);

331 332 333 334 335 336
  column = gtk_tree_view_column_new ();
  gtk_tree_view_column_set_title (column, _("Action"));

  cell = gtk_cell_renderer_pixbuf_new ();
  gtk_tree_view_column_pack_start (column, cell, FALSE);
  gtk_tree_view_column_set_attributes (column, cell,
337 338
                                       "icon-name",
                                       GIMP_ACTION_VIEW_COLUMN_ICON_NAME,
339 340 341 342 343
                                       NULL);

  cell = gtk_cell_renderer_text_new ();
  gtk_tree_view_column_pack_start (column, cell, TRUE);
  gtk_tree_view_column_set_attributes (column, cell,
344 345
                                       "text",
                                       GIMP_ACTION_VIEW_COLUMN_LABEL,
346 347 348 349 350 351
                                       NULL);

  gtk_tree_view_append_column (view, column);

  if (show_shortcuts)
    {
352
      g_signal_connect (view, "button-press-event",
353 354 355
                        G_CALLBACK (gimp_action_view_button_press),
                        NULL);

356 357 358
      g_signal_connect (accel_group, "accel-changed",
                        G_CALLBACK (gimp_action_view_accel_changed),
                        view);
359 360 361 362

      column = gtk_tree_view_column_new ();
      gtk_tree_view_column_set_title (column, _("Shortcut"));

363
      cell = gtk_cell_renderer_accel_new ();
364 365 366 367
      g_object_set (cell,
                    "mode",     GTK_CELL_RENDERER_MODE_EDITABLE,
                    "editable", TRUE,
                    NULL);
368 369
      gtk_tree_view_column_pack_start (column, cell, TRUE);
      gtk_tree_view_column_set_attributes (column, cell,
370 371
                                           "accel-key",
                                           GIMP_ACTION_VIEW_COLUMN_ACCEL_KEY,
372
                                           "accel-mods",
373
                                           GIMP_ACTION_VIEW_COLUMN_ACCEL_MASK,
374 375
                                           NULL);

376 377 378
      g_signal_connect (cell, "accel-edited",
                        G_CALLBACK (gimp_action_view_accel_edited),
                        view);
379 380 381
      g_signal_connect (cell, "accel-cleared",
                        G_CALLBACK (gimp_action_view_accel_cleared),
                        view);
382

383 384 385
      gtk_tree_view_append_column (view, column);
    }

386 387
  column = gtk_tree_view_column_new ();
  gtk_tree_view_column_set_title (column, _("Name"));
388

389 390 391 392 393 394 395 396
  cell = gtk_cell_renderer_text_new ();
  gtk_tree_view_column_pack_start (column, cell, TRUE);
  gtk_tree_view_column_set_attributes (column, cell,
                                       "text",
                                       GIMP_ACTION_VIEW_COLUMN_NAME,
                                       NULL);

  gtk_tree_view_append_column (view, column);
397

398 399
  if (select_path)
    {
400
      gimp_action_view_select_path (GIMP_ACTION_VIEW (view), select_path);
401 402 403
      gtk_tree_path_free (select_path);
    }

404 405 406
  return GTK_WIDGET (view);
}

407 408 409 410
void
gimp_action_view_set_filter (GimpActionView *view,
                             const gchar    *filter)
{
411 412 413 414 415 416
  GtkTreeSelection    *sel;
  GtkTreeModel        *filtered_model;
  GtkTreeModel        *model;
  GtkTreeIter          iter;
  gboolean             iter_valid;
  GtkTreeRowReference *selected_row = NULL;
417 418 419

  g_return_if_fail (GIMP_IS_ACTION_VIEW (view));

420 421
  filtered_model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
  model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filtered_model));
422 423 424 425 426

  if (filter && ! strlen (filter))
    filter = NULL;

  g_free (view->filter);
427 428 429 430
  view->filter = NULL;

  if (filter)
    view->filter = g_utf8_casefold (filter, -1);
431

432 433 434 435 436 437 438 439 440
  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));

  if (gtk_tree_selection_get_selected (sel, NULL, &iter))
    {
      GtkTreePath *path = gtk_tree_model_get_path (filtered_model, &iter);

      selected_row = gtk_tree_row_reference_new (filtered_model, path);
    }

441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter))
    {
      GtkTreeIter child_iter;
      gboolean    child_valid;
      gint        n_children = 0;

      for (child_valid = gtk_tree_model_iter_children (model, &child_iter,
                                                       &iter);
           child_valid;
           child_valid = gtk_tree_model_iter_next (model, &child_iter))
        {
          gboolean visible = TRUE;

          if (view->filter)
            {
              gchar *label;
              gchar *name;

              gtk_tree_model_get (model, &child_iter,
462 463
                                  GIMP_ACTION_VIEW_COLUMN_LABEL_CASEFOLD, &label,
                                  GIMP_ACTION_VIEW_COLUMN_NAME,           &name,
464 465
                                  -1);

466 467
              visible = label && name && (strstr (label, view->filter) != NULL ||
                                          strstr (name,  view->filter) != NULL);
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489

              g_free (label);
              g_free (name);
            }

          gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter,
                              GIMP_ACTION_VIEW_COLUMN_VISIBLE, visible,
                              -1);

          if (visible)
            n_children++;
        }

      gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
                          GIMP_ACTION_VIEW_COLUMN_VISIBLE, n_children > 0,
                          -1);
    }

  if (view->filter)
    gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
  else
    gtk_tree_view_collapse_all (GTK_TREE_VIEW (view));
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504

  gtk_tree_view_columns_autosize (GTK_TREE_VIEW (view));

  if (selected_row)
    {
      if (gtk_tree_row_reference_valid (selected_row))
        {
          GtkTreePath *path = gtk_tree_row_reference_get_path (selected_row);

          gimp_action_view_select_path (view, path);
          gtk_tree_path_free (path);
        }

      gtk_tree_row_reference_free (selected_row);
    }
505 506
}

507 508 509

/*  private functions  */

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
static void
gimp_action_view_select_path (GimpActionView *view,
                              GtkTreePath    *path)
{
  GtkTreeView *tv = GTK_TREE_VIEW (view);
  GtkTreePath *expand;

  expand = gtk_tree_path_copy (path);
  gtk_tree_path_up (expand);
  gtk_tree_view_expand_row (tv, expand, FALSE);
  gtk_tree_path_free (expand);

  gtk_tree_view_set_cursor (tv, path, NULL, FALSE);
  gtk_tree_view_scroll_to_cell (tv, path, NULL, TRUE, 0.5, 0.0);
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
static gboolean
gimp_action_view_accel_find_func (GtkAccelKey *key,
                                  GClosure    *closure,
                                  gpointer     data)
{
  return (GClosure *) data == closure;
}

static void
gimp_action_view_accel_changed (GtkAccelGroup   *accel_group,
                                guint            unused1,
                                GdkModifierType  unused2,
                                GClosure        *accel_closure,
                                GimpActionView  *view)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;
  gboolean      iter_valid;

  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
  if (! model)
    return;

549 550 551 552
  model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
  if (! model)
    return;

553 554 555 556 557 558 559 560 561 562 563 564
  for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
       iter_valid;
       iter_valid = gtk_tree_model_iter_next (model, &iter))
    {
      GtkTreeIter child_iter;
      gboolean    child_valid;

      for (child_valid = gtk_tree_model_iter_children (model, &child_iter,
                                                       &iter);
           child_valid;
           child_valid = gtk_tree_model_iter_next (model, &child_iter))
        {
565
          GClosure *closure;
566 567

          gtk_tree_model_get (model, &child_iter,
568
                              GIMP_ACTION_VIEW_COLUMN_ACCEL_CLOSURE, &closure,
569 570
                              -1);

571 572
          if (closure)
            g_closure_unref (closure);
573

574 575 576 577 578
          if (accel_closure == closure)
            {
              GtkAccelKey     *key;
              guint            accel_key  = 0;
              GdkModifierType  accel_mask = 0;
579

580 581 582
              key = gtk_accel_group_find (accel_group,
                                          gimp_action_view_accel_find_func,
                                          accel_closure);
583

584 585 586
              if (key            &&
                  key->accel_key &&
                  key->accel_flags & GTK_ACCEL_VISIBLE)
587
                {
588 589 590
                  accel_key  = key->accel_key;
                  accel_mask = key->accel_mods;
                }
591

592 593 594 595
              gtk_tree_store_set (GTK_TREE_STORE (model), &child_iter,
                                  GIMP_ACTION_VIEW_COLUMN_ACCEL_KEY,  accel_key,
                                  GIMP_ACTION_VIEW_COLUMN_ACCEL_MASK, accel_mask,
                                  -1);
596

597
              return;
598 599 600 601
            }
        }
    }
}
602 603 604

typedef struct
{
605
  GimpUIManager   *manager;
606 607 608 609 610 611
  gchar           *accel_path;
  guint            accel_key;
  GdkModifierType  accel_mask;
} ConfirmData;

static void
612 613 614
gimp_action_view_conflict_response (GtkWidget   *dialog,
                                    gint         response_id,
                                    ConfirmData *confirm_data)
615
{
616
  gtk_widget_destroy (dialog);
617

618 619
  if (response_id == GTK_RESPONSE_OK)
    {
620 621 622 623 624
      if (! gtk_accel_map_change_entry (confirm_data->accel_path,
                                        confirm_data->accel_key,
                                        confirm_data->accel_mask,
                                        TRUE))
        {
625
          gimp_message_literal (confirm_data->manager->gimp, G_OBJECT (dialog),
626 627
                                GIMP_MESSAGE_ERROR,
                                _("Changing shortcut failed."));
628 629
        }
    }
630 631

  g_free (confirm_data->accel_path);
Sven Neumann's avatar
Sven Neumann committed
632 633

  g_slice_free (ConfirmData, confirm_data);
634 635 636 637 638 639 640 641 642 643 644 645 646 647
}

static void
gimp_action_view_conflict_confirm (GimpActionView  *view,
                                   GtkAction       *action,
                                   guint            accel_key,
                                   GdkModifierType  accel_mask,
                                   const gchar     *accel_path)
{
  GimpActionGroup *group;
  gchar           *label;
  gchar           *accel_string;
  ConfirmData     *confirm_data;
  GtkWidget       *dialog;
648
  GimpMessageBox  *box;
649

650
  g_object_get (action, "action-group", &group, NULL);
651

652
  label = gimp_strip_uline (gtk_action_get_label (action));
653

654
  accel_string = gtk_accelerator_get_label (accel_key, accel_mask);
655

Sven Neumann's avatar
Sven Neumann committed
656
  confirm_data = g_slice_new (ConfirmData);
657

658
  confirm_data->manager    = view->manager;
659 660 661 662 663 664
  confirm_data->accel_path = g_strdup (accel_path);
  confirm_data->accel_key  = accel_key;
  confirm_data->accel_mask = accel_mask;

  dialog =
    gimp_message_dialog_new (_("Conflicting Shortcuts"),
665
                             GIMP_ICON_DIALOG_WARNING,
666 667 668
                             gtk_widget_get_toplevel (GTK_WIDGET (view)), 0,
                             gimp_standard_help_func, NULL,

669 670
                             _("_Cancel"),            GTK_RESPONSE_CANCEL,
                             _("_Reassign Shortcut"), GTK_RESPONSE_OK,
671 672 673

                             NULL);

674
  gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
675 676 677
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);
678

679 680 681 682 683 684 685 686 687
  g_signal_connect (dialog, "response",
                    G_CALLBACK (gimp_action_view_conflict_response),
                    confirm_data);

  box = GIMP_MESSAGE_DIALOG (dialog)->box;

  gimp_message_box_set_primary_text (box,
                                     _("Shortcut \"%s\" is already taken "
                                       "by \"%s\" from the \"%s\" group."),
688
                                     accel_string, label, group->label);
689 690 691
  gimp_message_box_set_text (box,
                             _("Reassigning the shortcut will cause it "
                               "to be removed from \"%s\"."),
692
                             label);
693

694
  g_free (label);
695 696 697 698 699
  g_free (accel_string);

  g_object_unref (group);

  gtk_widget_show (dialog);
700 701
}

702 703 704
static const gchar *
gimp_action_view_get_accel_action (GimpActionView  *view,
                                   const gchar     *path_string,
705 706 707
                                   GtkAction      **action_return,
                                   guint           *action_accel_key,
                                   GdkModifierType *action_accel_mask)
708 709 710 711 712 713 714
{
  GtkTreeModel *model;
  GtkTreePath  *path;
  GtkTreeIter   iter;

  model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
  if (! model)
715
    return NULL;
716 717 718 719 720

  path = gtk_tree_path_new_from_string (path_string);

  if (gtk_tree_model_get_iter (model, &iter, path))
    {
721
      GtkAction *action;
722 723

      gtk_tree_model_get (model, &iter,
724 725 726
                          GIMP_ACTION_VIEW_COLUMN_ACTION,     &action,
                          GIMP_ACTION_VIEW_COLUMN_ACCEL_KEY,  action_accel_key,
                          GIMP_ACTION_VIEW_COLUMN_ACCEL_MASK, action_accel_mask,
727 728 729 730 731
                          -1);

      if (! action)
        goto done;

732 733
      gtk_tree_path_free (path);
      g_object_unref (action);
734

735 736 737 738 739 740 741
      *action_return = action;

      return gtk_action_get_accel_path (action);
    }

 done:
  gtk_tree_path_free (path);
742

743 744 745 746 747 748 749 750 751 752 753
  return NULL;
}

static void
gimp_action_view_accel_edited (GtkCellRendererAccel *accel,
                               const char           *path_string,
                               guint                 accel_key,
                               GdkModifierType       accel_mask,
                               guint                 hardware_keycode,
                               GimpActionView       *view)
{
754 755 756 757
  GtkAction       *action;
  guint            action_accel_key;
  GdkModifierType  action_accel_mask;
  const gchar     *accel_path;
758 759

  accel_path = gimp_action_view_get_accel_action (view, path_string,
760 761 762
                                                  &action,
                                                  &action_accel_key,
                                                  &action_accel_mask);
763

764 765 766
  if (! accel_path)
    return;

767 768 769 770
  if (accel_key  == action_accel_key &&
      accel_mask == action_accel_mask)
    return;

771 772 773
  if (! accel_key ||

      /* Don't allow arrow keys, they are all swallowed by the canvas
774
       * and cannot be invoked anyway, the same applies to space.
775 776 777 778
       */
      accel_key == GDK_KEY_Left  ||
      accel_key == GDK_KEY_Right ||
      accel_key == GDK_KEY_Up    ||
779 780 781
      accel_key == GDK_KEY_Down  ||
      accel_key == GDK_KEY_space ||
      accel_key == GDK_KEY_KP_Space)
782
    {
783
      gimp_message_literal (view->manager->gimp,
784 785
                            G_OBJECT (view), GIMP_MESSAGE_ERROR,
                            _("Invalid shortcut."));
786
    }
787 788 789 790
  else if (accel_key        == GDK_KEY_F1 ||
           action_accel_key == GDK_KEY_F1)
    {
      gimp_message_literal (view->manager->gimp,
791 792
                            G_OBJECT (view), GIMP_MESSAGE_ERROR,
                            _("F1 cannot be remapped."));
793
    }
Jehan's avatar
Jehan committed
794 795 796 797 798 799 800 801 802 803 804
  else if (accel_key  >= GDK_KEY_0 &&
           accel_key  <= GDK_KEY_9 &&
           accel_mask == GDK_MOD1_MASK)
    {
      gimp_message (view->manager->gimp,
                    G_OBJECT (view), GIMP_MESSAGE_ERROR,
                    _("Alt+%d is used to switch to display %d and "
                      "cannot be remapped."),
                    accel_key - GDK_KEY_0,
                    accel_key == GDK_KEY_0 ? 10 : accel_key - GDK_KEY_0);
    }
805 806 807 808 809 810 811 812 813
  else if (! gtk_accel_map_change_entry (accel_path,
                                         accel_key, accel_mask, FALSE))
    {
      GtkTreeModel *model;
      GtkAction    *conflict_action = NULL;
      GtkTreeIter   iter;
      gboolean      iter_valid;

      model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
814
      model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
815 816 817 818

      for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
           iter_valid;
           iter_valid = gtk_tree_model_iter_next (model, &iter))
819
        {
820 821 822 823 824 825 826 827
          GtkTreeIter child_iter;
          gboolean    child_valid;

          for (child_valid = gtk_tree_model_iter_children (model,
                                                           &child_iter,
                                                           &iter);
               child_valid;
               child_valid = gtk_tree_model_iter_next (model, &child_iter))
828
            {
829 830 831 832 833 834 835 836 837
              guint           child_accel_key;
              GdkModifierType child_accel_mask;

              gtk_tree_model_get (model, &child_iter,
                                  GIMP_ACTION_VIEW_COLUMN_ACCEL_KEY,
                                  &child_accel_key,
                                  GIMP_ACTION_VIEW_COLUMN_ACCEL_MASK,
                                  &child_accel_mask,
                                  -1);
838

839 840
              if (accel_key  == child_accel_key &&
                  accel_mask == child_accel_mask)
841
                {
842
                  gtk_tree_model_get (model, &child_iter,
843 844
                                      GIMP_ACTION_VIEW_COLUMN_ACTION,
                                      &conflict_action,
845
                                      -1);
846
                  break;
847
                }
848 849
            }

850 851 852 853 854 855 856
          if (conflict_action)
            break;
        }

      if (conflict_action != action)
        {
          if (conflict_action)
857
            {
858 859 860 861 862 863 864 865
              gimp_action_view_conflict_confirm (view, conflict_action,
                                                 accel_key,
                                                 accel_mask,
                                                 accel_path);
              g_object_unref (conflict_action);
            }
          else
            {
866
              gimp_message_literal (view->manager->gimp,
867 868
                                    G_OBJECT (view), GIMP_MESSAGE_ERROR,
                                    _("Changing shortcut failed."));
869 870 871
            }
        }
    }
872
}
873

874 875 876 877 878
static void
gimp_action_view_accel_cleared (GtkCellRendererAccel *accel,
                                const char           *path_string,
                                GimpActionView       *view)
{
879 880 881 882
  GtkAction       *action;
  guint            action_accel_key;
  GdkModifierType  action_accel_mask;
  const gchar     *accel_path;
883

884
  accel_path = gimp_action_view_get_accel_action (view, path_string,
885 886 887
                                                  &action,
                                                  &action_accel_key,
                                                  &action_accel_mask);
888 889 890 891

  if (! accel_path)
    return;

892 893 894
  if (action_accel_key == GDK_KEY_F1)
    {
      gimp_message_literal (view->manager->gimp,
895 896
                            G_OBJECT (view), GIMP_MESSAGE_ERROR,
                            _("F1 cannot be remapped."));
897 898 899
      return;
    }

900 901
  if (! gtk_accel_map_change_entry (accel_path, 0, 0, FALSE))
    {
902
      gimp_message_literal (view->manager->gimp,
903 904
                            G_OBJECT (view), GIMP_MESSAGE_ERROR,
                            _("Removing shortcut failed."));
905
    }
906
}