5f893553f1cdceb003997c2f9c8e6de0185ce410
[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 _("New accelerator...")
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 == NULL)
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   gboolean edited;
259   gboolean cleared;
260   
261   CellRendererKey* key = CELL_RENDERER_KEY(data);
262
263   edited = FALSE;
264   cleared = FALSE;
265
266   guint scancode = event->hardware_keycode;
267   edited = TRUE;
268  out:
269   gdk_keyboard_ungrab (event->time);
270   gdk_pointer_ungrab (event->time);
271   
272   path = g_strdup (g_object_get_data (G_OBJECT (key->edit_widget), CELL_RENDERER_TEXT_PATH));
273
274   gtk_cell_editable_editing_done(GTK_CELL_EDITABLE (key->edit_widget));
275   gtk_cell_editable_remove_widget(GTK_CELL_EDITABLE (key->edit_widget));
276   key->edit_widget = NULL;
277   key->grab_widget = NULL;
278   
279   if (edited)
280     {
281       cell_renderer_key_set_scancode(key, scancode);
282       g_signal_emit_by_name (G_OBJECT(key), "accel_edited", path, scancode);
283     }
284   else if (cleared)
285     {
286       cell_renderer_key_set_scancode(key, 0);
287       g_signal_emit_by_name (G_OBJECT(key), "accel_cleared", path);
288     }
289
290   g_free (path);
291   return TRUE;
292 }
293
294 static void
295 ungrab_stuff (GtkWidget *widget, gpointer data)
296 {
297   CellRendererKey *key = CELL_RENDERER_KEY(data);
298
299   gdk_keyboard_ungrab (GDK_CURRENT_TIME);
300   gdk_pointer_ungrab (GDK_CURRENT_TIME);
301
302   g_signal_handlers_disconnect_by_func (G_OBJECT (key->grab_widget),
303                                         G_CALLBACK (grab_key_callback), data);
304 }
305
306 static void
307 pointless_eventbox_start_editing (GtkCellEditable *cell_editable,
308                                   GdkEvent        *event)
309 {
310   /* do nothing, because we are pointless */
311 }
312
313 static void
314 pointless_eventbox_cell_editable_init (GtkCellEditableIface *iface)
315 {
316   iface->start_editing = pointless_eventbox_start_editing;
317 }
318
319 static GType
320 pointless_eventbox_subclass_get_type (void)
321 {
322   static GType eventbox_type = 0;
323
324   if (!eventbox_type)
325     {
326       static const GTypeInfo eventbox_info =
327       {
328         sizeof (GtkEventBoxClass),
329         NULL,           /* base_init */
330         NULL,           /* base_finalize */
331         NULL,
332         NULL,           /* class_finalize */
333         NULL,           /* class_data */
334         sizeof (GtkEventBox),
335         0,              /* n_preallocs */
336         (GInstanceInitFunc) NULL,
337       };
338
339       static const GInterfaceInfo cell_editable_info = {
340         (GInterfaceInitFunc) pointless_eventbox_cell_editable_init,
341         NULL, NULL };
342
343       eventbox_type = g_type_register_static (GTK_TYPE_EVENT_BOX, "EggCellEditableEventBox", &eventbox_info, 0);
344       
345       g_type_add_interface_static (eventbox_type,
346                                    GTK_TYPE_CELL_EDITABLE,
347                                    &cell_editable_info);
348     }
349
350   return eventbox_type;
351 }
352
353 static GtkCellEditable *
354 cell_renderer_key_start_editing (GtkCellRenderer      *cell,
355                                       GdkEvent             *event,
356                                       GtkWidget            *widget,
357                                       const gchar          *path,
358                                       GdkRectangle         *background_area,
359                                       GdkRectangle         *cell_area,
360                                       GtkCellRendererState  flags)
361 {
362   GtkCellRendererText *celltext;
363   CellRendererKey *key;
364   GtkWidget *label;
365   GtkWidget *eventbox;
366   
367   celltext = GTK_CELL_RENDERER_TEXT (cell);
368   key = CELL_RENDERER_KEY (cell);
369
370   /* If the cell isn't editable we return NULL. */
371   if (celltext->editable == FALSE)
372     return NULL;
373
374   g_return_val_if_fail (widget->window != NULL, NULL);
375   
376   if (gdk_keyboard_grab (widget->window, FALSE,
377                          gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
378     return NULL;
379
380   if (gdk_pointer_grab (widget->window, FALSE,
381                         GDK_BUTTON_PRESS_MASK,
382                         NULL, NULL,
383                         gdk_event_get_time (event)) != GDK_GRAB_SUCCESS)
384     {
385       gdk_keyboard_ungrab (gdk_event_get_time (event));
386       return NULL;
387     }
388   
389   key->grab_widget = widget;
390
391   g_signal_connect (G_OBJECT (widget), "key_press_event",
392                     G_CALLBACK (grab_key_callback), key);
393
394   eventbox = g_object_new (pointless_eventbox_subclass_get_type (),
395                            NULL);
396   key->edit_widget = eventbox;
397   g_object_add_weak_pointer (G_OBJECT (key->edit_widget),
398                              (void**) &key->edit_widget);
399   
400   label = gtk_label_new (NULL);
401   //gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
402   
403   gtk_widget_modify_bg (eventbox, GTK_STATE_NORMAL,
404                         &widget->style->bg[GTK_STATE_SELECTED]);
405
406   gtk_widget_modify_fg (label, GTK_STATE_NORMAL,
407                         &widget->style->fg[GTK_STATE_SELECTED]);
408   
409   gtk_label_set_text (GTK_LABEL (label),
410                   TOOLTIP_TEXT);
411
412   gtk_container_add (GTK_CONTAINER (eventbox), label);
413   
414   g_object_set_data_full (G_OBJECT (key->edit_widget), CELL_RENDERER_TEXT_PATH,
415                           g_strdup (path), g_free);
416   
417   gtk_widget_show_all (key->edit_widget);
418
419   g_signal_connect (G_OBJECT (key->edit_widget), "unrealize",
420                     G_CALLBACK (ungrab_stuff), key);
421   
422   return GTK_CELL_EDITABLE (key->edit_widget);
423 }
424
425 void cell_renderer_key_set_scancode (CellRendererKey *key, gint scancode)
426 {
427   gboolean changed;
428
429   g_return_if_fail (IS_CELL_RENDERER_KEY(key));
430
431   g_object_freeze_notify(G_OBJECT(key));
432
433   changed = FALSE;
434   
435   if (scancode != key->scancode) {
436     key->scancode = scancode;
437     g_object_notify(G_OBJECT(key), "scancode");
438     changed = TRUE;
439   }
440
441   g_object_thaw_notify(G_OBJECT(key));
442
443   if (changed) {
444       const gchar *text;
445       /* sync string to the key values */
446       if (scancode <= 0) {
447         text = "None";
448       } else {
449         guint keyval = 0;
450
451         gdk_keymap_translate_keyboard_state(gdk_keymap_get_default(),
452           scancode, 0, 0, &keyval, NULL, NULL, NULL);
453         text = gdk_keyval_name(keyval);
454       }
455       g_object_set(key, "text", text, NULL);
456   }
457 }
458
459 gint cell_renderer_keys_get_scancode(CellRendererKey *key)
460 {
461   g_return_if_fail (IS_CELL_RENDERER_KEY(key));
462   return key->scancode;
463 }
464