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