gtkcellrendereraccel.c 24.4 KB
Newer Older
1
/* gtkcellrendereraccel.h
Matthias Clasen's avatar
Matthias Clasen committed
2 3 4 5 6 7 8 9 10 11 12 13 14
 * Copyright (C) 2000  Red Hat, Inc.,  Jonathan Blandford <jrb@redhat.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Matthias Clasen's avatar
Matthias Clasen committed
16 17 18
 */

#include "config.h"
19 20 21

#include "gtkcellrendereraccel.h"

Matthias Clasen's avatar
Matthias Clasen committed
22 23 24 25 26
#include "gtkintl.h"
#include "gtkaccelgroup.h"
#include "gtkmarshalers.h"
#include "gtklabel.h"
#include "gtkeventbox.h"
Matthias Clasen's avatar
Matthias Clasen committed
27
#include "gtkmain.h"
28
#include "gtksizerequest.h"
29
#include "gtktypebuiltins.h"
Matthias Clasen's avatar
Matthias Clasen committed
30 31 32
#include "gtkprivate.h"


33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/**
 * SECTION:gtkcellrendereraccel
 * @Short_description: Renders a keyboard accelerator in a cell
 * @Title: GtkCellRendererAccel
 *
 * #GtkCellRendererAccel displays a keyboard accelerator (i.e. a
 * key combination like <keycombo><keycap>Control</keycap><keycap>a</keycap></keycombo>.
 * If the cell renderer is editable, the accelerator can be changed by
 * simply typing the new combination.
 *
 * The #GtkCellRendererAccel cell renderer was added in GTK+ 2.10.
 */



48 49 50 51 52 53 54 55
static void gtk_cell_renderer_accel_get_property (GObject         *object,
                                                  guint            param_id,
                                                  GValue          *value,
                                                  GParamSpec      *pspec);
static void gtk_cell_renderer_accel_set_property (GObject         *object,
                                                  guint            param_id,
                                                  const GValue    *value,
                                                  GParamSpec      *pspec);
56 57 58 59 60
static void gtk_cell_renderer_accel_get_preferred_width 
                                                 (GtkCellRenderer *cell,
                                                  GtkWidget       *widget,
                                                  gint            *minimum_size,
                                                  gint            *natural_size);
61
static GtkCellEditable *
62 63 64 65 66 67 68
           gtk_cell_renderer_accel_start_editing (GtkCellRenderer      *cell,
                                                  GdkEvent             *event,
                                                  GtkWidget            *widget,
                                                  const gchar          *path,
                                                  const GdkRectangle   *background_area,
                                                  const GdkRectangle   *cell_area,
                                                  GtkCellRendererState  flags);
69 70 71 72
static gchar *convert_keysym_state_to_string     (GtkCellRendererAccel *accel,
                                                  guint                 keysym,
                                                  GdkModifierType       mask,
                                                  guint                 keycode);
Matthias Clasen's avatar
Matthias Clasen committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

enum {
  ACCEL_EDITED,
  ACCEL_CLEARED,
  LAST_SIGNAL
};

enum {
  PROP_0,
  PROP_ACCEL_KEY,
  PROP_ACCEL_MODS,
  PROP_KEYCODE,
  PROP_ACCEL_MODE
};

88
struct _GtkCellRendererAccelPrivate
89
{
90 91 92 93
  GtkWidget *edit_widget;
  GtkWidget *grab_widget;
  GtkWidget *sizing_label;

94 95
  GdkDevice *grab_keyboard;
  GdkDevice *grab_pointer;
96

97 98
  GtkCellRendererAccelMode accel_mode;

99 100 101 102
  GdkModifierType accel_mods;

  guint accel_key;
  guint keycode;
103 104
};

Matthias Clasen's avatar
Matthias Clasen committed
105 106
static guint signals[LAST_SIGNAL] = { 0 };

Matthias Clasen's avatar
Matthias Clasen committed
107
G_DEFINE_TYPE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT)
Matthias Clasen's avatar
Matthias Clasen committed
108 109

static void
110
gtk_cell_renderer_accel_init (GtkCellRendererAccel *cell_accel)
Matthias Clasen's avatar
Matthias Clasen committed
111
{
112 113
  gchar *text;

114 115
  cell_accel->priv = G_TYPE_INSTANCE_GET_PRIVATE (cell_accel,
                                                  GTK_TYPE_CELL_RENDERER_ACCEL,
116
                                                  GtkCellRendererAccelPrivate);
117

118 119 120
  text = convert_keysym_state_to_string (cell_accel, 0, 0, 0);
  g_object_set (cell_accel, "text", text, NULL);
  g_free (text);
Matthias Clasen's avatar
Matthias Clasen committed
121 122 123
}

static void
124
gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class)
Matthias Clasen's avatar
Matthias Clasen committed
125 126 127 128
{
  GObjectClass *object_class;
  GtkCellRendererClass *cell_renderer_class;

129 130
  object_class = G_OBJECT_CLASS (cell_accel_class);
  cell_renderer_class = GTK_CELL_RENDERER_CLASS (cell_accel_class);
Matthias Clasen's avatar
Matthias Clasen committed
131

132 133
  object_class->set_property = gtk_cell_renderer_accel_set_property;
  object_class->get_property = gtk_cell_renderer_accel_get_property;
Matthias Clasen's avatar
Matthias Clasen committed
134

135
  cell_renderer_class->get_preferred_width = gtk_cell_renderer_accel_get_preferred_width;
136
  cell_renderer_class->start_editing = gtk_cell_renderer_accel_start_editing;
Matthias Clasen's avatar
Matthias Clasen committed
137 138

  /**
139
   * GtkCellRendererAccel:accel-key:
Matthias Clasen's avatar
Matthias Clasen committed
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
   *
   * The keyval of the accelerator.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_ACCEL_KEY,
                                   g_param_spec_uint ("accel-key",
                                                     P_("Accelerator key"),
                                                     P_("The keyval of the accelerator"),
                                                      0,
                                                      G_MAXINT,
                                                      0,
                                                      GTK_PARAM_READWRITE));
  
  /**
156
   * GtkCellRendererAccel:accel-mods:
Matthias Clasen's avatar
Matthias Clasen committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
   *
   * The modifier mask of the accelerator.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_ACCEL_MODS,
                                   g_param_spec_flags ("accel-mods",
                                                       P_("Accelerator modifiers"),
                                                       P_("The modifier mask of the accelerator"),
                                                       GDK_TYPE_MODIFIER_TYPE,
                                                       0,
                                                       GTK_PARAM_READWRITE));

  /**
172
   * GtkCellRendererAccel:keycode:
Matthias Clasen's avatar
Matthias Clasen committed
173 174 175 176 177 178 179 180
   *
   * The hardware keycode of the accelerator. Note that the hardware keycode is
   * only relevant if the key does not have a keyval. Normally, the keyboard
   * configuration should assign keyvals to all keys.
   *
   * Since: 2.10
   */ 
  g_object_class_install_property (object_class,
181 182 183 184 185 186 187 188
                                   PROP_KEYCODE,
                                   g_param_spec_uint ("keycode",
                                                      P_("Accelerator keycode"),
                                                      P_("The hardware keycode of the accelerator"),
                                                      0,
                                                      G_MAXINT,
                                                      0,
                                                      GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
189 190

  /**
191
   * GtkCellRendererAccel:accel-mode:
Matthias Clasen's avatar
Matthias Clasen committed
192 193 194 195 196 197 198 199 200 201 202
   *
   * Determines if the edited accelerators are GTK+ accelerators. If
   * they are, consumed modifiers are suppressed, only accelerators
   * accepted by GTK+ are allowed, and the accelerators are rendered
   * in the same way as they are in menus.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_ACCEL_MODE,
                                   g_param_spec_enum ("accel-mode",
203 204 205 206 207
                                                      P_("Accelerator Mode"),
                                                      P_("The type of accelerators"),
                                                      GTK_TYPE_CELL_RENDERER_ACCEL_MODE,
                                                      GTK_CELL_RENDERER_ACCEL_MODE_GTK,
                                                      GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
208 209
  
  /**
210 211
   * GtkCellRendererAccel::accel-edited:
   * @accel: the object reveiving the signal
Matthias Clasen's avatar
Matthias Clasen committed
212 213 214 215 216 217 218 219 220 221
   * @path_string: the path identifying the row of the edited cell
   * @accel_key: the new accelerator keyval
   * @accel_mods: the new acclerator modifier mask
   * @hardware_keycode: the keycode of the new accelerator
   *
   * Gets emitted when the user has selected a new accelerator.
   *
   * Since: 2.10
   */
  signals[ACCEL_EDITED] = g_signal_new (I_("accel-edited"),
222 223 224 225 226 227 228 229 230 231
                                        GTK_TYPE_CELL_RENDERER_ACCEL,
                                        G_SIGNAL_RUN_LAST,
                                        G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_edited),
                                        NULL, NULL,
                                        _gtk_marshal_VOID__STRING_UINT_FLAGS_UINT,
                                        G_TYPE_NONE, 4,
                                        G_TYPE_STRING,
                                        G_TYPE_UINT,
                                        GDK_TYPE_MODIFIER_TYPE,
                                        G_TYPE_UINT);
Matthias Clasen's avatar
Matthias Clasen committed
232 233

  /**
234 235
   * GtkCellRendererAccel::accel-cleared:
   * @accel: the object reveiving the signal
Matthias Clasen's avatar
Matthias Clasen committed
236 237 238 239 240 241 242
   * @path_string: the path identifying the row of the edited cell
   *
   * Gets emitted when the user has removed the accelerator.
   *
   * Since: 2.10
   */
  signals[ACCEL_CLEARED] = g_signal_new (I_("accel-cleared"),
243 244 245 246 247 248 249
                                         GTK_TYPE_CELL_RENDERER_ACCEL,
                                         G_SIGNAL_RUN_LAST,
                                         G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_cleared),
                                         NULL, NULL,
                                         g_cclosure_marshal_VOID__STRING,
                                         G_TYPE_NONE, 1,
                                         G_TYPE_STRING);
250

251
  g_type_class_add_private (cell_accel_class, sizeof (GtkCellRendererAccelPrivate));
Matthias Clasen's avatar
Matthias Clasen committed
252 253 254 255
}


/**
256
 * gtk_cell_renderer_accel_new:
Matthias Clasen's avatar
Matthias Clasen committed
257
 *
258
 * Creates a new #GtkCellRendererAccel.
Matthias Clasen's avatar
Matthias Clasen committed
259 260 261 262 263 264
 * 
 * Returns: the new cell renderer
 *
 * Since: 2.10
 */
GtkCellRenderer *
265
gtk_cell_renderer_accel_new (void)
Matthias Clasen's avatar
Matthias Clasen committed
266
{
267
  return g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
268 269 270
}

static gchar *
271
convert_keysym_state_to_string (GtkCellRendererAccel *accel,
272
                                guint                 keysym,
273
                                GdkModifierType       mask,
274
                                guint                 keycode)
Matthias Clasen's avatar
Matthias Clasen committed
275
{
276
  GtkCellRendererAccelPrivate *priv = accel->priv;
277

Matthias Clasen's avatar
Matthias Clasen committed
278 279
  if (keysym == 0 && keycode == 0)
    /* This label is displayed in a treeview cell displaying
280
     * a disabled accelerator key combination.
Matthias Clasen's avatar
Matthias Clasen committed
281
     */
282
    return g_strdup (C_("Accelerator", "Disabled"));
Matthias Clasen's avatar
Matthias Clasen committed
283 284
  else 
    {
285
      if (priv->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
286 287 288 289 290 291 292 293 294 295
        {
          if (!gtk_accelerator_valid (keysym, mask))
            /* This label is displayed in a treeview cell displaying
             * an accelerator key combination that is not valid according
             * to gtk_accelerator_valid().
             */
            return g_strdup (C_("Accelerator", "Invalid"));

          return gtk_accelerator_get_label (keysym, mask);
        }
Matthias Clasen's avatar
Matthias Clasen committed
296
      else 
297 298
        {
          gchar *name;
Matthias Clasen's avatar
Matthias Clasen committed
299

300
          name = gtk_accelerator_get_label_with_keycode (NULL, keysym, keycode, mask);
301
          if (name == NULL)
302
            name = gtk_accelerator_name_with_keycode (NULL, keysym, keycode, mask);
Matthias Clasen's avatar
Matthias Clasen committed
303

304 305
          return name;
        }
Matthias Clasen's avatar
Matthias Clasen committed
306 307 308 309
    }
}

static void
310 311 312 313
gtk_cell_renderer_accel_get_property  (GObject    *object,
                                       guint       param_id,
                                       GValue     *value,
                                       GParamSpec *pspec)
Matthias Clasen's avatar
Matthias Clasen committed
314
{
315
  GtkCellRendererAccelPrivate *priv = GTK_CELL_RENDERER_ACCEL (object)->priv;
Matthias Clasen's avatar
Matthias Clasen committed
316 317 318 319

  switch (param_id)
    {
    case PROP_ACCEL_KEY:
320
      g_value_set_uint (value, priv->accel_key);
Matthias Clasen's avatar
Matthias Clasen committed
321 322 323
      break;

    case PROP_ACCEL_MODS:
324
      g_value_set_flags (value, priv->accel_mods);
Matthias Clasen's avatar
Matthias Clasen committed
325 326
      break;

327
    case PROP_KEYCODE:
328
      g_value_set_uint (value, priv->keycode);
329 330
      break;

Matthias Clasen's avatar
Matthias Clasen committed
331
    case PROP_ACCEL_MODE:
332
      g_value_set_enum (value, priv->accel_mode);
Matthias Clasen's avatar
Matthias Clasen committed
333 334 335 336 337 338 339 340
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
    }
}

static void
341 342 343 344
gtk_cell_renderer_accel_set_property  (GObject      *object,
                                       guint         param_id,
                                       const GValue *value,
                                       GParamSpec   *pspec)
Matthias Clasen's avatar
Matthias Clasen committed
345
{
346
  GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (object);
347
  GtkCellRendererAccelPrivate *priv = accel->priv;
Matthias Clasen's avatar
Matthias Clasen committed
348 349 350 351 352 353
  gboolean changed = FALSE;

  switch (param_id)
    {
    case PROP_ACCEL_KEY:
      {
354
        guint accel_key = g_value_get_uint (value);
Matthias Clasen's avatar
Matthias Clasen committed
355

356 357 358 359 360
        if (priv->accel_key != accel_key)
          {
            priv->accel_key = accel_key;
            changed = TRUE;
          }
Matthias Clasen's avatar
Matthias Clasen committed
361 362 363 364 365
      }
      break;

    case PROP_ACCEL_MODS:
      {
366
        guint accel_mods = g_value_get_flags (value);
Matthias Clasen's avatar
Matthias Clasen committed
367

368 369 370 371 372
        if (priv->accel_mods != accel_mods)
          {
            priv->accel_mods = accel_mods;
            changed = TRUE;
          }
Matthias Clasen's avatar
Matthias Clasen committed
373 374 375 376
      }
      break;
    case PROP_KEYCODE:
      {
377
        guint keycode = g_value_get_uint (value);
Matthias Clasen's avatar
Matthias Clasen committed
378

379 380 381 382 383
        if (priv->keycode != keycode)
          {
            priv->keycode = keycode;
            changed = TRUE;
          }
Matthias Clasen's avatar
Matthias Clasen committed
384 385 386 387
      }
      break;

    case PROP_ACCEL_MODE:
388
      priv->accel_mode = g_value_get_enum (value);
Matthias Clasen's avatar
Matthias Clasen committed
389 390 391 392 393 394 395 396 397 398
      break;
      
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
    }

  if (changed)
    {
      gchar *text;

399
      text = convert_keysym_state_to_string (accel, priv->accel_key, priv->accel_mods, priv->keycode);
400
      g_object_set (accel, "text", text, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
401 402 403 404 405
      g_free (text);
    }
}

static void
406 407 408 409
gtk_cell_renderer_accel_get_preferred_width (GtkCellRenderer    *cell,
                                             GtkWidget          *widget,
                                             gint               *minimum_size,
                                             gint               *natural_size)
Matthias Clasen's avatar
Matthias Clasen committed
410 411

{
412
  GtkCellRendererAccelPrivate *priv = GTK_CELL_RENDERER_ACCEL (cell)->priv;
413
  GtkRequisition min_req, nat_req;
Matthias Clasen's avatar
Matthias Clasen committed
414

415 416
  if (priv->sizing_label == NULL)
    priv->sizing_label = gtk_label_new (_("New accelerator..."));
Matthias Clasen's avatar
Matthias Clasen committed
417

418
  gtk_widget_get_preferred_size (priv->sizing_label, &min_req, &nat_req);
419

420 421
  GTK_CELL_RENDERER_CLASS (gtk_cell_renderer_accel_parent_class)->get_preferred_width (cell, widget,
                                                                                       minimum_size, natural_size);
422

Matthias Clasen's avatar
Matthias Clasen committed
423
  /* FIXME: need to take the cell_area et al. into account */
424 425 426 427
  if (minimum_size)
    *minimum_size = MAX (*minimum_size, min_req.width);
  if (natural_size)
    *natural_size = MAX (*natural_size, nat_req.width);
Matthias Clasen's avatar
Matthias Clasen committed
428 429 430
}

static gboolean
431 432 433
grab_key_callback (GtkWidget            *widget,
                   GdkEventKey          *event,
                   GtkCellRendererAccel *accel)
Matthias Clasen's avatar
Matthias Clasen committed
434
{
435
  GtkCellRendererAccelPrivate *priv = accel->priv;
Matthias Clasen's avatar
Matthias Clasen committed
436 437
  GdkModifierType accel_mods = 0;
  guint accel_key;
438
  guint keyval;
439
  gchar *path;
Matthias Clasen's avatar
Matthias Clasen committed
440 441
  gboolean edited;
  gboolean cleared;
442
  GdkModifierType consumed_modifiers;
Matthias Clasen's avatar
Matthias Clasen committed
443 444 445
  GdkDisplay *display;

  display = gtk_widget_get_display (widget);
446

Matthias Clasen's avatar
Matthias Clasen committed
447 448 449 450 451 452
  if (event->is_modifier)
    return TRUE;

  edited = FALSE;
  cleared = FALSE;

453 454
  accel_mods = event->state;

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
  if (event->keyval == GDK_KEY_Sys_Req && 
      (accel_mods & GDK_MOD1_MASK) != 0)
    {
      /* HACK: we don't want to use SysRq as a keybinding (but we do
       * want Alt+Print), so we avoid translation from Alt+Print to SysRq
       */
      keyval = GDK_KEY_Print;
      consumed_modifiers = 0;
    }
  else
    {
      _gtk_translate_keyboard_accel_state (gdk_keymap_get_for_display (display),
                                           event->hardware_keycode,
                                           event->state,
                                           gtk_accelerator_get_default_mod_mask (),
                                           event->group,
                                           &keyval, NULL, NULL, &consumed_modifiers);
    }
Matthias Clasen's avatar
Matthias Clasen committed
473

474
  accel_key = gdk_keyval_to_lower (keyval);
475 476
  if (accel_key == GDK_KEY_ISO_Left_Tab) 
    accel_key = GDK_KEY_Tab;
Matthias Clasen's avatar
Matthias Clasen committed
477

478
  accel_mods &= gtk_accelerator_get_default_mod_mask ();
Matthias Clasen's avatar
Matthias Clasen committed
479 480 481

  /* Filter consumed modifiers 
   */
482
  if (priv->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
Matthias Clasen's avatar
Matthias Clasen committed
483 484 485 486
    accel_mods &= ~consumed_modifiers;
  
  /* Put shift back if it changed the case of the key, not otherwise.
   */
487
  if (accel_key != keyval)
Matthias Clasen's avatar
Matthias Clasen committed
488 489 490 491
    accel_mods |= GDK_SHIFT_MASK;
    
  if (accel_mods == 0)
    {
492 493 494 495 496 497 498 499 500 501 502
      switch (keyval)
	{
	case GDK_KEY_Escape:
	  goto out; /* cancel */
	case GDK_KEY_BackSpace:
	  /* clear the accelerator on Backspace */
	  cleared = TRUE;
	  goto out;
	default:
	  break;
	}
Matthias Clasen's avatar
Matthias Clasen committed
503 504
    }

505
  if (priv->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
Matthias Clasen's avatar
Matthias Clasen committed
506 507
    {
      if (!gtk_accelerator_valid (accel_key, accel_mods))
508 509
        {
          gtk_widget_error_bell (widget);
Matthias Clasen's avatar
Matthias Clasen committed
510

511 512
          return TRUE;
        }
Matthias Clasen's avatar
Matthias Clasen committed
513 514 515 516 517
    }

  edited = TRUE;

 out:
518
  gtk_device_grab_remove (priv->grab_widget, priv->grab_pointer);
519 520
  gdk_device_ungrab (priv->grab_keyboard, event->time);
  gdk_device_ungrab (priv->grab_pointer, event->time);
521

522
  path = g_strdup (g_object_get_data (G_OBJECT (priv->edit_widget), "gtk-cell-renderer-text"));
Matthias Clasen's avatar
Matthias Clasen committed
523

524 525 526 527
  gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (priv->edit_widget));
  gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (priv->edit_widget));
  priv->edit_widget = NULL;
  priv->grab_widget = NULL;
528 529 530
  priv->grab_keyboard = NULL;
  priv->grab_pointer = NULL;

Matthias Clasen's avatar
Matthias Clasen committed
531
  if (edited)
532
    g_signal_emit (accel, signals[ACCEL_EDITED], 0, path, 
533
                   accel_key, accel_mods, event->hardware_keycode);
Matthias Clasen's avatar
Matthias Clasen committed
534
  else if (cleared)
535
    g_signal_emit (accel, signals[ACCEL_CLEARED], 0, path);
Matthias Clasen's avatar
Matthias Clasen committed
536 537 538 539 540 541 542

  g_free (path);

  return TRUE;
}

static void
543 544
ungrab_stuff (GtkWidget            *widget,
              GtkCellRendererAccel *accel)
Matthias Clasen's avatar
Matthias Clasen committed
545
{
546
  GtkCellRendererAccelPrivate *priv = accel->priv;
547

548
  gtk_device_grab_remove (priv->grab_widget, priv->grab_pointer);
549 550
  gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
  gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
Matthias Clasen's avatar
Matthias Clasen committed
551

552 553
  priv->grab_keyboard = NULL;
  priv->grab_pointer = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
554

555
  g_signal_handlers_disconnect_by_func (priv->grab_widget,
556 557
                                        G_CALLBACK (grab_key_callback),
                                        accel);
Matthias Clasen's avatar
Matthias Clasen committed
558 559 560 561
}

static void
_gtk_cell_editable_event_box_start_editing (GtkCellEditable *cell_editable,
562
                                            GdkEvent        *event)
Matthias Clasen's avatar
Matthias Clasen committed
563 564 565 566 567 568 569 570 571 572
{
  /* do nothing, because we are pointless */
}

static void
_gtk_cell_editable_event_box_cell_editable_init (GtkCellEditableIface *iface)
{
  iface->start_editing = _gtk_cell_editable_event_box_start_editing;
}

573 574 575 576 577 578 579 580
typedef struct _GtkCellEditableEventBox GtkCellEditableEventBox;
typedef         GtkEventBoxClass        GtkCellEditableEventBoxClass;

struct _GtkCellEditableEventBox
{
  GtkEventBox box;
  gboolean editing_canceled;
};
Matthias Clasen's avatar
Matthias Clasen committed
581 582 583

G_DEFINE_TYPE_WITH_CODE (GtkCellEditableEventBox, _gtk_cell_editable_event_box, GTK_TYPE_EVENT_BOX, { \
    G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, _gtk_cell_editable_event_box_cell_editable_init)   \
584
      })
Matthias Clasen's avatar
Matthias Clasen committed
585

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
enum {
  PROP_ZERO,
  PROP_EDITING_CANCELED
};

static void
gtk_cell_editable_event_box_set_property (GObject      *object,
                                          guint         prop_id,
                                          const GValue *value,
                                          GParamSpec   *pspec)
{
  GtkCellEditableEventBox *box = (GtkCellEditableEventBox*)object;

  switch (prop_id)
    {
    case PROP_EDITING_CANCELED:
      box->editing_canceled = g_value_get_boolean (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_cell_editable_event_box_get_property (GObject    *object,
                                          guint       prop_id,
                                          GValue     *value,
                                          GParamSpec *pspec)
{
  GtkCellEditableEventBox *box = (GtkCellEditableEventBox*)object;

  switch (prop_id)
    {
    case PROP_EDITING_CANCELED:
      g_value_set_boolean (value, box->editing_canceled);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}
Matthias Clasen's avatar
Matthias Clasen committed
628 629 630 631

static void
_gtk_cell_editable_event_box_class_init (GtkCellEditableEventBoxClass *class)
{
632 633 634 635 636 637 638 639
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);

  gobject_class->set_property = gtk_cell_editable_event_box_set_property;
  gobject_class->get_property = gtk_cell_editable_event_box_get_property;

  g_object_class_override_property (gobject_class,
                                    PROP_EDITING_CANCELED,
                                    "editing-canceled");
Matthias Clasen's avatar
Matthias Clasen committed
640 641 642 643 644 645 646 647
}

static void
_gtk_cell_editable_event_box_init (GtkCellEditableEventBox *box)
{
}

static GtkCellEditable *
648 649 650 651
gtk_cell_renderer_accel_start_editing (GtkCellRenderer      *cell,
                                       GdkEvent             *event,
                                       GtkWidget            *widget,
                                       const gchar          *path,
652 653
                                       const GdkRectangle   *background_area,
                                       const GdkRectangle   *cell_area,
654
                                       GtkCellRendererState  flags)
Matthias Clasen's avatar
Matthias Clasen committed
655
{
656
  GtkCellRendererAccelPrivate *priv;
Matthias Clasen's avatar
Matthias Clasen committed
657
  GtkCellRendererText *celltext;
658
  GtkCellRendererAccel *accel;
659 660
  GtkStyleContext *context;
  GdkRGBA color;
Matthias Clasen's avatar
Matthias Clasen committed
661 662
  GtkWidget *label;
  GtkWidget *eventbox;
663
  GdkDevice *device, *keyb, *pointer;
664
  GdkWindow *window;
665
  gboolean editable;
666 667
  guint32 time;

Matthias Clasen's avatar
Matthias Clasen committed
668
  celltext = GTK_CELL_RENDERER_TEXT (cell);
669
  accel = GTK_CELL_RENDERER_ACCEL (cell);
670
  priv = accel->priv;
Matthias Clasen's avatar
Matthias Clasen committed
671 672

  /* If the cell isn't editable we return NULL. */
673 674
  g_object_get (celltext, "editable", &editable, NULL);
  if (editable == FALSE)
Matthias Clasen's avatar
Matthias Clasen committed
675 676
    return NULL;

677
  window = gtk_widget_get_window (widget);
678
  context = gtk_widget_get_style_context (widget);
679 680

  g_return_val_if_fail (window != NULL, NULL);
681 682 683 684 685 686 687 688 689

  if (event)
    device = gdk_event_get_device (event);
  else
    device = gtk_get_current_event_device ();

  if (!device)
    return NULL;

690
  if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
691 692 693 694 695 696 697 698 699 700 701 702
    {
      keyb = device;
      pointer = gdk_device_get_associated_device (device);
    }
  else
    {
      pointer = device;
      keyb = gdk_device_get_associated_device (device);
    }

  time = gdk_event_get_time (event);

703
  if (gdk_device_grab (keyb, window,
704 705 706
                       GDK_OWNERSHIP_WINDOW, FALSE,
                       GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
                       NULL, time) != GDK_GRAB_SUCCESS)
Matthias Clasen's avatar
Matthias Clasen committed
707 708
    return NULL;

709
  if (gdk_device_grab (pointer, window,
710 711 712
                       GDK_OWNERSHIP_WINDOW, FALSE,
                       GDK_BUTTON_PRESS_MASK,
                       NULL, time) != GDK_GRAB_SUCCESS)
Matthias Clasen's avatar
Matthias Clasen committed
713
    {
714
      gdk_device_ungrab (keyb, time);
Matthias Clasen's avatar
Matthias Clasen committed
715 716
      return NULL;
    }
717 718 719

  priv->grab_keyboard = keyb;
  priv->grab_pointer = pointer;
720
  priv->grab_widget = widget;
Matthias Clasen's avatar
Matthias Clasen committed
721

722
  g_signal_connect (G_OBJECT (widget), "key-press-event",
Matthias Clasen's avatar
Matthias Clasen committed
723
                    G_CALLBACK (grab_key_callback),
724
                    accel);
Matthias Clasen's avatar
Matthias Clasen committed
725 726

  eventbox = g_object_new (_gtk_cell_editable_event_box_get_type (), NULL);
727 728 729
  priv->edit_widget = eventbox;
  g_object_add_weak_pointer (G_OBJECT (priv->edit_widget),
                             (gpointer) &priv->edit_widget);
Matthias Clasen's avatar
Matthias Clasen committed
730 731
  
  label = gtk_label_new (NULL);
732 733
  gtk_widget_set_halign (label, GTK_ALIGN_START);
  gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
734

735
  gtk_style_context_get_background_color (context, GTK_STATE_FLAG_SELECTED, &color);
736
  gtk_widget_override_background_color (eventbox, 0, &color);
Matthias Clasen's avatar
Matthias Clasen committed
737

738 739
  gtk_style_context_get_color (context, GTK_STATE_FLAG_SELECTED, &color);
  gtk_widget_override_color (label, 0, &color);
740

Matthias Clasen's avatar
Matthias Clasen committed
741 742 743 744 745 746 747 748
  /* This label is displayed in a treeview cell displaying
   * an accelerator when the cell is clicked to change the 
   * acelerator.
   */
  gtk_label_set_text (GTK_LABEL (label), _("New accelerator..."));

  gtk_container_add (GTK_CONTAINER (eventbox), label);
  
749
  g_object_set_data_full (G_OBJECT (priv->edit_widget), "gtk-cell-renderer-text",
Matthias Clasen's avatar
Matthias Clasen committed
750 751
                          g_strdup (path), g_free);
  
752
  gtk_widget_show_all (priv->edit_widget);
Matthias Clasen's avatar
Matthias Clasen committed
753

754
  gtk_device_grab_add (priv->grab_widget, pointer, TRUE);
755

756
  g_signal_connect (priv->edit_widget, "unrealize",
757
                    G_CALLBACK (ungrab_stuff), accel);
Matthias Clasen's avatar
Matthias Clasen committed
758
  
759
  return GTK_CELL_EDITABLE (priv->edit_widget);
Matthias Clasen's avatar
Matthias Clasen committed
760
}