girara
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
commands.c
Go to the documentation of this file.
1 /* See LICENSE file for license and copyright information */
2 
3 #include <string.h>
4 #include <stdlib.h>
5 #include <glib/gi18n-lib.h>
6 
7 #include "commands.h"
8 #include "datastructures.h"
9 #include "session.h"
10 #include "internal.h"
11 #include "utils.h"
12 #include "settings.h"
13 #include "shortcuts.h"
14 
15 #if GTK_MAJOR_VERSION == 2
16 #include "gtk2-compat.h"
17 #endif
18 
19 /* default commands implementation */
20 bool
21 girara_cmd_map_unmap(girara_session_t* session, girara_list_t* argument_list,
22  bool unmap)
23 {
24  typedef struct gdk_keyboard_button_s
25  {
26  char* identifier;
27  int keyval;
28  } gdk_keyboard_button_t;
29 
30  static const gdk_keyboard_button_t gdk_keyboard_buttons[] = {
31  {"BackSpace", GDK_KEY_BackSpace},
32  {"CapsLock", GDK_KEY_Caps_Lock},
33  {"Down", GDK_KEY_Down},
34  {"Esc", GDK_KEY_Escape},
35  {"F10", GDK_KEY_F10},
36  {"F11", GDK_KEY_F11},
37  {"F12", GDK_KEY_F12},
38  {"F1", GDK_KEY_F1},
39  {"F2", GDK_KEY_F2},
40  {"F3", GDK_KEY_F3},
41  {"F4", GDK_KEY_F4},
42  {"F5", GDK_KEY_F5},
43  {"F6", GDK_KEY_F6},
44  {"F7", GDK_KEY_F7},
45  {"F8", GDK_KEY_F8},
46  {"F9", GDK_KEY_F9},
47  {"Left", GDK_KEY_Left},
48  {"PageDown", GDK_KEY_Page_Down},
49  {"PageUp", GDK_KEY_Page_Up},
50  {"Home", GDK_KEY_Home},
51  {"End", GDK_KEY_End},
52  {"Return", GDK_KEY_Return},
53  {"Right", GDK_KEY_Right},
54  {"Space", GDK_KEY_space},
55  {"Super", GDK_KEY_Super_L},
56  {"Tab", GDK_KEY_Tab},
57  {"ShiftTab", GDK_KEY_ISO_Left_Tab},
58  {"Up", GDK_KEY_Up}
59  };
60 
61  typedef struct gdk_mouse_button_s
62  {
63  char* identifier;
64  int button;
65  } gdk_mouse_button_t;
66 
67  static const gdk_mouse_button_t gdk_mouse_buttons[] = {
68  {"Button1", GIRARA_MOUSE_BUTTON1},
69  {"Button2", GIRARA_MOUSE_BUTTON2},
70  {"Button3", GIRARA_MOUSE_BUTTON3},
71  {"Button4", GIRARA_MOUSE_BUTTON4},
72  {"Button5", GIRARA_MOUSE_BUTTON5},
73  {"Button6", GIRARA_MOUSE_BUTTON6},
74  {"Button7", GIRARA_MOUSE_BUTTON7},
75  {"Button8", GIRARA_MOUSE_BUTTON8},
76  {"Button9", GIRARA_MOUSE_BUTTON9}
77  };
78 
79  typedef struct event_type_s
80  {
81  char* identifier;
82  int event;
83  } event_type_t;
84 
85  static const event_type_t event_types[] = {
86  {"motion", GIRARA_EVENT_MOTION_NOTIFY},
87  {"scroll_up", GIRARA_EVENT_SCROLL_UP},
88  {"scroll_down", GIRARA_EVENT_SCROLL_DOWN},
89  {"scroll_left", GIRARA_EVENT_SCROLL_LEFT},
90  {"scroll_right", GIRARA_EVENT_SCROLL_RIGHT}
91  };
92 
93  typedef struct mouse_event_s
94  {
95  char* identifier;
96  int event;
97  } mouse_event_t;
98 
99  static const mouse_event_t mouse_events[] = {
100  {"button-pressed", GIRARA_EVENT_BUTTON_PRESS},
101  {"2-button-pressed", GIRARA_EVENT_2BUTTON_PRESS},
102  {"3-button-pressed", GIRARA_EVENT_2BUTTON_PRESS},
103  {"button-released", GIRARA_EVENT_BUTTON_RELEASE}
104  };
105 
106  size_t number_of_arguments = girara_list_size(argument_list);
107 
108  unsigned int limit = (unmap == true) ? 1 : 2;
109  if (number_of_arguments < limit) {
110  girara_warning("Invalid number of arguments passed: %zu instead of at least %u", number_of_arguments, limit);
111  girara_notify(session, GIRARA_ERROR,
112  _("Invalid number of arguments passed: %zu instead of at least %u"), number_of_arguments, limit);
113  return false;
114  }
115 
116  int shortcut_mask = 0;
117  int shortcut_key = 0;
118  int shortcut_mouse_button = 0;
119  girara_mode_t shortcut_mode = session->modes.normal;
120  char* shortcut_argument_data = NULL;
121  int shortcut_argument_n = 0;
122  char* shortcut_buffer_command = NULL;
124  girara_shortcut_function_t shortcut_function = NULL;
125  bool mouse_event = false;
126 
127  size_t current_command = 0;
128  char* tmp = girara_list_nth(argument_list, current_command);
129  size_t tmp_length = strlen(tmp);
130 
131  /* Check first argument for mode */
132  bool is_mode = false;
133  if (tmp_length >= 3 && tmp[0] == '[' && tmp[tmp_length - 1] == ']') {
134  char* tmp_inner = g_strndup(tmp + 1, tmp_length - 2);
135 
136  GIRARA_LIST_FOREACH(session->modes.identifiers, girara_mode_string_t*, iter, mode)
137  if (!g_strcmp0(tmp_inner, mode->name)) {
138  shortcut_mode = mode->index;
139  is_mode = true;
140  break;
141  }
142  GIRARA_LIST_FOREACH_END(session->modes.identifiers, girara_mode_string_t*, iter, mode);
143 
144  if (is_mode == false) {
145  girara_warning("Unregistered mode specified: %s", tmp_inner);
146  girara_notify(session, GIRARA_ERROR, _("Unregistered mode specified: %s"), tmp_inner);
147  g_free(tmp_inner);
148  return false;
149  }
150  g_free(tmp_inner);
151  }
152 
153  if (is_mode == true) {
154  tmp = girara_list_nth(argument_list, ++current_command);
155  tmp_length = strlen(tmp);
156  }
157 
158  /* Check for multi key shortcut */
159  if (tmp_length >= 3 && tmp[0] == '<' && tmp[tmp_length - 1] == '>') {
160  tmp = g_strndup(tmp + 1, tmp_length - 2);
161  tmp_length = strlen(tmp);
162 
163  /* Multi key shortcut */
164  if (strchr(tmp, '-') != NULL && tmp[1] == '-' && tmp_length > 2) {
165  switch (tmp[0]) {
166  case 'S':
167  shortcut_mask = GDK_SHIFT_MASK;
168  break;
169  case 'A':
170  case 'M':
171  shortcut_mask = GDK_MOD1_MASK;
172  break;
173  case 'C':
174  shortcut_mask = GDK_CONTROL_MASK;
175  break;
176  default:
177  girara_warning("Invalid modifier in %s", tmp);
178  girara_notify(session, GIRARA_ERROR, _("Invalid modifier in %s"), tmp);
179  g_free(tmp);
180  return false;
181  }
182 
183  /* Single key */
184  if (tmp_length == 3) {
185  shortcut_key = tmp[2];
186  /* Possible special key */
187  } else {
188  bool found = false;
189  for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
190  if (g_strcmp0(tmp + 2, gdk_keyboard_buttons[i].identifier) == 0) {
191  shortcut_key = gdk_keyboard_buttons[i].keyval;
192  found = true;
193  break;
194  }
195  }
196 
197  for (unsigned int i = 0; i < LENGTH(gdk_mouse_buttons); i++) {
198  if (!g_strcmp0(tmp + 2, gdk_mouse_buttons[i].identifier)) {
199  shortcut_mouse_button = gdk_mouse_buttons[i].button;
200  mouse_event = true;
201  found = true;
202  break;
203  }
204  }
205 
206  for (unsigned int i = 0; i < LENGTH(event_types); i++) {
207  if (!g_strcmp0(tmp + 2, event_types[i].identifier)) {
208  event_type = event_types[i].event;
209  mouse_event = true;
210  found = true;
211  break;
212  }
213  }
214 
215  if (found == false) {
216  girara_warning("Invalid special key value or mode: %s", tmp);
217  girara_notify(session, GIRARA_ERROR, _("Invalid special key value for %s"), tmp);
218  g_free(tmp);
219  return false;
220  }
221  }
222  /* Possible special key */
223  } else {
224  bool found = false;
225  for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
226  if (g_strcmp0(tmp, gdk_keyboard_buttons[i].identifier) == 0) {
227  shortcut_key = gdk_keyboard_buttons[i].keyval;
228  found = true;
229  break;
230  }
231  }
232 
233  for (unsigned int i = 0; i < LENGTH(gdk_mouse_buttons); i++) {
234  if (!g_strcmp0(tmp, gdk_mouse_buttons[i].identifier)) {
235  shortcut_mouse_button = gdk_mouse_buttons[i].button;
236  mouse_event = true;
237  found = true;
238  break;
239  }
240  }
241 
242  for (unsigned int i = 0; i < LENGTH(event_types); i++) {
243  if (!g_strcmp0(tmp, event_types[i].identifier)) {
244  event_type = event_types[i].event;
245  mouse_event = true;
246  found = true;
247  break;
248  }
249  }
250 
251  if (found == false) {
252  girara_warning("Invalid special key value or mode: %s", tmp);
253  girara_notify(session, GIRARA_ERROR, _("Invalid special key value or mode %s"), tmp);
254  g_free(tmp);
255  return false;
256  }
257  }
258 
259  g_free(tmp);
260  /* Single key shortcut */
261  } else if (tmp_length == 1) {
262  shortcut_key = tmp[0];
263  /* Buffer command */
264  } else {
265  shortcut_buffer_command = g_strdup(tmp);
266  }
267 
268  /* check for mouse mode */
269  bool mouse_mode = false;
270  if (unmap == false) {
271  if (++current_command < number_of_arguments) {
272  tmp = girara_list_nth(argument_list, current_command);
273  tmp_length = strlen(tmp);
274 
275  if (tmp_length >= 3 && tmp[0] == '[' && tmp[tmp_length - 1] == ']') {
276  mouse_mode = true;
277  if (mouse_event == false) {
278  girara_warning("Mode passed on non-mouse event: %s", tmp);
279  return false;
280  }
281 
282  char* tmp_inner = g_strndup(tmp + 1, tmp_length - 2);
283 
284  bool found = false;
285  for (unsigned int i = 0; i < LENGTH(mouse_events); i++) {
286  if (!g_strcmp0(tmp_inner, mouse_events[i].identifier)) {
287  event_type = mouse_events[i].event;
288  found = true;
289  break;
290  }
291  }
292 
293  if (found == false) {
294  girara_warning("Invalid mouse event mode has been passed: %s", tmp_inner);
295  g_free(tmp_inner);
296  return false;
297  }
298 
299  g_free(tmp_inner);
300  }
301  } else {
302  girara_warning("Invalid number of arguments passed");
303  return false;
304  }
305  }
306 
307  if (unmap == false) {
308  limit = (mouse_mode == true) ? 3 : 2;
309  if (number_of_arguments < limit) {
310  girara_warning("Invalid number of arguments passed: %zu instead of at least %u", number_of_arguments, limit);
311  girara_notify(session, GIRARA_ERROR,
312  _("Invalid number of arguments passed: %zu instead of at least %u"), number_of_arguments, limit);
313  return false;
314  }
315 
316  if (mouse_mode == true) {
317  tmp = girara_list_nth(argument_list, ++current_command);
318  }
319  }
320 
321  /* Check for passed shortcut command */
322  if (unmap == false) {
323  bool found_mapping = false;
324  GIRARA_LIST_FOREACH(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, mapping)
325  if (!g_strcmp0(tmp, mapping->identifier)) {
326  shortcut_function = mapping->function;
327  found_mapping = true;
328  break;
329  }
330  GIRARA_LIST_FOREACH_END(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, mapping);
331 
332  if (found_mapping == false) {
333  girara_warning("Not a valid shortcut function: %s", tmp);
334  girara_notify(session, GIRARA_ERROR, _("Not a valid shortcut function: %s"), tmp);
335  if (shortcut_buffer_command) {
336  g_free(shortcut_buffer_command);
337  }
338  return false;
339  }
340  }
341 
342  /* Check for passed argument */
343  if (unmap == false) {
344  if (++current_command < number_of_arguments) {
345  tmp = (char*) girara_list_nth(argument_list, current_command);
346 
347  GIRARA_LIST_FOREACH(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping)
348  if (!g_strcmp0(tmp, mapping->identifier)) {
349  shortcut_argument_n = mapping->value;
350  break;
351  }
352  GIRARA_LIST_FOREACH_END(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping);
353 
354  /* If no known argument is passed we save it in the data field */
355  if (shortcut_argument_n == 0) {
356  shortcut_argument_data = tmp;
357  /* If a known argument is passed and there are still more arguments,
358  * we save the next one in the data field */
359  } else if (++current_command < number_of_arguments) {
360  tmp = (char*) girara_list_nth(argument_list, current_command);
361  shortcut_argument_data = tmp;
362  }
363  }
364  }
365 
366  if (mouse_event == false) {
367  if (unmap == true) {
368  girara_shortcut_remove(session, shortcut_mask, shortcut_key,
369  shortcut_buffer_command, shortcut_mode);
370  } else {
371  girara_shortcut_add(session, shortcut_mask, shortcut_key, shortcut_buffer_command,
372  shortcut_function, shortcut_mode, shortcut_argument_n, shortcut_argument_data);
373  }
374  } else {
375  if (unmap == true) {
376  girara_mouse_event_remove(session, shortcut_mask, shortcut_mouse_button,
377  shortcut_mode);
378  } else {
379  girara_mouse_event_add(session, shortcut_mask, shortcut_mouse_button,
380  shortcut_function, shortcut_mode, event_type, shortcut_argument_n, shortcut_argument_data);
381  }
382  }
383 
384  if (shortcut_buffer_command) {
385  g_free(shortcut_buffer_command);
386  }
387 
388  return true;
389 }
390 
391 bool
392 girara_cmd_map(girara_session_t* session, girara_list_t* argument_list)
393 {
394  return girara_cmd_map_unmap(session, argument_list, false);
395 }
396 
397 bool
398 girara_cmd_unmap(girara_session_t* session, girara_list_t* argument_list)
399 {
400  return girara_cmd_map_unmap(session, argument_list, true);
401 }
402 
403 
404 bool
405 girara_cmd_quit(girara_session_t* session, girara_list_t* UNUSED(argument_list))
406 {
407  girara_argument_t arg = { GIRARA_HIDE, NULL };
408  girara_isc_completion(session, &arg, NULL, 0);
409 
410  gtk_main_quit();
411 
412  return true;
413 }
414 
415 bool
416 girara_cmd_set(girara_session_t* session, girara_list_t* argument_list)
417 {
418  const size_t number_of_arguments = girara_list_size(argument_list);
419 
420  if (number_of_arguments == 0) {
421  girara_warning("Not enough arguments for :set.");
422  girara_notify(session, GIRARA_ERROR, _("Not enough arguments."));
423  return false;
424  }
425  if (number_of_arguments > 2) {
426  girara_warning("Too many arguments for :set.");
427  girara_notify(session, GIRARA_ERROR, _("Too many arguments."));
428  return false;
429  }
430 
431  /* search for existing setting */
432  char* name = (char*) girara_list_nth(argument_list, 0);
433  if (name == NULL) {
434  return false;
435  }
436 
437  girara_setting_t* setting = girara_setting_find(session, name);
438  if (setting == NULL) {
439  girara_warning("Unknown option: %s", name);
440  girara_notify(session, GIRARA_ERROR, _("Unknown option: %s"), name);
441  return false;
442  }
443 
444  if (number_of_arguments == 1) {
445  /* display setting*/
446  switch (girara_setting_get_type(setting)) {
447  case BOOLEAN:
448  {
449  /* for compatibility reasons: toogle the setting */
450  bool value = false;
451  girara_setting_get_value(setting, &value);
452  bool tmp = !value;
453  girara_setting_set_value(session, setting, &tmp);
454  girara_notify(session, GIRARA_INFO, "%s: %s", name, tmp ? _("true") : _("false"));
455  break;
456  }
457  case FLOAT:
458  {
459  float value = 0;
460  girara_setting_get_value(setting, &value);
461  girara_notify(session, GIRARA_INFO, "%s: %f", name, value);
462  break;
463  }
464  case INT:
465  {
466  int value = 0;
467  girara_setting_get_value(setting, &value);
468  girara_notify(session, GIRARA_INFO, "%s: %i", name, value);
469  break;
470  }
471  case STRING:
472  {
473  char* str = NULL;
474  girara_setting_get_value(setting, &str);
475  girara_notify(session, GIRARA_INFO, "%s: %s", name, str ? str : "(NULL)");
476  g_free(str);
477  break;
478  }
479  default:
480  return false;
481  }
482  } else {
483  char* value = (char*) girara_list_nth(argument_list, 1);
484  if (value == NULL) {
485  girara_warning("No value defined for option: %s", name);
486  girara_notify(session, GIRARA_ERROR, _("No value defined for option: %s"), name);
487  return false;
488  }
489 
490  /* update value */
491  switch (girara_setting_get_type(setting)) {
492  case BOOLEAN:
493  if (g_strcmp0(value, "false") == 0 || g_strcmp0(value, "0") == 0) {
494  bool b = false;
495  girara_setting_set_value(session, setting, &b);
496  } else if (g_strcmp0(value, "true") == 0 || g_strcmp0(value, "1") == 0) {
497  bool b = true;
498  girara_setting_set_value(session, setting, &b);
499  } else {
500  girara_warning("Unknown value for option: %s", name);
501  girara_notify(session, GIRARA_ERROR, _("Unknown value for option: %s"), name);
502  }
503  break;
504  case FLOAT:
505  {
506  float f = g_ascii_strtod(value, NULL);
507  girara_setting_set_value(session, setting, &f);
508  break;
509  }
510  case INT:
511  {
512  int i = atoi(value);
513  girara_setting_set_value(session, setting, &i);
514  break;
515  }
516  case STRING:
517  girara_setting_set_value(session, setting, value);
518  break;
519  default:
520  return false;
521  }
522  }
523 
524  return true;
525 }
526 
527 bool
528 girara_inputbar_command_add(girara_session_t* session, const char* command,
529  const char* abbreviation, girara_command_function_t function,
530  girara_completion_function_t completion, const char* description)
531 {
532  g_return_val_if_fail(session != NULL, false);
533  g_return_val_if_fail(command != NULL, false);
534  g_return_val_if_fail(function != NULL, false);
535 
536  /* search for existing binding */
537  GIRARA_LIST_FOREACH(session->bindings.commands, girara_command_t*, iter, commands_it)
538  if (g_strcmp0(commands_it->command, command) == 0) {
539  g_free(commands_it->abbr);
540  g_free(commands_it->description);
541 
542  commands_it->abbr = abbreviation ? g_strdup(abbreviation) : NULL;
543  commands_it->function = function;
544  commands_it->completion = completion;
545  commands_it->description = description ? g_strdup(description) : NULL;
546 
548  return true;
549  }
550  GIRARA_LIST_FOREACH_END(session->bindings.commands, girara_command_t*, iter, commands_it);
551 
552  /* add new inputbar command */
553  girara_command_t* new_command = g_slice_new(girara_command_t);
554 
555  new_command->command = g_strdup(command);
556  new_command->abbr = abbreviation ? g_strdup(abbreviation) : NULL;
557  new_command->function = function;
558  new_command->completion = completion;
559  new_command->description = description ? g_strdup(description) : NULL;
560  girara_list_append(session->bindings.commands, new_command);
561 
562  return true;
563 }
564 
565 bool
566 girara_special_command_add(girara_session_t* session, char identifier, girara_inputbar_special_function_t function, bool always, int argument_n, void* argument_data)
567 {
568  g_return_val_if_fail(session != NULL, false);
569  g_return_val_if_fail(function != NULL, false);
570 
571  girara_argument_t argument = {argument_n, argument_data};
572 
573  /* search for existing special command */
574  GIRARA_LIST_FOREACH(session->bindings.special_commands, girara_special_command_t*, iter, scommand_it)
575  if (scommand_it->identifier == identifier) {
576  scommand_it->function = function;
577  scommand_it->always = always;
578  scommand_it->argument = argument;
580  return true;
581  }
582  GIRARA_LIST_FOREACH_END(session->bindings.special_commands, girara_special_command_t*, iter, scommand_it);
583 
584  /* create new special command */
585  girara_special_command_t* special_command = g_slice_new(girara_special_command_t);
586 
587  special_command->identifier = identifier;
588  special_command->function = function;
589  special_command->always = always;
590  special_command->argument = argument;
591 
592  girara_list_append(session->bindings.special_commands, special_command);
593 
594  return true;
595 }
596 
597 void
598 girara_special_command_free(girara_special_command_t* special_command)
599 {
600  if (special_command == NULL) {
601  return;
602  }
603  g_slice_free(girara_special_command_t, special_command);
604 }
605 
606 void
607 girara_command_free(girara_command_t* command)
608 {
609  if (command == NULL) {
610  return;
611  }
612 
613  g_free(command->command);
614  g_free(command->abbr);
615  g_free(command->description);
616  g_slice_free(girara_command_t, command);
617 }
618 
619 bool
620 girara_cmd_exec(girara_session_t* session, girara_list_t* argument_list)
621 {
622  if (session == NULL || argument_list == NULL) {
623  return true;
624  }
625 
626  return girara_exec_with_argument_list(session, argument_list);
627 }