1 | /* gtkcellrendereraccel.h |
2 | * Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com> |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include "config.h" |
19 | |
20 | #include "gtkcellrendereraccel.h" |
21 | |
22 | #include "gtkintl.h" |
23 | #include "gtkaccelgroup.h" |
24 | #include "gtkmarshalers.h" |
25 | #include "gtklabel.h" |
26 | #include "gtkmain.h" |
27 | #include "gtksizerequest.h" |
28 | #include "gtktypebuiltins.h" |
29 | #include "gtkprivate.h" |
30 | #include "gtkeventcontrollerkey.h" |
31 | #include "gtknative.h" |
32 | #include "gtkbinlayout.h" |
33 | |
34 | |
35 | /** |
36 | * GtkCellRendererAccel: |
37 | * |
38 | * Renders a keyboard accelerator in a cell |
39 | * |
40 | * `GtkCellRendererAccel` displays a keyboard accelerator (i.e. a key |
41 | * combination like `Control + a`). If the cell renderer is editable, |
42 | * the accelerator can be changed by simply typing the new combination. |
43 | */ |
44 | |
45 | |
46 | static void gtk_cell_renderer_accel_get_property (GObject *object, |
47 | guint param_id, |
48 | GValue *value, |
49 | GParamSpec *pspec); |
50 | static void gtk_cell_renderer_accel_set_property (GObject *object, |
51 | guint param_id, |
52 | const GValue *value, |
53 | GParamSpec *pspec); |
54 | static void gtk_cell_renderer_accel_get_preferred_width |
55 | (GtkCellRenderer *cell, |
56 | GtkWidget *widget, |
57 | int *minimum_size, |
58 | int *natural_size); |
59 | static GtkCellEditable * |
60 | gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell, |
61 | GdkEvent *event, |
62 | GtkWidget *widget, |
63 | const char *path, |
64 | const GdkRectangle *background_area, |
65 | const GdkRectangle *cell_area, |
66 | GtkCellRendererState flags); |
67 | static char *convert_keysym_state_to_string (GtkCellRendererAccel *accel, |
68 | guint keysym, |
69 | GdkModifierType mask, |
70 | guint keycode); |
71 | static GtkWidget *gtk_cell_editable_widget_new (GtkCellRenderer *cell, |
72 | GtkCellRendererAccelMode mode, |
73 | const char *path); |
74 | |
75 | enum { |
76 | ACCEL_EDITED, |
77 | ACCEL_CLEARED, |
78 | LAST_SIGNAL |
79 | }; |
80 | |
81 | enum { |
82 | PROP_ACCEL_KEY = 1, |
83 | PROP_ACCEL_MODS, |
84 | PROP_KEYCODE, |
85 | PROP_ACCEL_MODE |
86 | }; |
87 | |
88 | static guint signals[LAST_SIGNAL] = { 0 }; |
89 | |
90 | typedef struct _GtkCellRendererAccelPrivate GtkCellRendererAccelPrivate; |
91 | typedef struct _GtkCellRendererAccelClass GtkCellRendererAccelClass; |
92 | |
93 | struct _GtkCellRendererAccel |
94 | { |
95 | GtkCellRendererText parent; |
96 | }; |
97 | |
98 | struct _GtkCellRendererAccelClass |
99 | { |
100 | GtkCellRendererTextClass parent_class; |
101 | |
102 | void (* accel_edited) (GtkCellRendererAccel *accel, |
103 | const char *path_string, |
104 | guint accel_key, |
105 | GdkModifierType accel_mods, |
106 | guint hardware_keycode); |
107 | |
108 | void (* accel_cleared) (GtkCellRendererAccel *accel, |
109 | const char *path_string); |
110 | |
111 | /* Padding for future expansion */ |
112 | void (*_gtk_reserved0) (void); |
113 | void (*_gtk_reserved1) (void); |
114 | void (*_gtk_reserved2) (void); |
115 | void (*_gtk_reserved3) (void); |
116 | void (*_gtk_reserved4) (void); |
117 | }; |
118 | |
119 | struct _GtkCellRendererAccelPrivate |
120 | { |
121 | GtkWidget *sizing_label; |
122 | |
123 | GtkCellRendererAccelMode accel_mode; |
124 | GdkModifierType accel_mods; |
125 | guint accel_key; |
126 | guint keycode; |
127 | }; |
128 | |
129 | G_DEFINE_TYPE_WITH_PRIVATE (GtkCellRendererAccel, gtk_cell_renderer_accel, GTK_TYPE_CELL_RENDERER_TEXT) |
130 | |
131 | static void |
132 | gtk_cell_renderer_accel_init (GtkCellRendererAccel *cell_accel) |
133 | { |
134 | char *text; |
135 | |
136 | text = convert_keysym_state_to_string (accel: cell_accel, keysym: 0, mask: 0, keycode: 0); |
137 | g_object_set (object: cell_accel, first_property_name: "text" , text, NULL); |
138 | g_free (mem: text); |
139 | } |
140 | |
141 | static void |
142 | gtk_cell_renderer_accel_dispose (GObject *object) |
143 | { |
144 | GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); |
145 | |
146 | g_clear_object (&priv->sizing_label); |
147 | |
148 | G_OBJECT_CLASS (gtk_cell_renderer_accel_parent_class)->dispose (object); |
149 | } |
150 | |
151 | static void |
152 | gtk_cell_renderer_accel_class_init (GtkCellRendererAccelClass *cell_accel_class) |
153 | { |
154 | GObjectClass *object_class; |
155 | GtkCellRendererClass *cell_renderer_class; |
156 | |
157 | object_class = G_OBJECT_CLASS (cell_accel_class); |
158 | cell_renderer_class = GTK_CELL_RENDERER_CLASS (cell_accel_class); |
159 | |
160 | object_class->set_property = gtk_cell_renderer_accel_set_property; |
161 | object_class->get_property = gtk_cell_renderer_accel_get_property; |
162 | object_class->dispose = gtk_cell_renderer_accel_dispose; |
163 | |
164 | cell_renderer_class->get_preferred_width = gtk_cell_renderer_accel_get_preferred_width; |
165 | cell_renderer_class->start_editing = gtk_cell_renderer_accel_start_editing; |
166 | |
167 | /** |
168 | * GtkCellRendererAccel:accel-key: |
169 | * |
170 | * The keyval of the accelerator. |
171 | */ |
172 | g_object_class_install_property (oclass: object_class, |
173 | property_id: PROP_ACCEL_KEY, |
174 | pspec: g_param_spec_uint (name: "accel-key" , |
175 | P_("Accelerator key" ), |
176 | P_("The keyval of the accelerator" ), |
177 | minimum: 0, |
178 | G_MAXINT, |
179 | default_value: 0, |
180 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
181 | |
182 | /** |
183 | * GtkCellRendererAccel:accel-mods: |
184 | * |
185 | * The modifier mask of the accelerator. |
186 | */ |
187 | g_object_class_install_property (oclass: object_class, |
188 | property_id: PROP_ACCEL_MODS, |
189 | pspec: g_param_spec_flags (name: "accel-mods" , |
190 | P_("Accelerator modifiers" ), |
191 | P_("The modifier mask of the accelerator" ), |
192 | flags_type: GDK_TYPE_MODIFIER_TYPE, |
193 | default_value: 0, |
194 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
195 | |
196 | /** |
197 | * GtkCellRendererAccel:keycode: |
198 | * |
199 | * The hardware keycode of the accelerator. Note that the hardware keycode is |
200 | * only relevant if the key does not have a keyval. Normally, the keyboard |
201 | * configuration should assign keyvals to all keys. |
202 | */ |
203 | g_object_class_install_property (oclass: object_class, |
204 | property_id: PROP_KEYCODE, |
205 | pspec: g_param_spec_uint (name: "keycode" , |
206 | P_("Accelerator keycode" ), |
207 | P_("The hardware keycode of the accelerator" ), |
208 | minimum: 0, |
209 | G_MAXINT, |
210 | default_value: 0, |
211 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
212 | |
213 | /** |
214 | * GtkCellRendererAccel:accel-mode: |
215 | * |
216 | * Determines if the edited accelerators are GTK accelerators. If |
217 | * they are, consumed modifiers are suppressed, only accelerators |
218 | * accepted by GTK are allowed, and the accelerators are rendered |
219 | * in the same way as they are in menus. |
220 | */ |
221 | g_object_class_install_property (oclass: object_class, |
222 | property_id: PROP_ACCEL_MODE, |
223 | pspec: g_param_spec_enum (name: "accel-mode" , |
224 | P_("Accelerator Mode" ), |
225 | P_("The type of accelerators" ), |
226 | enum_type: GTK_TYPE_CELL_RENDERER_ACCEL_MODE, |
227 | default_value: GTK_CELL_RENDERER_ACCEL_MODE_GTK, |
228 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
229 | |
230 | /** |
231 | * GtkCellRendererAccel::accel-edited: |
232 | * @accel: the object reveiving the signal |
233 | * @path_string: the path identifying the row of the edited cell |
234 | * @accel_key: the new accelerator keyval |
235 | * @accel_mods: the new acclerator modifier mask |
236 | * @hardware_keycode: the keycode of the new accelerator |
237 | * |
238 | * Gets emitted when the user has selected a new accelerator. |
239 | */ |
240 | signals[ACCEL_EDITED] = g_signal_new (I_("accel-edited" ), |
241 | GTK_TYPE_CELL_RENDERER_ACCEL, |
242 | signal_flags: G_SIGNAL_RUN_LAST, |
243 | G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_edited), |
244 | NULL, NULL, |
245 | c_marshaller: _gtk_marshal_VOID__STRING_UINT_FLAGS_UINT, |
246 | G_TYPE_NONE, n_params: 4, |
247 | G_TYPE_STRING, |
248 | G_TYPE_UINT, |
249 | GDK_TYPE_MODIFIER_TYPE, |
250 | G_TYPE_UINT); |
251 | |
252 | /** |
253 | * GtkCellRendererAccel::accel-cleared: |
254 | * @accel: the object reveiving the signal |
255 | * @path_string: the path identifying the row of the edited cell |
256 | * |
257 | * Gets emitted when the user has removed the accelerator. |
258 | */ |
259 | signals[ACCEL_CLEARED] = g_signal_new (I_("accel-cleared" ), |
260 | GTK_TYPE_CELL_RENDERER_ACCEL, |
261 | signal_flags: G_SIGNAL_RUN_LAST, |
262 | G_STRUCT_OFFSET (GtkCellRendererAccelClass, accel_cleared), |
263 | NULL, NULL, |
264 | NULL, |
265 | G_TYPE_NONE, n_params: 1, |
266 | G_TYPE_STRING); |
267 | } |
268 | |
269 | |
270 | /** |
271 | * gtk_cell_renderer_accel_new: |
272 | * |
273 | * Creates a new `GtkCellRendererAccel`. |
274 | * |
275 | * Returns: the new cell renderer |
276 | */ |
277 | GtkCellRenderer * |
278 | gtk_cell_renderer_accel_new (void) |
279 | { |
280 | return g_object_new (GTK_TYPE_CELL_RENDERER_ACCEL, NULL); |
281 | } |
282 | |
283 | static char * |
284 | convert_keysym_state_to_string (GtkCellRendererAccel *accel, |
285 | guint keysym, |
286 | GdkModifierType mask, |
287 | guint keycode) |
288 | { |
289 | GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (self: accel); |
290 | |
291 | if (keysym == 0 && keycode == 0) |
292 | /* This label is displayed in a treeview cell displaying |
293 | * a disabled accelerator key combination. |
294 | */ |
295 | return g_strdup (C_("Accelerator" , "Disabled" )); |
296 | else |
297 | { |
298 | if (priv->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK) |
299 | { |
300 | if (!gtk_accelerator_valid (keyval: keysym, modifiers: mask)) |
301 | /* This label is displayed in a treeview cell displaying |
302 | * an accelerator key combination that is not valid according |
303 | * to gtk_accelerator_valid(). |
304 | */ |
305 | return g_strdup (C_("Accelerator" , "Invalid" )); |
306 | |
307 | return gtk_accelerator_get_label (accelerator_key: keysym, accelerator_mods: mask); |
308 | } |
309 | else |
310 | { |
311 | char *name; |
312 | |
313 | name = gtk_accelerator_get_label_with_keycode (NULL, accelerator_key: keysym, keycode, accelerator_mods: mask); |
314 | if (name == NULL) |
315 | name = gtk_accelerator_name_with_keycode (NULL, accelerator_key: keysym, keycode, accelerator_mods: mask); |
316 | |
317 | return name; |
318 | } |
319 | } |
320 | } |
321 | |
322 | static void |
323 | gtk_cell_renderer_accel_get_property (GObject *object, |
324 | guint param_id, |
325 | GValue *value, |
326 | GParamSpec *pspec) |
327 | { |
328 | GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); |
329 | |
330 | switch (param_id) |
331 | { |
332 | case PROP_ACCEL_KEY: |
333 | g_value_set_uint (value, v_uint: priv->accel_key); |
334 | break; |
335 | |
336 | case PROP_ACCEL_MODS: |
337 | g_value_set_flags (value, v_flags: priv->accel_mods); |
338 | break; |
339 | |
340 | case PROP_KEYCODE: |
341 | g_value_set_uint (value, v_uint: priv->keycode); |
342 | break; |
343 | |
344 | case PROP_ACCEL_MODE: |
345 | g_value_set_enum (value, v_enum: priv->accel_mode); |
346 | break; |
347 | |
348 | default: |
349 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); |
350 | } |
351 | } |
352 | |
353 | static void |
354 | gtk_cell_renderer_accel_set_property (GObject *object, |
355 | guint param_id, |
356 | const GValue *value, |
357 | GParamSpec *pspec) |
358 | { |
359 | GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (object); |
360 | GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (object)); |
361 | gboolean changed = FALSE; |
362 | |
363 | switch (param_id) |
364 | { |
365 | case PROP_ACCEL_KEY: |
366 | { |
367 | guint accel_key = g_value_get_uint (value); |
368 | |
369 | if (priv->accel_key != accel_key) |
370 | { |
371 | priv->accel_key = accel_key; |
372 | changed = TRUE; |
373 | g_object_notify (object, property_name: "accel-key" ); |
374 | } |
375 | } |
376 | break; |
377 | |
378 | case PROP_ACCEL_MODS: |
379 | { |
380 | guint accel_mods = g_value_get_flags (value); |
381 | |
382 | if (priv->accel_mods != accel_mods) |
383 | { |
384 | priv->accel_mods = accel_mods; |
385 | changed = TRUE; |
386 | g_object_notify (object, property_name: "accel-mods" ); |
387 | } |
388 | } |
389 | break; |
390 | case PROP_KEYCODE: |
391 | { |
392 | guint keycode = g_value_get_uint (value); |
393 | |
394 | if (priv->keycode != keycode) |
395 | { |
396 | priv->keycode = keycode; |
397 | changed = TRUE; |
398 | g_object_notify (object, property_name: "keycode" ); |
399 | } |
400 | } |
401 | break; |
402 | |
403 | case PROP_ACCEL_MODE: |
404 | if (priv->accel_mode != g_value_get_enum (value)) |
405 | { |
406 | priv->accel_mode = g_value_get_enum (value); |
407 | g_object_notify (object, property_name: "accel-mode" ); |
408 | } |
409 | break; |
410 | |
411 | default: |
412 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); |
413 | } |
414 | |
415 | if (changed) |
416 | { |
417 | char *text; |
418 | |
419 | text = convert_keysym_state_to_string (accel, keysym: priv->accel_key, mask: priv->accel_mods, keycode: priv->keycode); |
420 | g_object_set (object: accel, first_property_name: "text" , text, NULL); |
421 | g_free (mem: text); |
422 | } |
423 | } |
424 | |
425 | static void |
426 | gtk_cell_renderer_accel_get_preferred_width (GtkCellRenderer *cell, |
427 | GtkWidget *widget, |
428 | int *minimum_size, |
429 | int *natural_size) |
430 | |
431 | { |
432 | GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (GTK_CELL_RENDERER_ACCEL (cell)); |
433 | GtkRequisition min_req, nat_req; |
434 | |
435 | if (priv->sizing_label == NULL) |
436 | { |
437 | priv->sizing_label = gtk_label_new (_("New accelerator…" )); |
438 | g_object_ref_sink (priv->sizing_label); |
439 | } |
440 | |
441 | gtk_widget_get_preferred_size (widget: priv->sizing_label, minimum_size: &min_req, natural_size: &nat_req); |
442 | |
443 | GTK_CELL_RENDERER_CLASS (gtk_cell_renderer_accel_parent_class)->get_preferred_width (cell, widget, |
444 | minimum_size, natural_size); |
445 | |
446 | /* FIXME: need to take the cell_area et al. into account */ |
447 | if (minimum_size) |
448 | *minimum_size = MAX (*minimum_size, min_req.width); |
449 | if (natural_size) |
450 | *natural_size = MAX (*natural_size, nat_req.width); |
451 | } |
452 | |
453 | static GtkCellEditable * |
454 | gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell, |
455 | GdkEvent *event, |
456 | GtkWidget *widget, |
457 | const char *path, |
458 | const GdkRectangle *background_area, |
459 | const GdkRectangle *cell_area, |
460 | GtkCellRendererState flags) |
461 | { |
462 | GtkCellRendererText *celltext = GTK_CELL_RENDERER_TEXT (cell); |
463 | GtkCellRendererAccel *accel = GTK_CELL_RENDERER_ACCEL (cell); |
464 | GtkCellRendererAccelPrivate *priv = gtk_cell_renderer_accel_get_instance_private (self: accel); |
465 | GtkWidget *editable; |
466 | gboolean is_editable; |
467 | |
468 | /* If the cell isn't editable we return NULL. */ |
469 | g_object_get (object: celltext, first_property_name: "editable" , &is_editable, NULL); |
470 | if (!is_editable) |
471 | return NULL; |
472 | |
473 | editable = gtk_cell_editable_widget_new (cell, mode: priv->accel_mode, path); |
474 | |
475 | return GTK_CELL_EDITABLE (editable); |
476 | } |
477 | |
478 | /* --------------------------------- */ |
479 | |
480 | typedef struct _GtkCellEditableWidget GtkCellEditableWidget; |
481 | typedef GtkWidgetClass GtkCellEditableWidgetClass; |
482 | |
483 | struct _GtkCellEditableWidget |
484 | { |
485 | GtkWidget parent; |
486 | |
487 | gboolean editing_canceled; |
488 | GtkCellRendererAccelMode accel_mode; |
489 | char *path; |
490 | GtkCellRenderer *cell; |
491 | GtkWidget *label; |
492 | }; |
493 | |
494 | enum { |
495 | PROP_EDITING_CANCELED = 1, |
496 | PROP_MODE, |
497 | PROP_PATH |
498 | }; |
499 | |
500 | GType gtk_cell_editable_widget_get_type (void); |
501 | static void gtk_cell_editable_widget_cell_editable_init (GtkCellEditableIface *iface); |
502 | |
503 | G_DEFINE_TYPE_WITH_CODE (GtkCellEditableWidget, gtk_cell_editable_widget, GTK_TYPE_WIDGET, |
504 | G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE, gtk_cell_editable_widget_cell_editable_init)) |
505 | |
506 | static void |
507 | gtk_cell_editable_widget_start_editing (GtkCellEditable *cell_editable, |
508 | GdkEvent *event) |
509 | { |
510 | /* do nothing, because we are pointless */ |
511 | } |
512 | |
513 | static void |
514 | gtk_cell_editable_widget_cell_editable_init (GtkCellEditableIface *iface) |
515 | { |
516 | iface->start_editing = gtk_cell_editable_widget_start_editing; |
517 | } |
518 | |
519 | static gboolean |
520 | key_controller_modifiers (GtkEventControllerKey *key, |
521 | GdkModifierType state, |
522 | GtkWidget *widget) |
523 | { |
524 | /* Ignore modifiers */ |
525 | return TRUE; |
526 | } |
527 | |
528 | static gboolean |
529 | key_controller_key_pressed (GtkEventControllerKey *key, |
530 | guint keyval, |
531 | guint keycode, |
532 | GdkModifierType state, |
533 | GtkWidget *widget) |
534 | { |
535 | GtkCellEditableWidget *box = (GtkCellEditableWidget*)widget; |
536 | gboolean edited = FALSE; |
537 | gboolean cleared = FALSE; |
538 | GdkModifierType accel_mods = 0; |
539 | guint accel_key; |
540 | GdkEvent *event; |
541 | |
542 | event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (key)); |
543 | if (!gdk_key_event_get_match (event, keyval: &accel_key, modifiers: &accel_mods)) |
544 | return FALSE; |
545 | |
546 | if (accel_mods == 0) |
547 | { |
548 | switch (keyval) |
549 | { |
550 | case GDK_KEY_BackSpace: |
551 | cleared = TRUE; |
552 | G_GNUC_FALLTHROUGH; |
553 | case GDK_KEY_Escape: |
554 | goto out; |
555 | default: |
556 | break; |
557 | } |
558 | } |
559 | |
560 | if (box->accel_mode == GTK_CELL_RENDERER_ACCEL_MODE_GTK && |
561 | !gtk_accelerator_valid (keyval: accel_key, modifiers: accel_mods)) |
562 | { |
563 | gtk_widget_error_bell (widget); |
564 | return TRUE; |
565 | } |
566 | |
567 | edited = TRUE; |
568 | |
569 | out: |
570 | gtk_grab_remove (widget); |
571 | gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (widget)); |
572 | gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (widget)); |
573 | |
574 | if (edited) |
575 | g_signal_emit (instance: box->cell, signal_id: signals[ACCEL_EDITED], detail: 0, box->path, |
576 | accel_key, accel_mods, keycode); |
577 | else if (cleared) |
578 | g_signal_emit (instance: box->cell, signal_id: signals[ACCEL_CLEARED], detail: 0, box->path); |
579 | |
580 | return TRUE; |
581 | } |
582 | |
583 | static void |
584 | gtk_cell_editable_widget_unrealize (GtkWidget *widget) |
585 | { |
586 | gtk_grab_remove (widget); |
587 | |
588 | GTK_WIDGET_CLASS (gtk_cell_editable_widget_parent_class)->unrealize (widget); |
589 | } |
590 | |
591 | static void |
592 | gtk_cell_editable_widget_set_property (GObject *object, |
593 | guint prop_id, |
594 | const GValue *value, |
595 | GParamSpec *pspec) |
596 | { |
597 | GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; |
598 | |
599 | switch (prop_id) |
600 | { |
601 | case PROP_EDITING_CANCELED: |
602 | box->editing_canceled = g_value_get_boolean (value); |
603 | break; |
604 | case PROP_MODE: |
605 | box->accel_mode = g_value_get_enum (value); |
606 | break; |
607 | case PROP_PATH: |
608 | box->path = g_value_dup_string (value); |
609 | break; |
610 | default: |
611 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
612 | break; |
613 | } |
614 | } |
615 | |
616 | static void |
617 | gtk_cell_editable_widget_get_property (GObject *object, |
618 | guint prop_id, |
619 | GValue *value, |
620 | GParamSpec *pspec) |
621 | { |
622 | GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; |
623 | |
624 | switch (prop_id) |
625 | { |
626 | case PROP_EDITING_CANCELED: |
627 | g_value_set_boolean (value, v_boolean: box->editing_canceled); |
628 | break; |
629 | case PROP_MODE: |
630 | g_value_set_enum (value, v_enum: box->accel_mode); |
631 | break; |
632 | case PROP_PATH: |
633 | g_value_set_string (value, v_string: box->path); |
634 | break; |
635 | default: |
636 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
637 | break; |
638 | } |
639 | } |
640 | |
641 | static void |
642 | gtk_cell_editable_widget_dispose (GObject *object) |
643 | { |
644 | GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; |
645 | |
646 | g_clear_pointer (&box->label, gtk_widget_unparent); |
647 | |
648 | G_OBJECT_CLASS (gtk_cell_editable_widget_parent_class)->dispose (object); |
649 | } |
650 | |
651 | static void |
652 | gtk_cell_editable_widget_finalize (GObject *object) |
653 | { |
654 | GtkCellEditableWidget *box = (GtkCellEditableWidget*)object; |
655 | |
656 | g_free (mem: box->path); |
657 | |
658 | G_OBJECT_CLASS (gtk_cell_editable_widget_parent_class)->finalize (object); |
659 | } |
660 | |
661 | static void |
662 | gtk_cell_editable_widget_class_init (GtkCellEditableWidgetClass *class) |
663 | { |
664 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
665 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); |
666 | |
667 | object_class->finalize = gtk_cell_editable_widget_finalize; |
668 | object_class->dispose = gtk_cell_editable_widget_dispose; |
669 | object_class->set_property = gtk_cell_editable_widget_set_property; |
670 | object_class->get_property = gtk_cell_editable_widget_get_property; |
671 | |
672 | widget_class->unrealize = gtk_cell_editable_widget_unrealize; |
673 | |
674 | g_object_class_override_property (oclass: object_class, |
675 | property_id: PROP_EDITING_CANCELED, |
676 | name: "editing-canceled" ); |
677 | |
678 | g_object_class_install_property (oclass: object_class, property_id: PROP_MODE, |
679 | pspec: g_param_spec_enum (name: "accel-mode" , NULL, NULL, |
680 | enum_type: GTK_TYPE_CELL_RENDERER_ACCEL_MODE, |
681 | default_value: GTK_CELL_RENDERER_ACCEL_MODE_GTK, |
682 | GTK_PARAM_READWRITE)); |
683 | |
684 | g_object_class_install_property (oclass: object_class, property_id: PROP_PATH, |
685 | pspec: g_param_spec_string (name: "path" , NULL, NULL, |
686 | NULL, GTK_PARAM_READWRITE)); |
687 | |
688 | gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); |
689 | gtk_widget_class_set_css_name (widget_class, I_("acceleditor" )); |
690 | } |
691 | |
692 | static void |
693 | gtk_cell_editable_widget_init (GtkCellEditableWidget *box) |
694 | { |
695 | GtkWidget *widget = GTK_WIDGET (box); |
696 | GtkEventController *controller; |
697 | |
698 | gtk_widget_set_focusable (widget, TRUE); |
699 | |
700 | controller = gtk_event_controller_key_new (); |
701 | g_signal_connect (controller, "key-pressed" , |
702 | G_CALLBACK (key_controller_key_pressed), box); |
703 | g_signal_connect (controller, "modifiers" , |
704 | G_CALLBACK (key_controller_modifiers), box); |
705 | gtk_widget_add_controller (widget, controller); |
706 | } |
707 | |
708 | static GtkWidget * |
709 | gtk_cell_editable_widget_new (GtkCellRenderer *cell, |
710 | GtkCellRendererAccelMode mode, |
711 | const char *path) |
712 | { |
713 | GtkCellEditableWidget *box; |
714 | |
715 | box = g_object_new (object_type: gtk_cell_editable_widget_get_type (), |
716 | first_property_name: "accel-mode" , mode, |
717 | "path" , path, |
718 | NULL); |
719 | box->cell = cell; |
720 | |
721 | box->label = gtk_label_new (NULL); |
722 | gtk_widget_set_halign (widget: box->label, align: GTK_ALIGN_START); |
723 | gtk_widget_set_valign (widget: box->label, align: GTK_ALIGN_CENTER); |
724 | |
725 | gtk_widget_set_state_flags (widget: box->label, flags: GTK_STATE_FLAG_SELECTED, TRUE); |
726 | |
727 | /* This label is displayed in a treeview cell displaying an accelerator |
728 | * when the cell is clicked to change the acelerator. |
729 | */ |
730 | gtk_label_set_text (GTK_LABEL (box->label), _("New accelerator…" )); |
731 | |
732 | gtk_widget_set_parent (widget: box->label, GTK_WIDGET (box)); |
733 | |
734 | gtk_grab_add (GTK_WIDGET (box)); |
735 | |
736 | return GTK_WIDGET (box); |
737 | } |
738 | |