gtkcellrendereraccel.c 25 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
#include "gtkprivate.h"

32

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


46 47 48 49 50 51 52 53
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);
54 55 56 57 58
static void gtk_cell_renderer_accel_get_preferred_width 
                                                 (GtkCellRenderer *cell,
                                                  GtkWidget       *widget,
                                                  gint            *minimum_size,
                                                  gint            *natural_size);
59
static GtkCellEditable *
60 61 62 63 64 65 66
           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);
67 68 69 70
static gchar *convert_keysym_state_to_string     (GtkCellRendererAccel *accel,
                                                  guint                 keysym,
                                                  GdkModifierType       mask,
                                                  guint                 keycode);
71 72 73
static GtkWidget *gtk_cell_editable_event_box_new (GtkCellRenderer          *cell,
                                                   GtkCellRendererAccelMode  mode,
                                                   const gchar              *path);
Matthias Clasen's avatar
Matthias Clasen committed
74 75 76 77 78 79 80 81

enum {
  ACCEL_EDITED,
  ACCEL_CLEARED,
  LAST_SIGNAL
};

enum {
82
  PROP_ACCEL_KEY = 1,
Matthias Clasen's avatar
Matthias Clasen committed
83 84 85 86 87
  PROP_ACCEL_MODS,
  PROP_KEYCODE,
  PROP_ACCEL_MODE
};

88 89
static guint signals[LAST_SIGNAL] = { 0 };

90
struct _GtkCellRendererAccelPrivate
91
{
92 93
  GtkWidget *sizing_label;

94
  GtkCellRendererAccelMode accel_mode;
95 96 97
  GdkModifierType accel_mods;
  guint accel_key;
  guint keycode;
98

99 100
  GdkDevice *grab_pointer;
};
Matthias Clasen's avatar
Matthias Clasen committed
101

102
G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT)
Matthias Clasen's avatar
Matthias Clasen committed
103 104

static void
105
gtk_cell_renderer_accel_init (GtkCellRendererAccel *cell_accel)
Matthias Clasen's avatar
Matthias Clasen committed
106
{
107 108
  gchar *text;

109
  cell_accel->priv = gtk_cell_renderer_accel_get_instance_private (cell_accel);
110

111 112 113
  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
114 115 116
}

static void
117
gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class)
Matthias Clasen's avatar
Matthias Clasen committed
118 119 120 121
{
  GObjectClass *object_class;
  GtkCellRendererClass *cell_renderer_class;

122 123
  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
124

125 126
  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
127

128
  cell_renderer_class->get_preferred_width = gtk_cell_renderer_accel_get_preferred_width;
129
  cell_renderer_class->start_editing = gtk_cell_renderer_accel_start_editing;
Matthias Clasen's avatar
Matthias Clasen committed
130 131

  /**
132
   * GtkCellRendererAccel:accel-key:
Matthias Clasen's avatar
Matthias Clasen committed
133 134 135 136 137 138 139 140 141 142 143 144 145
   *
   * 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,
146
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Matthias Clasen's avatar
Matthias Clasen committed
147 148
  
  /**
149
   * GtkCellRendererAccel:accel-mods:
Matthias Clasen's avatar
Matthias Clasen committed
150 151 152 153 154 155 156 157 158 159 160 161
   *
   * 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,
162
                                                       GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Matthias Clasen's avatar
Matthias Clasen committed
163 164

  /**
165
   * GtkCellRendererAccel:keycode:
Matthias Clasen's avatar
Matthias Clasen committed
166 167 168 169 170 171 172 173
   *
   * 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,
174 175 176 177 178 179 180
                                   PROP_KEYCODE,
                                   g_param_spec_uint ("keycode",
                                                      P_("Accelerator keycode"),
                                                      P_("The hardware keycode of the accelerator"),
                                                      0,
                                                      G_MAXINT,
                                                      0,
181
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Matthias Clasen's avatar
Matthias Clasen committed
182 183

  /**
184
   * GtkCellRendererAccel:accel-mode:
Matthias Clasen's avatar
Matthias Clasen committed
185 186 187 188 189 190 191 192 193 194 195
   *
   * 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",
196 197 198 199
                                                      P_("Accelerator Mode"),
                                                      P_("The type of accelerators"),
                                                      GTK_TYPE_CELL_RENDERER_ACCEL_MODE,
                                                      GTK_CELL_RENDERER_ACCEL_MODE_GTK,
200
                                                      GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
Matthias Clasen's avatar
Matthias Clasen committed
201 202
  
  /**
203 204
   * GtkCellRendererAccel::accel-edited:
   * @accel: the object reveiving the signal
Matthias Clasen's avatar
Matthias Clasen committed
205 206 207 208 209 210 211 212 213 214
   * @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"),
215 216 217 218 219 220 221 222 223 224
                                        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
225 226

  /**
227 228
   * GtkCellRendererAccel::accel-cleared:
   * @accel: the object reveiving the signal
Matthias Clasen's avatar
Matthias Clasen committed
229 230 231 232 233 234 235
   * @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"),
236 237 238 239 240 241 242
                                         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);
Matthias Clasen's avatar
Matthias Clasen committed
243 244 245 246
}


/**
247
 * gtk_cell_renderer_accel_new:
Matthias Clasen's avatar
Matthias Clasen committed
248
 *
249
 * Creates a new #GtkCellRendererAccel.
Matthias Clasen's avatar
Matthias Clasen committed
250 251 252 253 254 255
 * 
 * Returns: the new cell renderer
 *
 * Since: 2.10
 */
GtkCellRenderer *
256
gtk_cell_renderer_accel_new (void)
Matthias Clasen's avatar
Matthias Clasen committed
257
{
258
  return g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL, NULL);
Matthias Clasen's avatar
Matthias Clasen committed
259 260 261
}

static gchar *
262
convert_keysym_state_to_string (GtkCellRendererAccel *accel,
263
                                guint                 keysym,
264
                                GdkModifierType       mask,
265
                                guint                 keycode)
Matthias Clasen's avatar
Matthias Clasen committed
266
{
267
  GtkCellRendererAccelPrivate *priv = accel->priv;
268

Matthias Clasen's avatar
Matthias Clasen committed
269 270
  if (keysym == 0 && keycode == 0)
    /* This label is displayed in a treeview cell displaying
271
     * a disabled accelerator key combination.
Matthias Clasen's avatar
Matthias Clasen committed
272
     */
273
    return g_strdup (C_("Accelerator", "Disabled"));
Matthias Clasen's avatar
Matthias Clasen committed
274 275
  else 
    {
276
      if (priv->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
277 278 279 280 281 282 283 284 285 286
        {
          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
287
      else 
288 289
        {
          gchar *name;
Matthias Clasen's avatar
Matthias Clasen committed
290

291
          name = gtk_accelerator_get_label_with_keycode (NULL, keysym, keycode, mask);
292
          if (name == NULL)
293
            name = gtk_accelerator_name_with_keycode (NULL, keysym, keycode, mask);
Matthias Clasen's avatar
Matthias Clasen committed
294

295 296
          return name;
        }
Matthias Clasen's avatar
Matthias Clasen committed
297 298 299 300
    }
}

static void
301 302 303 304
gtk_cell_renderer_accel_get_property (GObject    *object,
                                      guint       param_id,
                                      GValue     *value,
                                      GParamSpec *pspec)
Matthias Clasen's avatar
Matthias Clasen committed
305
{
306
  GtkCellRendererAccelPrivate *priv = GTK_CELL_RENDERER_ACCEL (object)->priv;
Matthias Clasen's avatar
Matthias Clasen committed
307 308 309 310

  switch (param_id)
    {
    case PROP_ACCEL_KEY:
311
      g_value_set_uint (value, priv->accel_key);
Matthias Clasen's avatar
Matthias Clasen committed
312 313 314
      break;

    case PROP_ACCEL_MODS:
315
      g_value_set_flags (value, priv->accel_mods);
Matthias Clasen's avatar
Matthias Clasen committed
316 317
      break;

318
    case PROP_KEYCODE:
319
      g_value_set_uint (value, priv->keycode);
320 321
      break;

Matthias Clasen's avatar
Matthias Clasen committed
322
    case PROP_ACCEL_MODE:
323
      g_value_set_enum (value, priv->accel_mode);
Matthias Clasen's avatar
Matthias Clasen committed
324 325 326 327 328 329 330 331
      break;

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

static void
332 333 334 335
gtk_cell_renderer_accel_set_property (GObject      *object,
                                      guint         param_id,
                                      const GValue *value,
                                      GParamSpec   *pspec)
Matthias Clasen's avatar
Matthias Clasen committed
336
{
337
  GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (object);
338
  GtkCellRendererAccelPrivate *priv = accel->priv;
Matthias Clasen's avatar
Matthias Clasen committed
339 340 341 342 343 344
  gboolean changed = FALSE;

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

347 348 349 350
        if (priv->accel_key != accel_key)
          {
            priv->accel_key = accel_key;
            changed = TRUE;
351
            g_object_notify (object, "accel-key");
352
          }
Matthias Clasen's avatar
Matthias Clasen committed
353 354 355 356 357
      }
      break;

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

360 361 362 363
        if (priv->accel_mods != accel_mods)
          {
            priv->accel_mods = accel_mods;
            changed = TRUE;
364
            g_object_notify (object, "accel-mods");
365
          }
Matthias Clasen's avatar
Matthias Clasen committed
366 367 368 369
      }
      break;
    case PROP_KEYCODE:
      {
370
        guint keycode = g_value_get_uint (value);
Matthias Clasen's avatar
Matthias Clasen committed
371

372 373 374 375
        if (priv->keycode != keycode)
          {
            priv->keycode = keycode;
            changed = TRUE;
376
            g_object_notify (object, "keycode");
377
          }
Matthias Clasen's avatar
Matthias Clasen committed
378 379 380 381
      }
      break;

    case PROP_ACCEL_MODE:
382 383 384 385 386
      if (priv->accel_mode != g_value_get_enum (value))
        {
          priv->accel_mode = g_value_get_enum (value);
          g_object_notify (object, "accel-mode");
        }
Matthias Clasen's avatar
Matthias Clasen committed
387 388 389 390 391 392 393 394 395 396
      break;
      
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
    }

  if (changed)
    {
      gchar *text;

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

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

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

413
  if (priv->sizing_label == NULL)
414
    priv->sizing_label = gtk_label_new (_("New accelerator…"));
Matthias Clasen's avatar
Matthias Clasen committed
415

416
  gtk_widget_get_preferred_size (priv->sizing_label, &min_req, &nat_req);
417

418 419
  GTK_CELL_RENDERER_CLASS (gtk_cell_renderer_accel_parent_class)->get_preferred_width (cell, widget,
                                                                                       minimum_size, natural_size);
420

Matthias Clasen's avatar
Matthias Clasen committed
421
  /* FIXME: need to take the cell_area et al. into account */
422 423 424 425
  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
426 427
}

428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
static GtkCellEditable *
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)
{
  GtkCellRendererAccelPrivate *priv;
  GtkCellRendererText *celltext;
  GtkCellRendererAccel *accel;
  GtkWidget *label;
  GtkWidget *eventbox;
  gboolean editable;
443
  GdkDevice *device, *pointer;
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
  GdkWindow *window;

  celltext = GTK_CELL_RENDERER_TEXT (cell);
  accel = GTK_CELL_RENDERER_ACCEL (cell);
  priv = accel->priv;

  /* If the cell isn't editable we return NULL. */
  g_object_get (celltext, "editable", &editable, NULL);
  if (!editable)
    return NULL;

  window = gtk_widget_get_window (gtk_widget_get_toplevel (widget));

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

  if (!device || !window)
    return NULL;

  if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
466
    pointer = gdk_device_get_associated_device (device);
467
  else
468
    pointer = device;
469

470 471 472
  if (gdk_seat_grab (gdk_device_get_seat (pointer), window,
                     GDK_SEAT_CAPABILITY_ALL, FALSE,
                     NULL, event, NULL, NULL) != GDK_GRAB_SUCCESS)
473 474 475 476 477 478 479 480 481 482
    return NULL;

  priv->grab_pointer = pointer;

  eventbox = gtk_cell_editable_event_box_new (cell, priv->accel_mode, path);

  label = gtk_label_new (NULL);
  gtk_widget_set_halign (label, GTK_ALIGN_START);
  gtk_widget_set_valign (label, GTK_ALIGN_CENTER);

483
  gtk_widget_set_state_flags (label, GTK_STATE_FLAG_SELECTED, TRUE);
484

485 486
  /* This label is displayed in a treeview cell displaying an accelerator
   * when the cell is clicked to change the acelerator.
487 488 489 490 491 492
   */
  gtk_label_set_text (GTK_LABEL (label), _("New accelerator…"));

  gtk_container_add (GTK_CONTAINER (eventbox), label);

  gtk_widget_show_all (eventbox);
493
  gtk_grab_add (eventbox);
494 495 496 497 498 499

  return GTK_CELL_EDITABLE (eventbox);
}

static void
gtk_cell_renderer_accel_ungrab (GtkCellRendererAccel *accel)
Matthias Clasen's avatar
Matthias Clasen committed
500
{
501
  GtkCellRendererAccelPrivate *priv = accel->priv;
502

503
  if (priv->grab_pointer)
504
    {
505
      gdk_seat_ungrab (gdk_device_get_seat (priv->grab_pointer));
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
      priv->grab_pointer = NULL;
    }
}

/* --------------------------------- */

typedef struct _GtkCellEditableEventBox GtkCellEditableEventBox;
typedef         GtkEventBoxClass        GtkCellEditableEventBoxClass;

struct _GtkCellEditableEventBox
{
  GtkEventBox box;
  gboolean editing_canceled;
  GtkCellRendererAccelMode accel_mode;
  gchar *path;
  GtkCellRenderer *cell;
};

enum {
  PROP_EDITING_CANCELED = 1,
  PROP_MODE,
  PROP_PATH
};

GType       gtk_cell_editable_event_box_get_type (void);
static void gtk_cell_editable_event_box_cell_editable_init (GtkCellEditableIface *iface);

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))

static void
gtk_cell_editable_event_box_start_editing (GtkCellEditable *cell_editable,
                                           GdkEvent        *event)
{
  /* 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;
}

static gboolean
gtk_cell_editable_event_box_key_press_event (GtkWidget   *widget,
                                             GdkEventKey *event)
{
  GtkCellEditableEventBox *box = (GtkCellEditableEventBox*)widget;
Matthias Clasen's avatar
Matthias Clasen committed
554 555
  GdkModifierType accel_mods = 0;
  guint accel_key;
556
  guint keyval;
Matthias Clasen's avatar
Matthias Clasen committed
557 558
  gboolean edited;
  gboolean cleared;
559
  GdkModifierType consumed_modifiers;
Matthias Clasen's avatar
Matthias Clasen committed
560 561 562
  GdkDisplay *display;

  display = gtk_widget_get_display (widget);
563

Matthias Clasen's avatar
Matthias Clasen committed
564 565 566 567 568 569
  if (event->is_modifier)
    return TRUE;

  edited = FALSE;
  cleared = FALSE;

570 571
  accel_mods = event->state;

572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
  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
590

591
  accel_key = gdk_keyval_to_lower (keyval);
592 593
  if (accel_key == GDK_KEY_ISO_Left_Tab) 
    accel_key = GDK_KEY_Tab;
Matthias Clasen's avatar
Matthias Clasen committed
594

595
  accel_mods &= gtk_accelerator_get_default_mod_mask ();
Matthias Clasen's avatar
Matthias Clasen committed
596 597 598

  /* Filter consumed modifiers 
   */
599
  if (box->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK)
Matthias Clasen's avatar
Matthias Clasen committed
600 601 602 603
    accel_mods &= ~consumed_modifiers;
  
  /* Put shift back if it changed the case of the key, not otherwise.
   */
604
  if (accel_key != keyval)
Matthias Clasen's avatar
Matthias Clasen committed
605 606 607 608
    accel_mods |= GDK_SHIFT_MASK;
    
  if (accel_mods == 0)
    {
609 610 611 612
      switch (keyval)
	{
	case GDK_KEY_BackSpace:
	  cleared = TRUE;
613 614
          /* fall thru */
	case GDK_KEY_Escape:
615 616 617 618
	  goto out;
	default:
	  break;
	}
Matthias Clasen's avatar
Matthias Clasen committed
619 620
    }

621 622
  if (box->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK &&
      !gtk_accelerator_valid (accel_key, accel_mods))
Matthias Clasen's avatar
Matthias Clasen committed
623
    {
624 625
      gtk_widget_error_bell (widget);
      return TRUE;
Matthias Clasen's avatar
Matthias Clasen committed
626 627 628 629 630
    }

  edited = TRUE;

 out:
631
  gtk_grab_remove (widget);
632 633 634
  gtk_cell_renderer_accel_ungrab (GTK_CELL_RENDERER_ACCEL (box->cell));
  gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget));
  gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget));
635

Matthias Clasen's avatar
Matthias Clasen committed
636
  if (edited)
637
    g_signal_emit (box->cell, signals[ACCEL_EDITED], 0, box->path,
638
                   accel_key, accel_mods, event->hardware_keycode);
Matthias Clasen's avatar
Matthias Clasen committed
639
  else if (cleared)
640
    g_signal_emit (box->cell, signals[ACCEL_CLEARED], 0, box->path);
Matthias Clasen's avatar
Matthias Clasen committed
641 642 643 644 645

  return TRUE;
}

static void
646
gtk_cell_editable_event_box_unrealize (GtkWidget *widget)
Matthias Clasen's avatar
Matthias Clasen committed
647
{
648
  GtkCellEditableEventBox *box = (GtkCellEditableEventBox*)widget;
Matthias Clasen's avatar
Matthias Clasen committed
649

650
  gtk_grab_remove (widget);
651 652 653
  gtk_cell_renderer_accel_ungrab (GTK_CELL_RENDERER_ACCEL (box->cell));
  
  GTK_WIDGET_CLASS (gtk_cell_editable_event_box_parent_class)->unrealize (widget); 
Matthias Clasen's avatar
Matthias Clasen committed
654 655
}

656 657 658 659 660 661 662 663 664 665 666 667 668
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;
669 670 671 672 673 674
    case PROP_MODE:
      box->accel_mode = g_value_get_enum (value);
      break;
    case PROP_PATH:
      box->path = g_value_dup_string (value);
      break;
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
    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;
694 695 696 697 698 699
    case PROP_MODE:
      g_value_set_enum (value, box->accel_mode);
      break;
    case PROP_PATH:
      g_value_set_string (value, box->path);
      break;
700 701 702 703 704
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}
Matthias Clasen's avatar
Matthias Clasen committed
705 706

static void
707
gtk_cell_editable_event_box_finalize (GObject *object)
Matthias Clasen's avatar
Matthias Clasen committed
708
{
709
  GtkCellEditableEventBox *box = (GtkCellEditableEventBox*)object;
710

711
  g_free (box->path);
712

713
  G_OBJECT_CLASS (gtk_cell_editable_event_box_parent_class)->finalize (object);
Matthias Clasen's avatar
Matthias Clasen committed
714 715 716
}

static void
717
gtk_cell_editable_event_box_class_init (GtkCellEditableEventBoxClass *class)
Matthias Clasen's avatar
Matthias Clasen committed
718
{
719 720
  GObjectClass *object_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
721

722 723 724
  object_class->finalize = gtk_cell_editable_event_box_finalize;
  object_class->set_property = gtk_cell_editable_event_box_set_property;
  object_class->get_property = gtk_cell_editable_event_box_get_property;
725

726 727
  widget_class->key_press_event = gtk_cell_editable_event_box_key_press_event;
  widget_class->unrealize = gtk_cell_editable_event_box_unrealize;
Matthias Clasen's avatar
Matthias Clasen committed
728

729 730 731
  g_object_class_override_property (object_class,
                                    PROP_EDITING_CANCELED,
                                    "editing-canceled");
Matthias Clasen's avatar
Matthias Clasen committed
732

733 734 735 736 737
  g_object_class_install_property (object_class, PROP_MODE,
      g_param_spec_enum ("accel-mode", NULL, NULL,
                         GTK_TYPE_CELL_RENDERER_ACCEL_MODE,
                         GTK_CELL_RENDERER_ACCEL_MODE_GTK,
                         GTK_PARAM_READWRITE));
738

739 740 741
  g_object_class_install_property (object_class, PROP_PATH,
      g_param_spec_string ("path", NULL, NULL,
                           NULL, GTK_PARAM_READWRITE));
742 743

  gtk_widget_class_set_css_name (widget_class, "acceleditor");
744
}
Matthias Clasen's avatar
Matthias Clasen committed
745

746 747 748 749 750
static void
gtk_cell_editable_event_box_init (GtkCellEditableEventBox *box)
{
  gtk_widget_set_can_focus (GTK_WIDGET (box), TRUE);
}
751

752 753 754 755 756 757
static GtkWidget *
gtk_cell_editable_event_box_new (GtkCellRenderer          *cell,
                                 GtkCellRendererAccelMode  mode,
                                 const gchar              *path)
{
  GtkCellEditableEventBox *box;
Matthias Clasen's avatar
Matthias Clasen committed
758

759 760 761 762 763
  box = g_object_new (gtk_cell_editable_event_box_get_type (),
                      "accel-mode", mode,
                      "path", path,
                      NULL);
  box->cell = cell;
Matthias Clasen's avatar
Matthias Clasen committed
764

765
  return GTK_WIDGET (box);
Matthias Clasen's avatar
Matthias Clasen committed
766
}