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