girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
shortcuts.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include "shortcuts.h"
4 #include "datastructures.h"
5 #include "internal.h"
6 #include "session.h"
7 #include "settings.h"
8 #include "tabs.h"
9 #include "input-history.h"
10 
11 #include <string.h>
12 #include <gtk/gtk.h>
13 
14 static void girara_toggle_widget_visibility(GtkWidget* widget);
15 static bool simulate_key_press(girara_session_t* session, int state, int key);
16 
17 bool
18 girara_shortcut_add(girara_session_t* session, guint modifier, guint key, const char* buffer, girara_shortcut_function_t function, girara_mode_t mode, int argument_n, void* argument_data)
19 {
20  g_return_val_if_fail(session != NULL, false);
21  g_return_val_if_fail(buffer || key || modifier, false);
22  g_return_val_if_fail(function != NULL, false);
23 
24  girara_argument_t argument = {argument_n, (argument_data != NULL) ?
25  g_strdup(argument_data) : NULL};
26 
27  /* search for existing binding */
28  bool found_existing_shortcut = false;
29  GIRARA_LIST_FOREACH(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it)
30  if (((shortcuts_it->mask == modifier && shortcuts_it->key == key && (modifier != 0 || key != 0)) ||
31  (buffer && shortcuts_it->buffered_command && !strcmp(shortcuts_it->buffered_command, buffer)))
32  && ((shortcuts_it->mode == mode) || (mode == 0)))
33  {
34  if (shortcuts_it->argument.data != NULL) {
35  g_free(shortcuts_it->argument.data);
36  }
37 
38  shortcuts_it->function = function;
39  shortcuts_it->argument = argument;
40  found_existing_shortcut = true;
41 
42  if (mode != 0) {
44  return true;
45  }
46  }
47  GIRARA_LIST_FOREACH_END(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it);
48 
49  if (found_existing_shortcut == true) {
50  return true;
51  }
52 
53  /* add new shortcut */
54  girara_shortcut_t* shortcut = g_slice_new(girara_shortcut_t);
55 
56  shortcut->mask = modifier;
57  shortcut->key = key;
58  shortcut->buffered_command = buffer;
59  shortcut->function = function;
60  shortcut->mode = mode;
61  shortcut->argument = argument;
62  girara_list_append(session->bindings.shortcuts, shortcut);
63 
64  return true;
65 }
66 
67 bool
68 girara_shortcut_remove(girara_session_t* session, guint modifier, guint key, const char* buffer, girara_mode_t mode)
69 {
70  g_return_val_if_fail(session != NULL, false);
71  g_return_val_if_fail(buffer || key || modifier, false);
72 
73  /* search for existing binding */
74  GIRARA_LIST_FOREACH(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it)
75  if (((shortcuts_it->mask == modifier && shortcuts_it->key == key && (modifier != 0 || key != 0)) ||
76  (buffer && shortcuts_it->buffered_command && !strcmp(shortcuts_it->buffered_command, buffer)))
77  && shortcuts_it->mode == mode)
78  {
79  girara_list_remove(session->bindings.shortcuts, shortcuts_it);
81  return true;
82  }
83  GIRARA_LIST_FOREACH_END(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it);
84 
85  return false;
86 }
87 
88 void
89 girara_shortcut_free(girara_shortcut_t* shortcut)
90 {
91  g_return_if_fail(shortcut != NULL);
92  g_free(shortcut->argument.data);
93  g_slice_free(girara_shortcut_t, shortcut);
94 }
95 
96 bool
97 girara_inputbar_shortcut_add(girara_session_t* session, guint modifier, guint key, girara_shortcut_function_t function, int argument_n, void* argument_data)
98 {
99  g_return_val_if_fail(session != NULL, false);
100  g_return_val_if_fail(function != NULL, false);
101 
102  girara_argument_t argument = {argument_n, argument_data};
103 
104  /* search for existing special command */
105  GIRARA_LIST_FOREACH(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it)
106  if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
107  inp_sh_it->function = function;
108  inp_sh_it->argument = argument;
109 
111  return true;
112  }
113  GIRARA_LIST_FOREACH_END(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it);
114 
115  /* create new inputbar shortcut */
116  girara_inputbar_shortcut_t* inputbar_shortcut = g_slice_new(girara_inputbar_shortcut_t);
117 
118  inputbar_shortcut->mask = modifier;
119  inputbar_shortcut->key = key;
120  inputbar_shortcut->function = function;
121  inputbar_shortcut->argument = argument;
122 
123  girara_list_append(session->bindings.inputbar_shortcuts, inputbar_shortcut);
124  return true;
125 }
126 
127 bool
128 girara_inputbar_shortcut_remove(girara_session_t* session, guint modifier, guint key)
129 {
130  g_return_val_if_fail(session != NULL, false);
131 
132  /* search for existing special command */
133  GIRARA_LIST_FOREACH(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it)
134  if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
135  girara_list_remove(session->bindings.inputbar_shortcuts, inp_sh_it);
137  return true;
138  }
139  GIRARA_LIST_FOREACH_END(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it);
140 
141  return true;
142 }
143 
144 void
145 girara_inputbar_shortcut_free(girara_inputbar_shortcut_t* inputbar_shortcut)
146 {
147  g_slice_free(girara_inputbar_shortcut_t, inputbar_shortcut);
148 }
149 
150 bool
151 girara_isc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
152 {
153  g_return_val_if_fail(session != NULL, false);
154 
155  /* hide completion */
156  girara_argument_t arg = { GIRARA_HIDE, NULL };
157  girara_isc_completion(session, &arg, NULL, 0);
158 
159  /* clear inputbar */
160  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
161 
162  /* grab view */
163  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));
164 
165  /* hide inputbar */
166  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
167  if (session->global.autohide_inputbar == true) {
168  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
169  }
170 
171  /* Begin from the last command when navigating through history */
172  girara_input_history_reset(session->command_history);
173 
174  /* reset custom functions */
175  session->signals.inputbar_custom_activate = NULL;
176  session->signals.inputbar_custom_key_press_event = NULL;
177  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
178 
179  return true;
180 }
181 
182 bool
183 girara_isc_string_manipulation(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
184 {
185  g_return_val_if_fail(session != NULL, false);
186 
187  gchar *separator = NULL;
188  girara_setting_get(session, "word-separator", &separator);
189  gchar *input = gtk_editable_get_chars(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
190  int length = strlen(input);
191  int pos = gtk_editable_get_position(GTK_EDITABLE(session->gtk.inputbar_entry));
192  int i;
193 
194  switch (argument->n) {
196  if (pos == 1 && (input[0] == ':' || input[0] == '/')) {
197  break;
198  }
199  if (pos == 0) {
200  break;
201  }
202 
203  i = pos - 1;
204 
205  /* remove trailing spaces */
206  for (; i >= 0 && input[i] == ' '; i--);
207 
208  /* find the beginning of the word */
209  while ((i == (pos - 1)) || ((i > 0) && !strchr(separator, input[i]))) {
210  i--;
211  }
212 
213  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), i + 1, pos);
214  gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), i + 1);
215  break;
217  if (length != 1 && pos == 1 && (input[0] == ':' || input[0] == '/')) {
218  break;
219  }
220  if (length == 1 && pos == 1) {
221  girara_isc_abort(session, argument, NULL, 0);
222  }
223  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos - 1, pos);
224  break;
226  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), 1, pos);
227  break;
228  case GIRARA_NEXT_CHAR:
229  gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), pos + 1);
230  break;
232  gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), (pos == 1) ? 1 : pos - 1);
233  break;
235  if (length != 1 && pos == 0 && (input[0] == ':' || input[0] == '/')){
236  break;
237  }
238  if(length == 1 && pos == 0) {
239  girara_isc_abort(session, argument, NULL, 0);
240  }
241  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos, pos + 1);
242  break;
244  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos, length);
245  break;
246  case GIRARA_GOTO_START:
247  gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), 1);
248  break;
249  case GIRARA_GOTO_END:
250  gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
251  break;
252  }
253 
254  g_free(separator);
255  g_free(input);
256 
257  return false;
258 }
259 
260 bool
261 girara_isc_command_history(girara_session_t* session, girara_argument_t*
262  argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
263 {
264  g_return_val_if_fail(session != NULL, false);
265 
266  char* temp = gtk_editable_get_chars(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
267  const char* command = argument->n == GIRARA_NEXT ?
268  girara_input_history_next(session->command_history, temp) :
269  girara_input_history_previous(session->command_history, temp);
270  g_free(temp);
271 
272  if (command != NULL) {
273  gtk_entry_set_text(session->gtk.inputbar_entry, command);
274  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));
275  gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
276  }
277 
278  return true;
279 }
280 
281 /* default shortcut implementation */
282 bool
283 girara_sc_focus_inputbar(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
284 {
285  g_return_val_if_fail(session != NULL, false);
286  g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
287 
288  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
289  gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
290  }
291 
292  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
293  gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
294  }
295 
296  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));
297 
298  if (argument != NULL && argument->data != NULL) {
299  gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);
300 
301  /* we save the X clipboard that will be clear by "grab_focus" */
302  gchar* x_clipboard_text = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));
303 
304  gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
305 
306  if (x_clipboard_text != NULL) {
307  /* we reset the X clipboard with saved text */
308  gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), x_clipboard_text, -1);
309  g_free(x_clipboard_text);
310  }
311  }
312 
313  return true;
314 }
315 
316 bool
317 girara_sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
318 {
319  g_return_val_if_fail(session != NULL, false);
320 
321  girara_isc_abort(session, NULL, NULL, 0);
322 
323  gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
324 
325  if (session->global.autohide_inputbar == false) {
326  gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
327  }
328 
329  return false;
330 }
331 
332 bool
333 girara_sc_quit(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
334 {
335  g_return_val_if_fail(session != NULL, false);
336 
337  girara_argument_t arg = { GIRARA_HIDE, NULL };
338  girara_isc_completion(session, &arg, NULL, 0);
339 
340  gtk_main_quit();
341 
342  return false;
343 }
344 
345 bool
346 girara_sc_tab_close(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
347 {
348  g_return_val_if_fail(session != NULL, false);
349 
350  girara_tab_t* tab = girara_tab_current_get(session);
351 
352  if (tab != NULL) {
353  girara_tab_remove(session, tab);
354  }
355 
356  return false;
357 }
358 
359 bool
360 girara_sc_tab_navigate(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int t)
361 {
362  g_return_val_if_fail(session != NULL, false);
363 
364  unsigned int number_of_tabs = girara_get_number_of_tabs(session);
365  if (number_of_tabs == 0)
366  return false;
367 
368  unsigned int current_tab = girara_tab_position_get(session, girara_tab_current_get(session));
369  unsigned int step = (argument->n == GIRARA_PREVIOUS) ? -1 : 1;
370  unsigned int new_tab = (current_tab + step) % number_of_tabs;
371 
372  if (t != 0 && t <= number_of_tabs) {
373  new_tab = t - 1;
374  }
375 
376  girara_tab_t* tab = girara_tab_get(session, new_tab);
377 
378  if (tab != NULL) {
379  girara_tab_current_set(session, tab);
380  }
381 
382  girara_tab_update(session);
383 
384  return false;
385 }
386 
387 static void
388 girara_toggle_widget_visibility(GtkWidget* widget)
389 {
390  if (widget == NULL) {
391  return;
392  }
393 
394  if (gtk_widget_get_visible(widget)) {
395  gtk_widget_hide(widget);
396  } else {
397  gtk_widget_show(widget);
398  }
399 }
400 
401 bool
402 girara_sc_toggle_inputbar(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
403 {
404  g_return_val_if_fail(session != NULL, false);
405 
406  girara_toggle_widget_visibility(GTK_WIDGET(session->gtk.inputbar));
407 
408  return true;
409 }
410 
411 bool
412 girara_sc_toggle_statusbar(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
413 {
414  g_return_val_if_fail(session != NULL, false);
415 
416  girara_toggle_widget_visibility(GTK_WIDGET(session->gtk.statusbar));
417 
418  return true;
419 }
420 
421 bool
422 girara_sc_toggle_tabbar(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
423 {
424  g_return_val_if_fail(session != NULL, false);
425 
426  girara_toggle_widget_visibility(GTK_WIDGET(session->gtk.tabbar));
427 
428  return true;
429 }
430 
431 bool
432 girara_sc_set(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
433 {
434  g_return_val_if_fail(session != NULL, false);
435 
436  if (argument == NULL || argument->data == NULL) {
437  return false;
438  }
439 
440  /* create argument list */
441  girara_list_t* argument_list = girara_list_new();
442  if (argument_list == NULL) {
443  return false;
444  }
445 
446  gchar** argv = NULL;
447  gint argc = 0;
448 
449  girara_list_set_free_function(argument_list, g_free);
450  if (g_shell_parse_argv((const gchar*) argument->data, &argc, &argv, NULL) != FALSE) {
451  for(int i = 0; i < argc; i++) {
452  char* argument = g_strdup(argv[i]);
453  girara_list_append(argument_list, (void*) argument);
454  }
455  } else {
456  girara_list_free(argument_list);
457  return false;
458  }
459 
460  /* call set */
461  girara_cmd_set(session, argument_list);
462 
463  /* cleanup */
464  girara_list_free(argument_list);
465 
466  return false;
467 }
468 
469 bool
470 girara_sc_feedkeys(girara_session_t* session, girara_argument_t* argument,
471  girara_event_t* UNUSED(event), unsigned int t)
472 {
473  if (session == NULL || argument == NULL) {
474  return false;
475  }
476 
477  typedef struct gdk_keyboard_button_s {
478  char* identifier;
479  int keyval;
480  } gdk_keyboard_button_t;
481 
482  static const gdk_keyboard_button_t gdk_keyboard_buttons[] = {
483  {"BackSpace", GDK_KEY_BackSpace},
484  {"CapsLock", GDK_KEY_Caps_Lock},
485  {"Down", GDK_KEY_Down},
486  {"Esc", GDK_KEY_Escape},
487  {"F10", GDK_KEY_F10},
488  {"F11", GDK_KEY_F11},
489  {"F12", GDK_KEY_F12},
490  {"F1", GDK_KEY_F1},
491  {"F2", GDK_KEY_F2},
492  {"F3", GDK_KEY_F3},
493  {"F4", GDK_KEY_F4},
494  {"F5", GDK_KEY_F5},
495  {"F6", GDK_KEY_F6},
496  {"F7", GDK_KEY_F7},
497  {"F8", GDK_KEY_F8},
498  {"F9", GDK_KEY_F9},
499  {"Left", GDK_KEY_Left},
500  {"PageDown", GDK_KEY_Page_Down},
501  {"PageUp", GDK_KEY_Page_Up},
502  {"Home", GDK_KEY_Home},
503  {"End", GDK_KEY_End},
504  {"Return", GDK_KEY_Return},
505  {"Right", GDK_KEY_Right},
506  {"Space", GDK_KEY_space},
507  {"Super", GDK_KEY_Super_L},
508  {"Tab", GDK_KEY_Tab},
509  {"ShiftTab", GDK_KEY_ISO_Left_Tab},
510  {"Up", GDK_KEY_Up}
511  };
512 
513  char* input = (char*) argument->data;
514  unsigned int input_length = strlen(input);
515 
516  t = (t == 0) ? 1 : t;
517  for (unsigned int c = 0; c < t; c++) {
518  for (unsigned i = 0; i < input_length; i++) {
519  int state = 0;
520  int keyval = input[i];
521 
522  /* possible special button */
523  if ((input_length - i) >= 3 && input[i] == '<') {
524  char* end = strchr(input + i, '>');
525  if (end == NULL) {
526  goto single_key;
527  }
528 
529  int length = end - (input + i) - 1;
530  char* tmp = g_strndup(input + i + 1, length);
531  bool found = false;
532 
533  /* Multi key shortcut */
534  if (length > 2 && tmp[1] == '-') {
535  switch (tmp[0]) {
536  case 'S':
537  state = GDK_SHIFT_MASK;
538  break;
539  case 'A':
540  state = GDK_MOD1_MASK;
541  break;
542  case 'C':
543  state = GDK_CONTROL_MASK;
544  break;
545  default:
546  break;
547  }
548 
549  if (length == 3) {
550  keyval = tmp[2];
551  found = true;
552  } else {
553  for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
554  if (g_strcmp0(tmp + 2, gdk_keyboard_buttons[i].identifier) == 0) {
555  keyval = gdk_keyboard_buttons[i].keyval;
556  found = true;
557  break;
558  }
559  }
560  }
561  /* Possible special key */
562  } else {
563  for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
564  if (g_strcmp0(tmp, gdk_keyboard_buttons[i].identifier) == 0) {
565  keyval = gdk_keyboard_buttons[i].keyval;
566  found = true;
567  break;
568  }
569  }
570  }
571 
572  g_free(tmp);
573 
574  /* parsed special key */
575  if (found == true) {
576  i += length + 1;
577  }
578  }
579 
580 single_key:
581 
582  update_state_by_keyval(&state, keyval);
583  simulate_key_press(session, state, keyval);
584  }
585  }
586 
587  return true;
588 }
589 
590 bool
591 girara_shortcut_mapping_add(girara_session_t* session, const char* identifier, girara_shortcut_function_t function)
592 {
593  g_return_val_if_fail(session != NULL, false);
594 
595  if (function == NULL || identifier == NULL) {
596  return false;
597  }
598 
599  GIRARA_LIST_FOREACH(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, data)
600  if (strcmp(data->identifier, identifier) == 0) {
601  data->function = function;
603  return true;
604  }
605  GIRARA_LIST_FOREACH_END(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, data);
606 
607  /* add new config handle */
608  girara_shortcut_mapping_t* mapping = g_slice_new(girara_shortcut_mapping_t);
609 
610  mapping->identifier = g_strdup(identifier);
611  mapping->function = function;
612  girara_list_append(session->config.shortcut_mappings, mapping);
613 
614  return true;
615 }
616 
617 void
618 girara_shortcut_mapping_free(girara_shortcut_mapping_t* mapping)
619 {
620  if (mapping == NULL) {
621  return;
622  }
623 
624  g_free(mapping->identifier);
625  g_slice_free(girara_shortcut_mapping_t, mapping);
626 }
627 
628 bool
629 girara_argument_mapping_add(girara_session_t* session, const char* identifier, int value)
630 {
631  g_return_val_if_fail(session != NULL, false);
632 
633  if (identifier == NULL) {
634  return false;
635  }
636 
637  GIRARA_LIST_FOREACH(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping);
638  if (g_strcmp0(mapping->identifier, identifier) == 0) {
639  mapping->value = value;
641  return true;
642  }
643  GIRARA_LIST_FOREACH_END(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping);
644 
645  /* add new config handle */
646  girara_argument_mapping_t* mapping = g_slice_new(girara_argument_mapping_t);
647 
648  mapping->identifier = g_strdup(identifier);
649  mapping->value = value;
650  girara_list_append(session->config.argument_mappings, mapping);
651 
652  return true;
653 }
654 
655 void
656 girara_argument_mapping_free(girara_argument_mapping_t* argument_mapping)
657 {
658  if (argument_mapping == NULL) {
659  return;
660  }
661 
662  g_free(argument_mapping->identifier);
663  g_slice_free(girara_argument_mapping_t, argument_mapping);
664 }
665 
666 bool
667 girara_mouse_event_add(girara_session_t* session, guint mask, guint button,
669  event_type, int argument_n, void* argument_data)
670 {
671  g_return_val_if_fail(session != NULL, false);
672  g_return_val_if_fail(function != NULL, false);
673 
674  girara_argument_t argument = {argument_n, argument_data};
675 
676  /* search for existing binding */
677  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it)
678  if (me_it->mask == mask && me_it->button == button &&
679  me_it->mode == mode && me_it->event_type == event_type)
680  {
681  me_it->function = function;
682  me_it->argument = argument;
684  return true;
685  }
686  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it);
687 
688  /* add new mouse event */
689  girara_mouse_event_t* mouse_event = g_slice_new(girara_mouse_event_t);
690 
691  mouse_event->mask = mask;
692  mouse_event->button = button;
693  mouse_event->function = function;
694  mouse_event->mode = mode;
695  mouse_event->event_type = event_type;
696  mouse_event->argument = argument;
697  girara_list_append(session->bindings.mouse_events, mouse_event);
698 
699  return true;
700 }
701 
702 bool
703 girara_mouse_event_remove(girara_session_t* session, guint mask, guint button, girara_mode_t mode)
704 {
705  g_return_val_if_fail(session != NULL, false);
706 
707  /* search for existing binding */
708  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it)
709  if (me_it->mask == mask && me_it->button == button &&
710  me_it->mode == mode)
711  {
712  girara_list_remove(session->bindings.mouse_events, me_it);
714  return true;
715  }
716  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it);
717 
718  return false;
719 }
720 
721 void
722 girara_mouse_event_free(girara_mouse_event_t* mouse_event)
723 {
724  if (mouse_event == NULL) {
725  return;
726  }
727  g_slice_free(girara_mouse_event_t, mouse_event);
728 }
729 
730 static bool
731 simulate_key_press(girara_session_t* session, int state, int key)
732 {
733  if (session == NULL || session->gtk.box == NULL) {
734  return false;
735  }
736 
737  GdkEvent* event = gdk_event_new(GDK_KEY_PRESS);
738 
739  event->key.type = GDK_KEY_PRESS;
740  event->key.window = gtk_widget_get_parent_window(GTK_WIDGET(session->gtk.box));
741  event->key.send_event = false;
742  event->key.time = GDK_CURRENT_TIME;
743  event->key.state = state;
744  event->key.keyval = key;
745 
746  g_object_ref(event->key.window);
747 
748  GdkKeymapKey* keys;
749  gint number_of_keys;
750 
751  if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(),
752  event->key.keyval, &keys, &number_of_keys) == FALSE) {
753  gdk_event_free(event);
754  return false;
755  }
756 
757  event->key.hardware_keycode = keys[0].keycode;
758  event->key.group = keys[0].group;
759 
760  g_free(keys);
761 
762  gdk_event_put(event);
763  gdk_event_free(event);
764 
765  gtk_main_iteration_do(FALSE);
766 
767  return true;
768 }