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