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  default:
338  return false;
339  }
340 
341  const guint state = scroll->state & MOUSE_MASK;
342 
343  /* search registered mouse events */
344  /* TODO: Filter correct event */
345  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event)
346  if (mouse_event->function != NULL
347  && state == mouse_event->mask
348  && mouse_event->event_type == event.type
349  && (session->modes.current_mode == mouse_event->mode || mouse_event->mode == 0)
350  ) {
351  mouse_event->function(session, &(mouse_event->argument), &event, session->buffer.n);
353  return true;
354  }
355  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, mouse_event);
356 
357  return false;
358 }
359 
360 bool
361 girara_callback_inputbar_activate(GtkEntry* entry, girara_session_t* session)
362 {
363  g_return_val_if_fail(session != NULL, FALSE);
364 
365  /* a custom handler has been installed (e.g. by girara_dialog) */
366  if (session->signals.inputbar_custom_activate != NULL) {
367  bool return_value = session->signals.inputbar_custom_activate(entry, session->signals.inputbar_custom_data);
368 
369  /* disconnect custom handler */
370  session->signals.inputbar_custom_activate = NULL;
371  session->signals.inputbar_custom_key_press_event = NULL;
372  session->signals.inputbar_custom_data = NULL;
373 
374  if (session->gtk.inputbar_dialog != NULL && session->gtk.inputbar_entry != NULL) {
375  gtk_label_set_markup(session->gtk.inputbar_dialog, "");
376  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
377  if (session->global.autohide_inputbar == true) {
378  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
379  }
380  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
381  girara_isc_abort(session, NULL, NULL, 0);
382  return true;
383  }
384 
385  return return_value;
386  }
387 
388  gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
389  if (input == NULL) {
390  girara_isc_abort(session, NULL, NULL, 0);
391  return false;
392  }
393 
394  if (strlen(input) == 0) {
395  g_free(input);
396  girara_isc_abort(session, NULL, NULL, 0);
397  return false;
398  }
399 
400  /* append to command history */
401  const char* command = gtk_entry_get_text(entry);
402  girara_input_history_append(session->command_history, command);
403 
404  /* parse input */
405  gchar** argv = NULL;
406  gint argc = 0;
407 
408  if (g_shell_parse_argv(input, &argc, &argv, NULL) == FALSE) {
409  g_free(input);
410  return false;
411  }
412 
413  gchar *cmd = argv[0];
414 
415  /* special commands */
416  char *identifier_s = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, 1);
417  if (identifier_s == NULL) {
418  g_free(input);
419  g_strfreev(argv);
420  return false;
421  }
422 
423  char identifier = identifier_s[0];
424  g_free(identifier_s);
425 
426  GIRARA_LIST_FOREACH(session->bindings.special_commands, girara_special_command_t*, iter, special_command)
427  if (special_command->identifier == identifier) {
428  if (special_command->always != true) {
429  special_command->function(session, input, &(special_command->argument));
430  }
431 
432  g_free(input);
433  g_strfreev(argv);
434 
435  girara_isc_abort(session, NULL, NULL, 0);
436 
438  return true;
439  }
440  GIRARA_LIST_FOREACH_END(session->bindings.special_commands, girara_special_command_t*, iter, special_command);
441 
442  /* search commands */
443  GIRARA_LIST_FOREACH(session->bindings.commands, girara_command_t*, iter, command)
444  if ((g_strcmp0(cmd, command->command) == 0) ||
445  (g_strcmp0(cmd, command->abbr) == 0))
446  {
447  girara_list_t* argument_list = girara_list_new();
448  if (argument_list == NULL) {
449  g_free(input);
450  g_strfreev(argv);
452  return false;
453  }
454 
455  girara_list_set_free_function(argument_list, g_free);
456 
457  for(int i = 1; i < argc; i++) {
458  char* argument = g_strdup(argv[i]);
459  girara_list_append(argument_list, (void*) argument);
460  }
461 
462  command->function(session, argument_list);
463 
464  girara_list_free(argument_list);
465  g_free(input);
466  g_strfreev(argv);
467 
468  girara_isc_abort(session, NULL, NULL, 0);
469 
470  if (session->global.autohide_inputbar == true) {
471  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
472  }
473  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
475  return true;
476  }
477  GIRARA_LIST_FOREACH_END(session->bindings.commands, girara_command_t*, iter, command);
478 
479  /* check for unknown command event handler */
480  if (session->events.unknown_command != NULL) {
481  if (session->events.unknown_command(session, input) == true) {
482  g_strfreev(argv);
483  g_free(input);
484  girara_isc_abort(session, NULL, NULL, 0);
485 
486  if (session->global.autohide_inputbar == true) {
487  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
488  }
489  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
490 
491  return true;
492  }
493  }
494 
495  /* unhandled command */
496  girara_notify(session, GIRARA_ERROR, _("Not a valid command: %s"), cmd);
497  g_strfreev(argv);
498  girara_isc_abort(session, NULL, NULL, 0);
499 
500  return false;
501 }
502 
503 bool
504 girara_callback_inputbar_key_press_event(GtkWidget* entry, GdkEventKey* event, girara_session_t* session)
505 {
506  g_return_val_if_fail(session != NULL, false);
507 
508  /* a custom handler has been installed (e.g. by girara_dialog) */
509  bool custom_ret = false;
510  if (session->signals.inputbar_custom_key_press_event != NULL) {
511  custom_ret = session->signals.inputbar_custom_key_press_event(entry, event, session->signals.inputbar_custom_data);
512  if (custom_ret == true) {
513  girara_isc_abort(session, NULL, NULL, 0);
514 
515  if (session->global.autohide_inputbar == true) {
516  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
517  }
518  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
519  }
520  }
521 
522  guint keyval = 0;
523  guint clean = 0;
524 
525  if (clean_mask(event->hardware_keycode, event->state, event->group, &clean, &keyval) == false) {
526  return false;
527  }
528 
529  if (custom_ret == false) {
530  GIRARA_LIST_FOREACH(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inputbar_shortcut)
531  if (inputbar_shortcut->key == keyval
532  && inputbar_shortcut->mask == clean)
533  {
534  if (inputbar_shortcut->function != NULL) {
535  inputbar_shortcut->function(session, &(inputbar_shortcut->argument), NULL, 0);
536  }
537 
539  return true;
540  }
541  GIRARA_LIST_FOREACH_END(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inputbar_shortcut);
542  }
543 
544  if ((session->gtk.results != NULL) &&
545  (gtk_widget_get_visible(GTK_WIDGET(session->gtk.results)) == TRUE) &&
546  (keyval == GDK_KEY_space))
547  {
548  gtk_widget_hide(GTK_WIDGET(session->gtk.results));
549  }
550 
551  return custom_ret;
552 }
553 
554 bool
555 girara_callback_inputbar_changed_event(GtkEditable* entry, girara_session_t* session)
556 {
557  g_return_val_if_fail(session != NULL, false);
558 
559  /* special commands */
560  char *identifier_s = gtk_editable_get_chars(entry, 0, 1);
561  if (identifier_s == NULL) {
562  return false;
563  }
564 
565  char identifier = identifier_s[0];
566  g_free(identifier_s);
567 
568  GIRARA_LIST_FOREACH(session->bindings.special_commands, girara_special_command_t*, iter, special_command)
569  if ((special_command->identifier == identifier) &&
570  (special_command->always == true))
571  {
572  gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
573  special_command->function(session, input, &(special_command->argument));
574  g_free(input);
576  return true;
577  }
578  GIRARA_LIST_FOREACH_END(session->bindings.special_commands, girara_special_command_t*, iter, special_command);
579 
580  return false;
581 }
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:555
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:504
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:509
bool girara_callback_inputbar_activate(GtkEntry *entry, girara_session_t *session)
Definition: callbacks.c:361
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)