07ef6fda65bd941eb1327aba24afb0499f076392
[drnoksnes] / gui / cellrendererkey.c
1 #include <libintl.h>
2 #include <gtk/gtk.h>
3 #include <gdk/gdkx.h>
4 #include <gdk/gdkkeysyms.h>
5 #include "cellrendererkey.h"
6
7 #define _(x) x
8 #define N_(x) x
9
10 #define CELL_RENDERER_TEXT_PATH "cell-renderer-key-text"
11
12 #define TOOLTIP_TEXT _("Press key or…")
13
14 static void             cell_renderer_key_finalize      (GObject             *object);
15 static void             cell_renderer_key_init          (CellRendererKey *cell_key);
16 static void             cell_renderer_key_class_init    (CellRendererKeyClass *cell_key_class);
17 static GtkCellEditable *cell_renderer_key_start_editing (GtkCellRenderer          *cell,
18                                                               GdkEvent                 *event,
19                                                               GtkWidget                *widget,
20                                                               const gchar              *path,
21                                                               GdkRectangle             *background_area,
22                                                               GdkRectangle             *cell_area,
23                                                               GtkCellRendererState      flags);
24
25 static void cell_renderer_key_get_property (GObject         *object,
26                                                  guint            param_id,
27                                                  GValue          *value,
28                                                  GParamSpec      *pspec);
29 static void cell_renderer_key_set_property (GObject         *object,
30                                                  guint            param_id,
31                                                  const GValue    *value,
32                                                  GParamSpec      *pspec);
33 static void cell_renderer_key_get_size     (GtkCellRenderer *cell,
34                                                  GtkWidget       *widget,
35                                                  GdkRectangle    *cell_area,
36                                                  gint            *x_offset,
37                                                  gint            *y_offset,
38                                                  gint            *width,
39                                                  gint            *height);
40
41
42 enum {
43   PROP_0,
44
45   PROP_SCANCODE
46 };
47
48 static GtkCellRendererTextClass *parent_class = NULL;
49
50 GType cell_renderer_key_get_type (void)
51 {
52   static GType cell_key_type = 0;
53
54   if (!cell_key_type)
55     {
56       static const GTypeInfo cell_key_info =
57       {
58         sizeof (CellRendererKeyClass),
59         NULL,           /* base_init */
60         NULL,           /* base_finalize */
61         (GClassInitFunc)cell_renderer_key_class_init,
62         NULL,           /* class_finalize */
63         NULL,           /* class_data */
64         sizeof (CellRendererKey),
65         0,              /* n_preallocs */
66         (GInstanceInitFunc) cell_renderer_key_init
67       };
68
69       cell_key_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT, "CellRendererKey", &cell_key_info, 0);
70     }
71
72   return cell_key_type;
73 }
74
75 static void
76 cell_renderer_key_init (CellRendererKey *key)
77 {
78         key->scancode = -1;
79 }
80
81
82 static void
83 marshal_VOID__STRING_UINT (GClosure     *closure,
84                                       GValue       *return_value,
85                                       guint         n_param_values,
86                                       const GValue *param_values,
87                                       gpointer      invocation_hint,
88                                       gpointer      marshal_data)
89 {
90   typedef void (*GMarshalFunc_VOID__STRING_UINT) (gpointer     data1,
91                                  const char  *arg_1,
92                                                              guint        arg_2,
93                                                              gpointer     data2);
94   register GMarshalFunc_VOID__STRING_UINT callback;
95   register GCClosure *cc = (GCClosure*) closure;
96   register gpointer data1, data2;
97
98   g_return_if_fail (n_param_values == 3);
99
100   if (G_CCLOSURE_SWAP_DATA (closure))
101     {
102       data1 = closure->data;
103       data2 = g_value_peek_pointer (param_values + 0);
104     }
105   else
106     {
107       data1 = g_value_peek_pointer (param_values + 0);
108       data2 = closure->data;
109     }
110   
111   callback = (GMarshalFunc_VOID__STRING_UINT) (marshal_data ? marshal_data : cc->callback);
112
113   callback (data1,
114             g_value_get_string (param_values + 1),
115             g_value_get_uint (param_values + 2),
116             data2);
117 }
118
119 static void
120 cell_renderer_key_class_init (CellRendererKeyClass *cell_key_class)
121 {
122   GObjectClass *object_class;
123   GtkCellRendererClass *cell_renderer_class;
124
125   object_class = G_OBJECT_CLASS (cell_key_class);
126   cell_renderer_class = GTK_CELL_RENDERER_CLASS (cell_key_class);
127   parent_class = g_type_class_peek_parent (object_class);
128   
129   GTK_CELL_RENDERER_CLASS(cell_key_class)->start_editing = cell_renderer_key_start_editing;
130
131   object_class->set_property = cell_renderer_key_set_property;
132   object_class->get_property = cell_renderer_key_get_property;
133   cell_renderer_class->get_size = cell_renderer_key_get_size;
134
135   object_class->finalize = cell_renderer_key_finalize;
136    
137   g_object_class_install_property (object_class,
138                                    PROP_SCANCODE,
139                                    g_param_spec_int ("scancode",
140                                                      _("Scancode"),
141                                                      _("Scancode"),
142                                                       -1,
143                                                       G_MAXINT,
144                                                       -1,
145                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
146   
147   g_signal_new ("accel_edited",
148                 TYPE_CELL_RENDERER_KEY,
149                 G_SIGNAL_RUN_LAST,
150                 G_STRUCT_OFFSET(CellRendererKeyClass, accel_edited),
151                 NULL, NULL,
152                 marshal_VOID__STRING_UINT,
153                 G_TYPE_NONE,
154                 2,G_TYPE_STRING,G_TYPE_UINT);
155
156   g_signal_new ("accel_cleared",
157                 TYPE_CELL_RENDERER_KEY,
158                 G_SIGNAL_RUN_LAST,
159                 G_STRUCT_OFFSET(CellRendererKeyClass, accel_cleared),
160                 NULL, NULL,
161                 gtk_marshal_VOID__STRING,
162                 G_TYPE_NONE,
163                 1,G_TYPE_STRING);
164 }
165
166
167 GtkCellRenderer *
168 cell_renderer_key_new (void)
169 {
170   return GTK_CELL_RENDERER (g_object_new (TYPE_CELL_RENDERER_KEY, NULL));
171 }
172
173 static void
174 cell_renderer_key_finalize (GObject *object)
175 {
176   
177   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
178 }
179
180 static void
181 cell_renderer_key_get_property  (GObject                  *object,
182                                       guint                     param_id,
183                                       GValue                   *value,
184                                       GParamSpec               *pspec)
185 {
186   CellRendererKey *key;
187
188   g_return_if_fail (IS_CELL_RENDERER_KEY(object));
189
190   key = CELL_RENDERER_KEY(object);
191   
192   switch (param_id)
193     {
194     case PROP_SCANCODE:
195       g_value_set_int(value, key->scancode);
196       break;
197
198     default:
199       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
200     }
201 }
202
203 static void
204 cell_renderer_key_set_property  (GObject                  *object,
205                                       guint                     param_id,
206                                       const GValue             *value,
207                                       GParamSpec               *pspec)
208 {
209   CellRendererKey *key;
210
211   g_return_if_fail (IS_CELL_RENDERER_KEY(object));
212
213   key = CELL_RENDERER_KEY(object);
214   
215   switch (param_id)
216     {
217     case PROP_SCANCODE:
218       cell_renderer_key_set_scancode(key, g_value_get_int(value));
219       break;
220       
221     default:
222       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
223     }
224 }
225
226 static void
227 cell_renderer_key_get_size (GtkCellRenderer *cell,
228                                  GtkWidget       *widget,
229                                  GdkRectangle    *cell_area,
230                                  gint            *x_offset,
231                                  gint            *y_offset,
232                                  gint            *width,
233                                  gint            *height)
234
235 {
236   CellRendererKey *key = (CellRendererKey *) cell;
237   GtkRequisition requisition;
238
239   if (!key->sizing_label)
240     key->sizing_label = gtk_label_new(TOOLTIP_TEXT);
241
242   gtk_widget_size_request (key->sizing_label, &requisition);
243   (* GTK_CELL_RENDERER_CLASS (parent_class)->get_size) (cell, widget, cell_area, x_offset, y_offset, width, height);
244   /* FIXME: need to take the cell_area et al. into account */
245   if (width)
246     *width = MAX (*width, requisition.width);
247   if (height)
248     *height = MAX (*height, requisition.height);
249 }
250
251  
252 static gboolean
253 grab_key_callback (GtkWidget    *widget,
254                    GdkEventKey  *event,
255                    void         *data)
256 {
257   char *path;
258   CellRendererKey* key = CELL_RENDERER_KEY(data);
259   guint scancode = event->hardware_keycode;
260
261   gdk_keyboard_ungrab (event->time);
262   gdk_pointer_ungrab (event->time);
263
264   path = g_strdup (g_object_get_data (G_OBJECT (key->edit_widget), CELL_RENDERER_TEXT_PATH));
265
266   gtk_cell_editable_editing_done(GTK_CELL_EDITABLE (key->edit_widget));
267   gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE (key->edit_widget));
268   key->edit_widget = NULL;
269   key->grab_widget = NULL;
270
271   cell_renderer_key_set_scancode(key, scancode);
272   g_signal_emit_by_name (G_OBJECT(key), "accel_edited", path, scancode);
273
274   g_free (path);
275   return TRUE;
276 }
277
278 static void
279 clear_key_callback(GtkButton *widget, gpointer data)
280 {
281   char *path;
282   CellRendererKey* key = CELL_RENDERER_KEY(data);
283
284   gdk_keyboard_ungrab(GDK_CURRENT_TIME);
285   gdk_pointer_ungrab(GDK_CURRENT_TIME);
286
287   path = g_strdup (g_object_get_data (G_OBJECT (key->edit_widget), CELL_RENDERER_TEXT_PATH));
288
289   gtk_cell_editable_editing_done(GTK_CELL_EDITABLE (key->edit_widget));
290   gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE (key->edit_widget));
291   key->edit_widget = NULL;
292   key->grab_widget = NULL;
293
294   cell_renderer_key_set_scancode(key, 0);
295   g_signal_emit_by_name (G_OBJECT(key), "accel_cleared", path);
296
297   g_free (path);
298 }
299
300 static void
301 ungrab_stuff (GtkWidget *widget, gpointer data)
302 {
303   CellRendererKey *key = CELL_RENDERER_KEY(data);
304
305   gdk_keyboard_ungrab (GDK_CURRENT_TIME);
306   gdk_pointer_ungrab (GDK_CURRENT_TIME);
307
308   g_signal_handlers_disconnect_by_func (G_OBJECT (key->grab_widget),
309                                         G_CALLBACK (grab_key_callback), data);
310 }
311
312 static void
313 pointless_eventbox_start_editing (GtkCellEditable *cell_editable,
314                                   GdkEvent        *event)
315 {
316   /* do nothing, because we are pointless */
317 }
318
319 static void
320 pointless_eventbox_cell_editable_init (GtkCellEditableIface *iface)
321 {
322   iface->start_editing = pointless_eventbox_start_editing;
323 }
324
325 static GType
326 pointless_eventbox_subclass_get_type (void)
327 {
328   static GType eventbox_type = 0;
329
330   if (!eventbox_type)
331     {
332       static const GTypeInfo eventbox_info =
333       {
334         sizeof (GtkEventBoxClass),
335         NULL,           /* base_init */
336         NULL,           /* base_finalize */
337         NULL,
338         NULL,           /* class_finalize */
339         NULL,           /* class_data */
340         sizeof (GtkEventBox),
341         0,              /* n_preallocs */
342         (GInstanceInitFunc) NULL,
343       };
344
345       static const GInterfaceInfo cell_editable_info = {
346         (GInterfaceInitFunc) pointless_eventbox_cell_editable_init,
347         NULL, NULL };
348
349       eventbox_type = g_type_register_static (GTK_TYPE_EVENT_BOX, "CellEditableEventBox", &eventbox_info, 0);
350       
351       g_type_add_interface_static (eventbox_type,
352                                    GTK_TYPE_CELL_EDITABLE,
353                                    &cell_editable_info);
354     }
355
356   return eventbox_type;
357 }
358
359 static GtkCellEditable *
360 cell_renderer_key_start_editing (GtkCellRenderer      *cell,
361                                       GdkEvent             *event,
362                                       GtkWidget            *widget,
363                                       const gchar          *path,
364                                       GdkRectangle         *background_area,
365                                       GdkRectangle         *cell_area,
366                                       GtkCellRendererState  flags)
367 {
368   GtkCellRendererText *celltext;
369   CellRendererKey *key;
370   GtkWidget *hbox;
371   GtkWidget *label;
372   GtkWidget *clear_button;
373   GtkWidget *eventbox;
374   
375   celltext = GTK_CELL_RENDERER_TEXT (cell);
376   key = CELL_RENDERER_KEY (cell);
377
378   /* If the cell isn't editable we return NULL. */
379   if (celltext->editable == FALSE)
380     return NULL;
381
382   g_return_val_if_fail (widget->window != NULL, NULL);
383   
384   if (gdk_keyboard_grab (widget->window, FALSE,
385                          gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
386     return NULL;
387
388   if (gdk_pointer_grab (widget->window, TRUE,
389                         GDK_BUTTON_PRESS_MASK,
390                         NULL, NULL,
391                         gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
392     {
393       gdk_keyboard_ungrab (gdk_event_get_time (event));
394       return NULL;
395     }
396   
397   key->grab_widget = widget;
398
399   g_signal_connect(G_OBJECT (widget), "key_press_event",
400                     G_CALLBACK (grab_key_callback), key);
401
402   eventbox = g_object_new(pointless_eventbox_subclass_get_type(), NULL);
403   key->edit_widget = eventbox;
404   g_object_add_weak_pointer (G_OBJECT (key->edit_widget),
405                              (void**) &key->edit_widget);
406
407
408   hbox = gtk_hbox_new(FALSE, 2);
409
410   label = gtk_label_new(TOOLTIP_TEXT);
411   gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE);
412   gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
413
414   clear_button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
415   g_signal_connect(G_OBJECT(clear_button), "clicked",
416                     G_CALLBACK(clear_key_callback), key);
417
418   gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL,
419                         &widget->style->bg[GTK_STATE_SELECTED]);
420
421   gtk_widget_modify_fg(label, GTK_STATE_NORMAL,
422                         &widget->style->fg[GTK_STATE_SELECTED]);
423
424   gtk_box_pack_start_defaults(GTK_BOX(hbox), label);
425   gtk_box_pack_start(GTK_BOX(hbox), clear_button, FALSE, FALSE, 0);
426   gtk_container_add(GTK_CONTAINER(eventbox), hbox);
427   gtk_container_set_border_width(GTK_CONTAINER(eventbox), 0);
428   gtk_widget_set_size_request(GTK_WIDGET(eventbox),
429     cell_area->width, cell_area->height);
430
431   g_object_set_data_full(G_OBJECT(eventbox), CELL_RENDERER_TEXT_PATH,
432                           g_strdup (path), g_free);
433
434   gtk_widget_show_all(eventbox);
435
436   g_signal_connect (G_OBJECT(eventbox), "unrealize",
437                     G_CALLBACK (ungrab_stuff), key);
438
439   return GTK_CELL_EDITABLE(eventbox);
440 }
441
442 void cell_renderer_key_set_scancode (CellRendererKey *key, gint scancode)
443 {
444   gboolean changed;
445
446   g_return_if_fail (IS_CELL_RENDERER_KEY(key));
447
448   g_object_freeze_notify(G_OBJECT(key));
449
450   changed = FALSE;
451   
452   if (scancode != key->scancode) {
453     key->scancode = scancode;
454     g_object_notify(G_OBJECT(key), "scancode");
455     changed = TRUE;
456   }
457
458   g_object_thaw_notify(G_OBJECT(key));
459
460   if (changed) {
461       const gchar *text;
462       /* sync string to the key values */
463       if (scancode <= 0) {
464         text = "None";
465       } else {
466         guint keyval = 0;
467
468         gdk_keymap_translate_keyboard_state(gdk_keymap_get_default(),
469           scancode, 0, 0, &keyval, NULL, NULL, NULL);
470         text = gdk_keyval_name(keyval);
471       }
472       g_object_set(key, "text", text, NULL);
473   }
474 }
475
476 gint cell_renderer_keys_get_scancode(CellRendererKey *key)
477 {
478   g_return_val_if_fail(IS_CELL_RENDERER_KEY(key), -1);
479   return key->scancode;
480 }
481