girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
callbacks.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include "callbacks.h"
4 #include "datastructures.h"
5 #include "session.h"
6 #include "shortcuts.h"
7 #include "input-history.h"
8 #include <string.h>
9 #include <glib/gi18n-lib.h>
10 
11 #include "internal.h"
12 
13 static const guint ALL_ACCELS_MASK = GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK;
14 static const guint MOUSE_MASK = GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK |
15  GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK | GDK_BUTTON4_MASK | GDK_BUTTON5_MASK;
16 
17 static bool
18 clean_mask(guint hardware_keycode, GdkModifierType state, gint group, guint* clean, guint* keyval)
19 {
20  GdkModifierType consumed = 0;
21  if ((gdk_keymap_translate_keyboard_state(
22  gdk_keymap_get_default(),
23  hardware_keycode,
24  state, group,
25  keyval,
26  NULL,
27  NULL,
28  &consumed)
29  ) == FALSE) {
30  return false;
31  }
32 
33  if (clean != NULL) {
34  *clean = state & ~consumed & ALL_ACCELS_MASK;
35  }
36 
37  /* numpad numbers */
38  switch (*keyval) {
39  case GDK_KEY_KP_0:
40  *keyval = GDK_KEY_0;
41  break;
42  case GDK_KEY_KP_1:
43  *keyval = GDK_KEY_1;
44  break;
45  case GDK_KEY_KP_2:
46  *keyval = GDK_KEY_2;
47  break;
48  case GDK_KEY_KP_3:
49  *keyval = GDK_KEY_3;
50  break;
51  case GDK_KEY_KP_4:
52  *keyval = GDK_KEY_4;
53  break;
54  case GDK_KEY_KP_5:
55  *keyval = GDK_KEY_5;
56  break;
57  case GDK_KEY_KP_6:
58  *keyval = GDK_KEY_6;
59  break;
60  case GDK_KEY_KP_7:
61  *keyval = GDK_KEY_7;
62  break;
63  case GDK_KEY_KP_8:
64  *keyval = GDK_KEY_8;
65  break;
66  case GDK_KEY_KP_9:
67  *keyval = GDK_KEY_9;
68  break;
69  }
70 
71  return true;
72 }
73 
74 /* callback implementation */
75 bool
77  GdkEventKey* event, girara_session_t* session)
78 {
79  g_return_val_if_fail(session != NULL, FALSE);
80 
81  guint clean = 0;
82  guint keyval = 0;
83 
84  if (clean_mask(event->hardware_keycode, event->state, event->group, &clean, &keyval) == false) {
85  return false;
86  }
87 
88  /* prepare event */
89  GIRARA_LIST_FOREACH(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcut)
90  if (session->buffer.command != NULL) {
91  break;
92  }
93 
94  if ( keyval == shortcut->key
95  && (clean == shortcut->mask || (shortcut->key >= 0x21
96  && shortcut->key <= 0x7E && clean == GDK_SHIFT_MASK))
97  && (session->modes.current_mode == shortcut->mode || shortcut->mode == 0)
98  && shortcut->function != NULL
99  )
100  {
101  int t = (session->buffer.n > 0) ? session->buffer.n : 1;
102  for (int i = 0; i < t; i++) {
103  if (shortcut->function(session, &(shortcut->argument), NULL, session->buffer.n) == false) {
104  break;
105  }
106  }
107 
108  if (session->global.buffer != NULL) {
109  g_string_free(session->global.buffer, TRUE);
110  session->global.buffer = NULL;
111  }
112 
113  session->buffer.n = 0;
114 
115  if (session->events.buffer_changed != NULL) {
116  session->events.buffer_changed(session);
117  }
118 
120  return TRUE;
121  }
122  GIRARA_LIST_FOREACH_END(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcut);
123 
124  /* update buffer */
125  if (keyval >= 0x21 && keyval <= 0x7E) {
126  /* overall buffer */
127  if (session->global.buffer == NULL) {
128  session->global.buffer = g_string_new("");
129  }
130 
131  session->global.buffer = g_string_append_c(session->global.buffer, keyval);
132 
133  if (session->buffer.command == NULL && keyval >= 0x30 && keyval <= 0x39) {
134  if (((session->buffer.n * 10) + (keyval - '0')) < INT_MAX) {
135  session->buffer.n = (session->buffer.n * 10) + (keyval - '0');
136  }
137  } else {
138  if (session->buffer.command == NULL) {
139  session->buffer.command = g_string_new("");
140  }
141 
142  session->buffer.command = g_string_append_c(session->buffer.command, keyval);
143  }
144 
145  if (session->events.buffer_changed != NULL) {
146  session->events.buffer_changed(session);
147  }
148  }
149 
150  /* check for buffer command */
151  if (session->buffer.command != NULL) {
152  bool matching_command = FALSE;
153 
154  GIRARA_LIST_FOREACH(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcut)
155  if (shortcut->buffered_command != NULL) {
156  /* buffer could match a command */
157  if (!strncmp(session->buffer.command->str, shortcut->buffered_command, session->buffer.command->len)) {
158  /* command matches buffer exactly */
159  if (!strcmp(session->buffer.command->str, shortcut->buffered_command)
160  && (session->modes.current_mode == shortcut->mode || shortcut->mode == 0)) {
161  g_string_free(session->buffer.command, TRUE);
162  g_string_free(session->global.buffer, TRUE);
163  session->buffer.command = NULL;
164  session->global.buffer = NULL;
165 
166  if (session->events.buffer_changed != NULL) {
167  session->events.buffer_changed(session);
168  }
169 
170  int t = (session->buffer.n > 0) ? session->buffer.n : 1;
171  for (int i = 0; i < t; i++) {
172  if (shortcut->function(session, &(shortcut->argument), NULL, session->buffer.n) == false) {
173  break;
174  }
175  }
176 
177  session->buffer.n = 0;
179  return TRUE;
180  }
181 
182  matching_command = TRUE;
183  }
184  }
185  GIRARA_LIST_FOREACH_END(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcut);
186 
187  /* free buffer if buffer will never match a command */
188  if (matching_command == false) {
189  g_string_free(session->buffer.command, TRUE);
190  g_string_free(session->global.buffer, TRUE);
191  session->buffer.command = NULL;
192  session->global.buffer = NULL;
193  session->buffer.n = 0;
194 
195  if (session->events.buffer_changed != NULL) {
196  session->events.buffer_changed(session);
197  }
198  }
199  }
200 
201  return FALSE;
202 }
203 
204 bool
206  GdkEventButton* button, girara_session_t* session)
207 {
208  g_return_val_if_fail(session != NULL, false);
209  g_return_val_if_fail(button != NULL, false);
210 
211  /* prepare girara event */
212  girara_event_t event;
213 
214  switch (button->type) {
215  case GDK_BUTTON_PRESS:
216  event.type = GIRARA_EVENT_BUTTON_PRESS;
217  break;
218  case GDK_2BUTTON_PRESS:
219  event.type = GIRARA_EVENT_2BUTTON_PRESS;
220  break;
221  case GDK_3BUTTON_PRESS:
222  event.type = GIRARA_EVENT_3BUTTON_PRESS;
223  break;
224  default: /* do not handle unknown events */
225  event.type = GIRARA_EVENT_OTHER;
226  break;
227  }
228 
229  event.x = button->x;
230  event.y = button->y;
231 
232  const guint state = button->state & MOUSE_MASK;
233 
234  /* search registered mouse events */
235  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event)
236  if (mouse_event->function != NULL
237  && button->button == mouse_event->button
238  && state == mouse_event->mask
239  && mouse_event->event_type == event.type
240  && (session->modes.current_mode == mouse_event->mode || mouse_event->mode == 0)
241  ) {
242  mouse_event->function(session, &(mouse_event->argument), &event, session->buffer.n);
244  return true;
245  }
246  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event);
247 
248  return false;
249 }
250 
251 bool
252 girara_callback_view_button_release_event(GtkWidget* UNUSED(widget), GdkEventButton* button, girara_session_t* session)
253 {
254  g_return_val_if_fail(session != NULL, false);
255  g_return_val_if_fail(button != NULL, false);
256 
257  /* prepare girara event */
258  girara_event_t event;
259  event.type = GIRARA_EVENT_BUTTON_RELEASE;
260  event.x = button->x;
261  event.y = button->y;
262 
263  const guint state = button->state & MOUSE_MASK;
264 
265  /* search registered mouse events */
266  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event)
267  if (mouse_event->function != NULL
268  && button->button == mouse_event->button
269  && state == mouse_event->mask
270  && mouse_event->event_type == GIRARA_EVENT_BUTTON_RELEASE
271  && (session->modes.current_mode == mouse_event->mode || mouse_event->mode == 0)
272  ) {
273  mouse_event->function(session, &(mouse_event->argument), &event, session->buffer.n);
275  return true;
276  }
277  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event);
278 
279  return false;
280 }
281 
282 bool
283 girara_callback_view_button_motion_notify_event(GtkWidget* UNUSED(widget), GdkEventMotion* button, girara_session_t* session)
284 {
285  g_return_val_if_fail(session != NULL, false);
286  g_return_val_if_fail(button != NULL, false);
287 
288  /* prepare girara event */
289  girara_event_t event = {
291  .x = button->x,
292  .y = button->y
293  };
294 
295  const guint state = button->state & MOUSE_MASK;
296 
297  /* search registered mouse events */
298  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event)
299  if (mouse_event->function != NULL
300  && state == mouse_event->mask
301  && mouse_event->event_type == event.type
302  && (session->modes.current_mode == mouse_event->mode || mouse_event->mode == 0)
303  ) {
304  mouse_event->function(session, &(mouse_event->argument), &event, session->buffer.n);
306  return true;
307  }
308  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event);
309 
310  return false;
311 }
312 
313 bool
314 girara_callback_view_scroll_event(GtkWidget* UNUSED(widget), GdkEventScroll* scroll, girara_session_t* session)
315 {
316  g_return_val_if_fail(session != NULL, false);
317  g_return_val_if_fail(scroll != NULL, false);
318 
319  /* prepare girara event */
320  girara_event_t event;
321  event.x = scroll->x;
322  event.y = scroll->y;
323 
324  switch (scroll->direction) {
325  case GDK_SCROLL_UP:
326  event.type = GIRARA_EVENT_SCROLL_UP;
327  break;
328  case GDK_SCROLL_DOWN:
329  event.type = GIRARA_EVENT_SCROLL_DOWN;
330  break;
331  case GDK_SCROLL_LEFT:
332  event.type = GIRARA_EVENT_SCROLL_LEFT;
333  break;
334  case GDK_SCROLL_RIGHT:
335  event.type = GIRARA_EVENT_SCROLL_RIGHT;
336  break;
337  case GDK_SCROLL_SMOOTH:
339  /* We abuse x and y here. We really need more fields in girara_event_t. */
340  gdk_event_get_scroll_deltas((GdkEvent*)scroll, &event.x, &event.y);
341  break;
342  default:
343  return false;
344  }
345 
346  const guint state = scroll->state & MOUSE_MASK;
347 
348  /* search registered mouse events */
349  /* TODO: Filter correct event */
350  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event)
351  if (mouse_event->function != NULL
352  && state == mouse_event->mask
353  && mouse_event->event_type == event.type
354  && (session->modes.current_mode == mouse_event->mode || mouse_event->mode == 0)
355  ) {
356  mouse_event->function(session, &(mouse_event->argument), &event, session->buffer.n);
358  return true;
359  }
360  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event);
361 
362  return false;
363 }
364 
365 bool
366 girara_callback_inputbar_activate(GtkEntry* entry, girara_session_t* session)
367 {
368  g_return_val_if_fail(session != NULL, FALSE);
369 
370  /* a custom handler has been installed (e.g. by girara_dialog) */
371  if (session->signals.inputbar_custom_activate != NULL) {
372  bool return_value = session->signals.inputbar_custom_activate(entry, session->signals.inputbar_custom_data);
373 
374  /* disconnect custom handler */
375  session->signals.inputbar_custom_activate = NULL;
376  session->signals.inputbar_custom_key_press_event = NULL;
377  session->signals.inputbar_custom_data = NULL;
378 
379  if (session->gtk.inputbar_dialog != NULL && session->gtk.inputbar_entry != NULL) {
380  gtk_label_set_markup(session->gtk.inputbar_dialog, "");
381  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
382  if (session->global.autohide_inputbar == true) {
383  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
384  }
385  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
386  girara_isc_abort(session, NULL, NULL, 0);
387  return true;
388  }
389 
390  return return_value;
391  }
392 
393  gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
394  if (input == NULL) {
395  girara_isc_abort(session, NULL, NULL, 0);
396  return false;
397  }
398 
399  if (strlen(input) == 0) {
400  g_free(input);
401  girara_isc_abort(session, NULL, NULL, 0);
402  return false;
403  }
404 
405  /* append to command history */
406  const char* command = gtk_entry_get_text(entry);
407  girara_input_history_append(session->command_history, command);
408 
409  /* parse input */
410  gchar** argv = NULL;
411  gint argc = 0;
412 
413  if (g_shell_parse_argv(input, &argc, &argv, NULL) == FALSE) {
414  g_free(input);
415  return false;
416  }
417 
418  gchar *cmd = argv[0];
419 
420  /* special commands */
421  char *identifier_s = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, 1);
422  if (identifier_s == NULL) {
423  g_free(input);
424  g_strfreev(argv);
425  return false;
426  }
427 
428  char identifier = identifier_s[0];
429  g_free(identifier_s);
430 
431  GIRARA_LIST_FOREACH(session->bindings.special_commands, girara_special_command_t*, iter, special_command)
432  if (special_command->identifier == identifier) {
433  if (special_command->always != true) {
434  special_command->function(session, input, &(special_command->argument));
435  }
436 
437  g_free(input);
438  g_strfreev(argv);
439 
440  girara_isc_abort(session, NULL, NULL, 0);
441 
443  return true;
444  }
445  GIRARA_LIST_FOREACH_END(session->bindings.special_commands, girara_special_command_t*, iter, special_command);
446 
447  /* search commands */
448  GIRARA_LIST_FOREACH(session->bindings.commands, girara_command_t*, iter, command)
449  if ((g_strcmp0(cmd, command->command) == 0) ||
450  (g_strcmp0(cmd, command->abbr) == 0))
451  {
452  girara_list_t* argument_list = girara_list_new();
453  if (argument_list == NULL) {
454  g_free(input);
455  g_strfreev(argv);
457  return false;
458  }
459 
460  girara_list_set_free_function(argument_list, g_free);
461 
462  for(int i = 1; i < argc; i++) {
463  char* argument = g_strdup(argv[i]);
464  girara_list_append(argument_list, (void*) argument);
465  }
466 
467  command->function(session, argument_list);
468 
469  girara_list_free(argument_list);
470  g_free(input);
471  g_strfreev(argv);
472 
473  girara_isc_abort(session, NULL, NULL, 0);
474 
475  if (session->global.autohide_inputbar == true) {
476  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
477  }
478  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
480  return true;
481  }
482  GIRARA_LIST_FOREACH_END(session->bindings.commands, girara_command_t*, iter, command);
483 
484  /* check for unknown command event handler */
485  if (session->events.unknown_command != NULL) {
486  if (session->events.unknown_command(session, input) == true) {
487  g_strfreev(argv);
488  g_free(input);
489  girara_isc_abort(session, NULL, NULL, 0);
490 
491  if (session->global.autohide_inputbar == true) {
492  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
493  }
494  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
495 
496  return true;
497  }
498  }
499 
500  /* unhandled command */
501  girara_notify(session, GIRARA_ERROR, _("Not a valid command: %s"), cmd);
502  g_strfreev(argv);
503  girara_isc_abort(session, NULL, NULL, 0);
504 
505  return false;
506 }
507 
508 bool
509 girara_callback_inputbar_key_press_event(GtkWidget* entry, GdkEventKey* event, girara_session_t* session)
510 {
511  g_return_val_if_fail(session != NULL, false);
512 
513  /* a custom handler has been installed (e.g. by girara_dialog) */
514  bool custom_ret = false;
515  if (session->signals.inputbar_custom_key_press_event != NULL) {
516  custom_ret = session->signals.inputbar_custom_key_press_event(entry, event, session->signals.inputbar_custom_data);
517  if (custom_ret == true) {
518  girara_isc_abort(session, NULL, NULL, 0);
519 
520  if (session->global.autohide_inputbar == true) {
521  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
522  }
523  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
524  }
525  }
526 
527  guint keyval = 0;
528  guint clean = 0;
529 
530  if (clean_mask(event->hardware_keycode, event->state, event->group, &clean, &keyval) == false) {
531  return false;
532  }
533 
534  if (custom_ret == false) {
535  GIRARA_LIST_FOREACH(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inputbar_shortcut)
536  if (inputbar_shortcut->key == keyval
537  && inputbar_shortcut->mask == clean)
538  {
539  if (inputbar_shortcut->function != NULL) {
540  inputbar_shortcut->function(session, &(inputbar_shortcut->argument), NULL, 0);
541  }
542 
544  return true;
545  }
546  GIRARA_LIST_FOREACH_END(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inputbar_shortcut);
547  }
548 
549  if ((session->gtk.results != NULL) &&
550  (gtk_widget_get_visible(GTK_WIDGET(session->gtk.results)) == TRUE) &&
551  (keyval == GDK_KEY_space))
552  {
553  gtk_widget_hide(GTK_WIDGET(session->gtk.results));
554  }
555 
556  return custom_ret;
557 }
558 
559 bool
560 girara_callback_inputbar_changed_event(GtkEditable* entry, girara_session_t* session)
561 {
562  g_return_val_if_fail(session != NULL, false);
563 
564  /* special commands */
565  char *identifier_s = gtk_editable_get_chars(entry, 0, 1);
566  if (identifier_s == NULL) {
567  return false;
568  }
569 
570  char identifier = identifier_s[0];
571  g_free(identifier_s);
572 
573  GIRARA_LIST_FOREACH(session->bindings.special_commands, girara_special_command_t*, iter, special_command)
574  if ((special_command->identifier == identifier) &&
575  (special_command->always == true))
576  {
577  gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
578  special_command->function(session, input, &(special_command->argument));
579  g_free(input);
581  return true;
582  }
583  GIRARA_LIST_FOREACH_END(session->bindings.special_commands, girara_special_command_t*, iter, special_command);
584 
585  return false;
586 }
void girara_list_append(girara_list_t *list, void *data)
bool girara_callback_view_button_release_event(GtkWidget *UNUSED(widget), GdkEventButton *button, girara_session_t *session)
Definition: callbacks.c:252
#define UNUSED(x)
Definition: internal.h:15
bool girara_callback_inputbar_changed_event(GtkEditable *entry, girara_session_t *session)
Definition: callbacks.c:560
bool girara_callback_view_key_press_event(GtkWidget *UNUSED(widget), GdkEventKey *event, girara_session_t *session)
Definition: callbacks.c:76
void girara_list_free(girara_list_t *list)
bool girara_callback_inputbar_key_press_event(GtkWidget *entry, GdkEventKey *event, girara_session_t *session)
Definition: callbacks.c:509
void girara_list_set_free_function(girara_list_t *list, girara_free_function_t gfree)
girara_list_t * girara_list_new(void)
void girara_list_iterator_free(girara_list_iterator_t *iter)
bool girara_callback_view_button_motion_notify_event(GtkWidget *UNUSED(widget), GdkEventMotion *button, girara_session_t *session)
Definition: callbacks.c:283
bool girara_isc_abort(girara_session_t *session, girara_argument_t *UNUSED(argument), girara_event_t *UNUSED(event), unsigned int UNUSED(t))
Definition: shortcuts.c:152
void girara_notify(girara_session_t *session, int level, const char *format,...)
Definition: session.c:692
bool girara_callback_inputbar_activate(GtkEntry *entry, girara_session_t *session)
Definition: callbacks.c:366
bool girara_callback_view_scroll_event(GtkWidget *UNUSED(widget), GdkEventScroll *scroll, girara_session_t *session)
Definition: callbacks.c:314
bool girara_callback_view_button_press_event(GtkWidget *UNUSED(widget), GdkEventButton *button, girara_session_t *session)
Definition: callbacks.c:205
void girara_input_history_append(GiraraInputHistory *history, const char *input)
#define GIRARA_LIST_FOREACH_END(list, type, iter, data)
#define GIRARA_LIST_FOREACH(list, type, iter, data)