1 /*
2 * This file is part of audacious-hotkey plugin for audacious
3 *
4 * Copyright (c) 2007 - 2008 Sascha Hlusiak <contact@saschahlusiak.de>
5 * Name: gui.c
6 * Description: gui.c
7 *
8 * Part of this code is from itouch-ctrl plugin.
9 * Authors of itouch-ctrl are listed below:
10 *
11 * Copyright (c) 2006 - 2007 Vladimir Paskov <vlado.paskov@gmail.com>
12 *
13 * Part of this code are from xmms-itouch plugin.
14 * Authors of xmms-itouch are listed below:
15 *
16 * Copyright (C) 2000-2002 Ville Syrjälä <syrjala@sci.fi>
17 * Bryn Davies <curious@ihug.com.au>
18 * Jonathan A. Davis <davis@jdhouse.org>
19 * Jeremy Tan <nsx@nsx.homeip.net>
20 *
21 * audacious-hotkey is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * audacious-hotkey is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with audacious-hotkey; if not, write to the Free Software
33 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
34 * USA.
35 */
36
37 #include <libaudcore/i18n.h>
38 #include <libaudcore/preferences.h>
39
40 #include <libaudgui/libaudgui-gtk.h>
41
42 #include <gdk/gdkkeysyms-compat.h>
43 #include <gdk/gdkx.h>
44 #include <gtk/gtk.h>
45
46 #include <X11/XKBlib.h>
47
48 #include "grab.h"
49 #include "gui.h"
50 #include "plugin.h"
51
52 typedef struct _KeyControls
53 {
54 GtkWidget * keytext;
55 GtkWidget * grid;
56 GtkWidget * button;
57 GtkWidget * combobox;
58
59 HotkeyConfiguration hotkey;
60 struct _KeyControls *next, *prev, *first;
61 } KeyControls;
62
63 static KeyControls * first_controls;
64
65 static void clear_keyboard(GtkWidget * widget, void * data);
66 static void add_callback(GtkWidget * widget, void * data);
67 static void destroy_callback();
68 static void ok_callback();
69
70 static const char * event_desc[] = {N_("Previous track"),
71 N_("Play"),
72 N_("Pause/Resume"),
73 N_("Stop"),
74 N_("Next track"),
75 N_("Step forward"),
76 N_("Step backward"),
77 N_("Mute"),
78 N_("Volume up"),
79 N_("Volume down"),
80 N_("Jump to file"),
81 N_("Toggle player window(s)"),
82 N_("Show On-Screen-Display"),
83 N_("Toggle repeat"),
84 N_("Toggle shuffle"),
85 N_("Toggle stop after current"),
86 N_("Raise player window(s)")};
87
88 static_assert(aud::n_elems(event_desc) == EVENT_MAX,
89 "event_desc table is not up to date");
90
set_keytext(GtkWidget * entry,int key,int mask,int type)91 static void set_keytext(GtkWidget * entry, int key, int mask, int type)
92 {
93 char * text = nullptr;
94
95 if (key == 0 && mask == 0)
96 {
97 text = g_strdup(_("(none)"));
98 }
99 else
100 {
101 static const char * modifier_string[] = {
102 "Control", "Shift", "Alt", "Mod2", "Mod3", "Super", "Mod5"};
103 static const unsigned int modifiers[] = {
104 ControlMask, ShiftMask, Mod1Mask, Mod2Mask,
105 Mod3Mask, Mod4Mask, Mod5Mask};
106 const char * strings[9];
107 char * keytext = nullptr;
108 int i, j;
109 if (type == TYPE_KEY)
110 {
111 KeySym keysym;
112 keysym = XkbKeycodeToKeysym(
113 GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), key, 0, 0);
114 if (keysym == 0 || keysym == NoSymbol)
115 {
116 keytext = g_strdup_printf("#%d", key);
117 }
118 else
119 {
120 keytext = g_strdup(XKeysymToString(keysym));
121 }
122 }
123 if (type == TYPE_MOUSE)
124 {
125 keytext = g_strdup_printf("Button%d", key);
126 }
127
128 for (i = 0, j = 0; j < 7; j++)
129 {
130 if (mask & modifiers[j])
131 strings[i++] = modifier_string[j];
132 }
133 if (key != 0)
134 strings[i++] = keytext;
135 strings[i] = nullptr;
136
137 text = g_strjoinv(" + ", (char **)strings);
138 g_free(keytext);
139 }
140
141 gtk_entry_set_text(GTK_ENTRY(entry), text);
142 gtk_editable_set_position(GTK_EDITABLE(entry), -1);
143 if (text)
144 g_free(text);
145 }
146
on_entry_key_press_event(GtkWidget * widget,GdkEventKey * event,void * user_data)147 static gboolean on_entry_key_press_event(GtkWidget * widget,
148 GdkEventKey * event, void * user_data)
149 {
150 KeyControls * controls = (KeyControls *)user_data;
151 int is_mod;
152 int mod;
153
154 if (event->keyval == GDK_Tab)
155 return false;
156 if (event->keyval == GDK_Escape && ((event->state & ~GDK_LOCK_MASK) == 0))
157 return false;
158 if (event->keyval == GDK_Return && ((event->state & ~GDK_LOCK_MASK) == 0))
159 return false;
160 if (event->keyval == GDK_ISO_Left_Tab)
161 {
162 set_keytext(controls->keytext, controls->hotkey.key,
163 controls->hotkey.mask, controls->hotkey.type);
164 return false;
165 }
166 if (event->keyval == GDK_Up && ((event->state & ~GDK_LOCK_MASK) == 0))
167 return false;
168 if (event->keyval == GDK_Down && ((event->state & ~GDK_LOCK_MASK) == 0))
169 return false;
170
171 mod = 0;
172 is_mod = 0;
173
174 if ((event->state & GDK_CONTROL_MASK) |
175 (!is_mod && (is_mod = (event->keyval == GDK_Control_L ||
176 event->keyval == GDK_Control_R))))
177 mod |= ControlMask;
178
179 if ((event->state & GDK_MOD1_MASK) |
180 (!is_mod &&
181 (is_mod = (event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R))))
182 mod |= Mod1Mask;
183
184 if ((event->state & GDK_SHIFT_MASK) |
185 (!is_mod && (is_mod = (event->keyval == GDK_Shift_L ||
186 event->keyval == GDK_Shift_R))))
187 mod |= ShiftMask;
188
189 if ((event->state & GDK_MOD5_MASK) |
190 (!is_mod && (is_mod = (event->keyval == GDK_ISO_Level3_Shift))))
191 mod |= Mod5Mask;
192
193 if ((event->state & GDK_MOD4_MASK) |
194 (!is_mod && (is_mod = (event->keyval == GDK_Super_L ||
195 event->keyval == GDK_Super_R))))
196 mod |= Mod4Mask;
197
198 if (!is_mod)
199 {
200 controls->hotkey.key = event->hardware_keycode;
201 controls->hotkey.mask = mod;
202 controls->hotkey.type = TYPE_KEY;
203 if (controls->next == nullptr)
204 add_callback(nullptr, (void *)controls);
205 else
206 gtk_widget_grab_focus(GTK_WIDGET(controls->next->keytext));
207 }
208
209 set_keytext(controls->keytext, is_mod ? 0 : event->hardware_keycode, mod,
210 TYPE_KEY);
211 return true;
212 }
213
on_entry_key_release_event(GtkWidget * widget,GdkEventKey * event,void * user_data)214 static gboolean on_entry_key_release_event(GtkWidget * widget,
215 GdkEventKey * event,
216 void * user_data)
217 {
218 KeyControls * controls = (KeyControls *)user_data;
219 if (!gtk_widget_is_focus(widget))
220 return false;
221 set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask,
222 controls->hotkey.type);
223
224 return true;
225 }
226
on_entry_button_press_event(GtkWidget * widget,GdkEventButton * event,void * user_data)227 static gboolean on_entry_button_press_event(GtkWidget * widget,
228 GdkEventButton * event,
229 void * user_data)
230 {
231 KeyControls * controls = (KeyControls *)user_data;
232 int mod;
233
234 if (!gtk_widget_is_focus(widget))
235 return false;
236
237 mod = 0;
238 if (event->state & GDK_CONTROL_MASK)
239 mod |= ControlMask;
240
241 if (event->state & GDK_MOD1_MASK)
242 mod |= Mod1Mask;
243
244 if (event->state & GDK_SHIFT_MASK)
245 mod |= ShiftMask;
246
247 if (event->state & GDK_MOD5_MASK)
248 mod |= Mod5Mask;
249
250 if (event->state & GDK_MOD4_MASK)
251 mod |= Mod4Mask;
252
253 if ((event->button <= 3) && (mod == 0))
254 {
255 GtkWidget * dialog;
256 int response;
257 dialog = gtk_message_dialog_new(
258 GTK_WINDOW(gtk_widget_get_toplevel(widget)), GTK_DIALOG_MODAL,
259 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO,
260 _("It is not recommended to bind the primary mouse buttons without "
261 "modifiers.\n\n"
262 "Do you want to continue?"));
263 gtk_window_set_title(GTK_WINDOW(dialog), _("Binding mouse buttons"));
264 response = gtk_dialog_run(GTK_DIALOG(dialog));
265 gtk_widget_destroy(dialog);
266 if (response != GTK_RESPONSE_YES)
267 return true;
268 }
269
270 controls->hotkey.key = event->button;
271 controls->hotkey.mask = mod;
272 controls->hotkey.type = TYPE_MOUSE;
273 set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask,
274 controls->hotkey.type);
275 if (controls->next == nullptr)
276 add_callback(nullptr, (void *)controls);
277
278 return true;
279 }
280
on_entry_scroll_event(GtkWidget * widget,GdkEventScroll * event,void * user_data)281 static gboolean on_entry_scroll_event(GtkWidget * widget,
282 GdkEventScroll * event, void * user_data)
283 {
284 KeyControls * controls = (KeyControls *)user_data;
285 int mod;
286
287 if (!gtk_widget_is_focus(widget))
288 return false;
289
290 mod = 0;
291 if (event->state & GDK_CONTROL_MASK)
292 mod |= ControlMask;
293
294 if (event->state & GDK_MOD1_MASK)
295 mod |= Mod1Mask;
296
297 if (event->state & GDK_SHIFT_MASK)
298 mod |= ShiftMask;
299
300 if (event->state & GDK_MOD5_MASK)
301 mod |= Mod5Mask;
302
303 if (event->state & GDK_MOD4_MASK)
304 mod |= Mod4Mask;
305
306 if (event->direction == GDK_SCROLL_UP)
307 controls->hotkey.key = 4;
308 else if (event->direction == GDK_SCROLL_DOWN)
309 controls->hotkey.key = 5;
310 else if (event->direction == GDK_SCROLL_LEFT)
311 controls->hotkey.key = 6;
312 else if (event->direction == GDK_SCROLL_RIGHT)
313 controls->hotkey.key = 7;
314 else
315 return false;
316
317 controls->hotkey.mask = mod;
318 controls->hotkey.type = TYPE_MOUSE;
319 set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask,
320 controls->hotkey.type);
321 if (controls->next == nullptr)
322 add_callback(nullptr, (void *)controls);
323 return true;
324 }
325
add_event_controls(KeyControls * list,GtkWidget * grid,int row,HotkeyConfiguration * hotkey)326 KeyControls * add_event_controls(KeyControls * list, GtkWidget * grid, int row,
327 HotkeyConfiguration * hotkey)
328 {
329 KeyControls * controls;
330 int i;
331
332 controls = (KeyControls *)g_malloc(sizeof(KeyControls));
333 controls->next = nullptr;
334 controls->prev = list;
335 controls->first = list->first;
336 controls->grid = grid;
337 list->next = controls;
338
339 if (hotkey)
340 {
341 controls->hotkey.key = hotkey->key;
342 controls->hotkey.mask = hotkey->mask;
343 controls->hotkey.type = hotkey->type;
344 controls->hotkey.event = hotkey->event;
345 if (controls->hotkey.key == 0)
346 controls->hotkey.mask = 0;
347 }
348 else
349 {
350 controls->hotkey.key = 0;
351 controls->hotkey.mask = 0;
352 controls->hotkey.type = TYPE_KEY;
353 controls->hotkey.event = (EVENT)0;
354 }
355
356 controls->combobox = gtk_combo_box_text_new();
357 for (i = 0; i < EVENT_MAX; i++)
358 {
359 gtk_combo_box_text_append_text((GtkComboBoxText *)controls->combobox,
360 _(event_desc[i]));
361 }
362 gtk_combo_box_set_active(GTK_COMBO_BOX(controls->combobox),
363 controls->hotkey.event);
364 gtk_table_attach_defaults(GTK_TABLE(grid), controls->combobox, 0, 1, row,
365 row + 1);
366
367 controls->keytext = gtk_entry_new();
368 gtk_table_attach_defaults(GTK_TABLE(grid), controls->keytext, 1, 2, row,
369 row + 1);
370 gtk_editable_set_editable(GTK_EDITABLE(controls->keytext), false);
371
372 set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask,
373 controls->hotkey.type);
374 g_signal_connect((void *)controls->keytext, "key_press_event",
375 G_CALLBACK(on_entry_key_press_event), controls);
376 g_signal_connect((void *)controls->keytext, "key_release_event",
377 G_CALLBACK(on_entry_key_release_event), controls);
378 g_signal_connect((void *)controls->keytext, "button_press_event",
379 G_CALLBACK(on_entry_button_press_event), controls);
380 g_signal_connect((void *)controls->keytext, "scroll_event",
381 G_CALLBACK(on_entry_scroll_event), controls);
382
383 controls->button = gtk_button_new();
384 gtk_button_set_image(
385 GTK_BUTTON(controls->button),
386 gtk_image_new_from_icon_name("edit-delete", GTK_ICON_SIZE_BUTTON));
387 gtk_table_attach_defaults(GTK_TABLE(grid), controls->button, 2, 3, row,
388 row + 1);
389 g_signal_connect(G_OBJECT(controls->button), "clicked",
390 G_CALLBACK(clear_keyboard), controls);
391
392 gtk_widget_grab_focus(GTK_WIDGET(controls->keytext));
393 return controls;
394 }
395
make_config_widget()396 void * make_config_widget()
397 {
398 KeyControls * current_controls;
399 GtkWidget *main_vbox, *hbox;
400 GtkWidget * alignment;
401 GtkWidget * frame;
402 GtkWidget * label;
403 GtkWidget * image;
404 GtkWidget * grid;
405 GtkWidget *button_box, *button;
406 PluginConfig * plugin_cfg;
407 HotkeyConfiguration *hotkey, temphotkey;
408 int i;
409
410 load_config();
411
412 plugin_cfg = get_config();
413
414 ungrab_keys();
415
416 main_vbox = gtk_vbox_new(false, 4);
417
418 alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
419 gtk_box_pack_start(GTK_BOX(main_vbox), alignment, false, true, 0);
420 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 4, 0, 0, 0);
421 hbox = gtk_hbox_new(false, 2);
422 gtk_container_add(GTK_CONTAINER(alignment), hbox);
423 image = gtk_image_new_from_icon_name("dialog-information",
424 GTK_ICON_SIZE_DIALOG);
425 gtk_box_pack_start(GTK_BOX(hbox), image, false, true, 0);
426 label = gtk_label_new(_("Press a key combination inside a text field.\nYou "
427 "can also bind mouse buttons."));
428 gtk_box_pack_start(GTK_BOX(hbox), label, true, true, 0);
429 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
430
431 label = gtk_label_new(nullptr);
432 gtk_label_set_markup(GTK_LABEL(label), _("Hotkeys:"));
433 frame = gtk_frame_new(nullptr);
434 gtk_frame_set_label_widget(GTK_FRAME(frame), label);
435 gtk_box_pack_start(GTK_BOX(main_vbox), frame, true, true, 0);
436 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
437 alignment = gtk_alignment_new(0, 0, 1, 0);
438 gtk_container_add(GTK_CONTAINER(frame), alignment);
439 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 3, 3, 3, 3);
440
441 grid = gtk_table_new(0, 0, false);
442 gtk_table_set_col_spacings(GTK_TABLE(grid), 2);
443 gtk_container_add(GTK_CONTAINER(alignment), grid);
444
445 label = gtk_label_new(nullptr);
446 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
447 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
448 gtk_label_set_markup(GTK_LABEL(label), _("<b>Action:</b>"));
449 gtk_table_attach_defaults(GTK_TABLE(grid), label, 0, 1, 0, 1);
450
451 label = gtk_label_new(nullptr);
452 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
453 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
454 gtk_label_set_markup(GTK_LABEL(label), _("<b>Key Binding:</b>"));
455 gtk_table_attach_defaults(GTK_TABLE(grid), label, 1, 2, 0, 1);
456
457 hotkey = &(plugin_cfg->first);
458 i = 1;
459 first_controls = (KeyControls *)g_malloc(sizeof(KeyControls));
460 first_controls->next = nullptr;
461 first_controls->prev = nullptr;
462 first_controls->grid = grid;
463 first_controls->button = nullptr;
464 first_controls->combobox = nullptr;
465 first_controls->keytext = nullptr;
466 first_controls->first = first_controls;
467 first_controls->hotkey.key = 0;
468 first_controls->hotkey.mask = 0;
469 first_controls->hotkey.event = (EVENT)0;
470 first_controls->hotkey.type = TYPE_KEY;
471 current_controls = first_controls;
472 if (hotkey->key != 0)
473 {
474 while (hotkey)
475 {
476 current_controls =
477 add_event_controls(current_controls, grid, i, hotkey);
478 hotkey = hotkey->next;
479 i++;
480 }
481 }
482 temphotkey.key = 0;
483 temphotkey.mask = 0;
484 temphotkey.type = TYPE_KEY;
485 if (current_controls != first_controls)
486 temphotkey.event = (EVENT)(current_controls->hotkey.event + 1);
487 else
488 temphotkey.event = (EVENT)0;
489 if (temphotkey.event >= EVENT_MAX)
490 temphotkey.event = (EVENT)0;
491 add_event_controls(current_controls, grid, i, &temphotkey);
492
493 hbox = gtk_hbox_new(false, 0);
494 gtk_box_pack_start(GTK_BOX(main_vbox), hbox, false, true, 0);
495
496 button_box = gtk_hbutton_box_new();
497 gtk_box_pack_start(GTK_BOX(hbox), button_box, false, true, 0);
498 gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_START);
499 gtk_box_set_spacing(GTK_BOX(button_box), 4);
500
501 button = audgui_button_new(_("_Add"), "list-add", nullptr, nullptr);
502 gtk_container_add(GTK_CONTAINER(button_box), button);
503 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(add_callback),
504 first_controls);
505
506 return main_vbox;
507 }
508
clear_keyboard(GtkWidget * widget,void * data)509 static void clear_keyboard(GtkWidget * widget, void * data)
510 {
511 KeyControls * controls = (KeyControls *)data;
512
513 if ((controls->next == nullptr) && (controls->prev->keytext == nullptr))
514 {
515 controls->hotkey.key = 0;
516 controls->hotkey.mask = 0;
517 controls->hotkey.type = TYPE_KEY;
518 set_keytext(controls->keytext, 0, 0, TYPE_KEY);
519 gtk_combo_box_set_active(GTK_COMBO_BOX(controls->combobox), 0);
520 return;
521 }
522
523 if (controls->prev)
524 {
525 KeyControls * c;
526 GtkWidget * grid;
527 int row;
528
529 gtk_widget_destroy(GTK_WIDGET(controls->button));
530 gtk_widget_destroy(GTK_WIDGET(controls->keytext));
531 gtk_widget_destroy(GTK_WIDGET(controls->combobox));
532
533 row = 0;
534 c = controls->first;
535 while (c)
536 {
537 if (c == controls)
538 break;
539 row++;
540 c = c->next;
541 }
542 c = controls->next;
543 controls->prev->next = controls->next;
544 if (controls->next)
545 controls->next->prev = controls->prev;
546 g_free(controls);
547 if (c)
548 grid = c->grid;
549 else
550 grid = nullptr;
551 while (c)
552 {
553 g_object_ref(c->combobox);
554 g_object_ref(c->keytext);
555 g_object_ref(c->button);
556
557 gtk_container_remove(GTK_CONTAINER(c->grid), c->combobox);
558 gtk_container_remove(GTK_CONTAINER(c->grid), c->keytext);
559 gtk_container_remove(GTK_CONTAINER(c->grid), c->button);
560
561 gtk_table_attach_defaults(GTK_TABLE(c->grid), c->combobox, 0, 1,
562 row, row + 1);
563 gtk_table_attach_defaults(GTK_TABLE(c->grid), c->keytext, 1, 2, row,
564 row + 1);
565 gtk_table_attach_defaults(GTK_TABLE(c->grid), c->button, 2, 3, row,
566 row + 1);
567
568 g_object_unref(c->combobox);
569 g_object_unref(c->keytext);
570 g_object_unref(c->button);
571
572 c = c->next;
573 row++;
574 }
575 if (grid)
576 gtk_widget_show_all(GTK_WIDGET(grid));
577
578 return;
579 }
580 }
581
add_callback(GtkWidget * widget,void * data)582 void add_callback(GtkWidget * widget, void * data)
583 {
584 KeyControls * controls = (KeyControls *)data;
585 HotkeyConfiguration temphotkey;
586 int count;
587 if (controls == nullptr)
588 return;
589 if ((controls->next == nullptr) &&
590 (controls->hotkey.event + 1 == EVENT_MAX))
591 return;
592 controls = controls->first;
593 if (controls == nullptr)
594 return;
595 count = 1;
596 while (controls->next)
597 {
598 controls = controls->next;
599 count = count + 1;
600 }
601 temphotkey.key = 0;
602 temphotkey.mask = 0;
603 temphotkey.type = TYPE_KEY;
604 temphotkey.event = (EVENT)(controls->hotkey.event + 1);
605 if (temphotkey.event >= EVENT_MAX)
606 temphotkey.event = (EVENT)0;
607 add_event_controls(controls, controls->grid, count, &temphotkey);
608 gtk_widget_show_all(GTK_WIDGET(controls->grid));
609 }
610
destroy_callback()611 void destroy_callback()
612 {
613 KeyControls * controls = first_controls;
614
615 grab_keys();
616
617 while (controls)
618 {
619 KeyControls * old;
620 old = controls;
621 controls = controls->next;
622 g_free(old);
623 }
624
625 first_controls = nullptr;
626 }
627
ok_callback()628 void ok_callback()
629 {
630 KeyControls * controls = first_controls;
631 PluginConfig * plugin_cfg = get_config();
632 HotkeyConfiguration * hotkey;
633
634 hotkey = &(plugin_cfg->first);
635 hotkey = hotkey->next;
636 while (hotkey)
637 {
638 HotkeyConfiguration * old;
639 old = hotkey;
640 hotkey = hotkey->next;
641 g_free(old);
642 }
643 plugin_cfg->first.next = nullptr;
644 plugin_cfg->first.key = 0;
645 plugin_cfg->first.event = (EVENT)0;
646 plugin_cfg->first.mask = 0;
647
648 hotkey = &(plugin_cfg->first);
649 while (controls)
650 {
651 if (controls->hotkey.key)
652 {
653 if (hotkey->key)
654 {
655 hotkey->next = g_new(HotkeyConfiguration, 1);
656 hotkey = hotkey->next;
657 hotkey->next = nullptr;
658 }
659 hotkey->key = controls->hotkey.key;
660 hotkey->mask = controls->hotkey.mask;
661 hotkey->event = (EVENT)gtk_combo_box_get_active(
662 GTK_COMBO_BOX(controls->combobox));
663 hotkey->type = controls->hotkey.type;
664 }
665 controls = controls->next;
666 }
667
668 save_config();
669 }
670
671 static const PreferencesWidget hotkey_widgets[] = {
672 WidgetCustomGTK(make_config_widget)};
673
674 const PluginPreferences hotkey_prefs = {{hotkey_widgets},
675 nullptr, // init
676 ok_callback,
677 destroy_callback};
678