shortcuts.c 21.9 KB
Newer Older
1
/* SPDX-License-Identifier: Zlib */
2 3

#include "shortcuts.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
4

5
#include "datastructures.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
6
#include "input-history.h"
7 8 9 10 11
#include "internal.h"
#include "session.h"
#include "settings.h"

#include <gtk/gtk.h>
Sebastian Ramacher's avatar
Sebastian Ramacher committed
12
#include <string.h>
13 14 15 16

static void girara_toggle_widget_visibility(GtkWidget* widget);

bool
Sebastian Ramacher's avatar
Sebastian Ramacher committed
17
girara_shortcut_add(girara_session_t* session, guint modifier, guint key, const char* buffer, girara_shortcut_function_t function, girara_mode_t mode, int argument_n, void* argument_data)
18
{
Moritz Lipp's avatar
Moritz Lipp committed
19 20 21
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(buffer || key || modifier, false);
  g_return_val_if_fail(function != NULL, false);
22

Moritz Lipp's avatar
Moritz Lipp committed
23 24
  girara_argument_t argument = {argument_n, (argument_data != NULL) ?
    g_strdup(argument_data) : NULL};
25 26

  /* search for existing binding */
Moritz Lipp's avatar
Moritz Lipp committed
27
  bool found_existing_shortcut = false;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
28
  GIRARA_LIST_FOREACH_BODY_WITH_ITER(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it,
29
    if (((shortcuts_it->mask == modifier && shortcuts_it->key == key && (modifier != 0 || key != 0)) ||
Sebastian Ramacher's avatar
Sebastian Ramacher committed
30
       (buffer && shortcuts_it->buffered_command && !g_strcmp0(shortcuts_it->buffered_command, buffer)))
Moritz Lipp's avatar
Moritz Lipp committed
31
        && ((shortcuts_it->mode == mode) || (mode == 0)))
32
    {
Moritz Lipp's avatar
Moritz Lipp committed
33 34 35 36
      if (shortcuts_it->argument.data != NULL) {
        g_free(shortcuts_it->argument.data);
      }

Moritz Lipp's avatar
Moritz Lipp committed
37 38 39 40 41 42 43 44
      shortcuts_it->function  = function;
      shortcuts_it->argument  = argument;
      found_existing_shortcut = true;

      if (mode != 0) {
        girara_list_iterator_free(iter);
        return true;
      }
45
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
46
  );
47

Moritz Lipp's avatar
Moritz Lipp committed
48 49 50 51
  if (found_existing_shortcut == true) {
    return true;
  }

52 53 54 55 56
  /* add new shortcut */
  girara_shortcut_t* shortcut = g_slice_new(girara_shortcut_t);

  shortcut->mask             = modifier;
  shortcut->key              = key;
57
  shortcut->buffered_command = g_strdup(buffer);
58 59 60 61 62
  shortcut->function         = function;
  shortcut->mode             = mode;
  shortcut->argument         = argument;
  girara_list_append(session->bindings.shortcuts, shortcut);

Moritz Lipp's avatar
Moritz Lipp committed
63
  return true;
64 65
}

66 67 68
bool
girara_shortcut_remove(girara_session_t* session, guint modifier, guint key, const char* buffer, girara_mode_t mode)
{
Moritz Lipp's avatar
Moritz Lipp committed
69 70
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(buffer || key || modifier, false);
71

72
  bool handled = false;
73
  /* search for existing binding */
74
  GIRARA_LIST_FOREACH_BODY(session->bindings.shortcuts, girara_shortcut_t*, shortcuts_it,
75
    if (((shortcuts_it->mask == modifier && shortcuts_it->key == key && (modifier != 0 || key != 0)) ||
Sebastian Ramacher's avatar
Sebastian Ramacher committed
76
       (buffer && shortcuts_it->buffered_command && !g_strcmp0(shortcuts_it->buffered_command, buffer)))
77
        && shortcuts_it->mode == mode) {
78
      girara_list_remove(session->bindings.shortcuts, shortcuts_it);
79 80
      handled = true;
      break;
81
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
82
  );
83

84
  return handled;
85 86
}

87 88 89
void
girara_shortcut_free(girara_shortcut_t* shortcut)
{
Moritz Lipp's avatar
Moritz Lipp committed
90
  g_return_if_fail(shortcut != NULL);
91
  g_free(shortcut->buffered_command);
Moritz Lipp's avatar
Moritz Lipp committed
92
  g_free(shortcut->argument.data);
93 94 95 96 97 98 99 100 101 102
  g_slice_free(girara_shortcut_t, shortcut);
}

bool
girara_inputbar_shortcut_add(girara_session_t* session, guint modifier, guint key, girara_shortcut_function_t function, int argument_n, void* argument_data)
{
  g_return_val_if_fail(session  != NULL, false);
  g_return_val_if_fail(function != NULL, false);

  girara_argument_t argument = {argument_n, argument_data};
103
  bool found = false;
104 105

  /* search for existing special command */
106
  GIRARA_LIST_FOREACH_BODY(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, inp_sh_it,
107 108 109 110
    if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
      inp_sh_it->function = function;
      inp_sh_it->argument = argument;

111 112
      found = true;
      break;
113
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
114
  );
115

116 117 118
  if (found == false) {
    /* create new inputbar shortcut */
    girara_inputbar_shortcut_t* inputbar_shortcut = g_slice_new(girara_inputbar_shortcut_t);
119

120 121 122 123 124 125 126
    inputbar_shortcut->mask     = modifier;
    inputbar_shortcut->key      = key;
    inputbar_shortcut->function = function;
    inputbar_shortcut->argument = argument;

    girara_list_append(session->bindings.inputbar_shortcuts, inputbar_shortcut);
  }
127

Moritz Lipp's avatar
Moritz Lipp committed
128
  return true;
129 130
}

131 132 133 134 135 136
bool
girara_inputbar_shortcut_remove(girara_session_t* session, guint modifier, guint key)
{
  g_return_val_if_fail(session  != NULL, false);

  /* search for existing special command */
137
  GIRARA_LIST_FOREACH_BODY(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, inp_sh_it,
138 139
    if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
      girara_list_remove(session->bindings.inputbar_shortcuts, inp_sh_it);
140
      break;
141
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
142
  );
143

Moritz Lipp's avatar
Moritz Lipp committed
144
  return true;
145 146
}

147 148 149 150 151
void
girara_inputbar_shortcut_free(girara_inputbar_shortcut_t* inputbar_shortcut)
{
  g_slice_free(girara_inputbar_shortcut_t, inputbar_shortcut);
}
Moritz Lipp's avatar
Moritz Lipp committed
152

153 154 155 156
bool
girara_isc_activate(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
    girara_callback_inputbar_activate(session->gtk.inputbar_entry, session);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
157
    return true;
158 159
}

160
bool
161
girara_isc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
162
{
Moritz Lipp's avatar
Moritz Lipp committed
163 164
  g_return_val_if_fail(session != NULL, false);

165 166
  /* hide completion */
  girara_argument_t arg = { GIRARA_HIDE, NULL };
167
  girara_isc_completion(session, &arg, NULL, 0);
168 169

  /* clear inputbar */
Moritz Lipp's avatar
Moritz Lipp committed
170
  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
171 172 173 174 175

  /* grab view */
  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.view));

  /* hide inputbar */
Moritz Lipp's avatar
Moritz Lipp committed
176
  gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar_dialog));
Moritz Lipp's avatar
Moritz Lipp committed
177 178 179
  if (session->global.autohide_inputbar == true) {
    gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
  }
180

181
  /* Begin from the last command when navigating through history */
182
  girara_input_history_reset(session->command_history);
183

184 185 186
  /* reset custom functions */
  session->signals.inputbar_custom_activate        = NULL;
  session->signals.inputbar_custom_key_press_event = NULL;
187
  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
188

189 190 191 192
  return true;
}

bool
193
girara_isc_string_manipulation(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
194
{
Moritz Lipp's avatar
Moritz Lipp committed
195 196
  g_return_val_if_fail(session != NULL, false);

197 198
  gchar *separator = NULL;
  girara_setting_get(session, "word-separator", &separator);
Moritz Lipp's avatar
Moritz Lipp committed
199
  gchar *input  = gtk_editable_get_chars(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
200
  int    length = strlen(input);
Moritz Lipp's avatar
Moritz Lipp committed
201
  int pos       = gtk_editable_get_position(GTK_EDITABLE(session->gtk.inputbar_entry));
202 203 204 205
  int i;

  switch (argument->n) {
    case GIRARA_DELETE_LAST_WORD:
206
      if (pos == 1 && (input[0] == ':' || input[0] == '/')) {
207
        break;
208
      }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
209
      if (pos == 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
210
        break;
211 212 213
      }

      i = pos - 1;
214 215

      /* remove trailing spaces */
216 217
      for (; i >= 0 && input[i] == ' '; i--) {
      }
218 219

      /* find the beginning of the word */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
220
      while ((i == (pos - 1)) || ((i > 0) && separator != NULL && !strchr(separator, input[i]))) {
221 222 223
        i--;
      }

Moritz Lipp's avatar
Moritz Lipp committed
224 225
      gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry),  i + 1, pos);
      gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), i + 1);
226 227
      break;
    case GIRARA_DELETE_LAST_CHAR:
228
      if (length != 1 && pos == 1 && (input[0] == ':' || input[0] == '/')) {
229 230
        break;
      }
231
      if (length == 1 && pos == 1) {
232
        girara_isc_abort(session, argument, NULL, 0);
233
      }
Moritz Lipp's avatar
Moritz Lipp committed
234
      gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos - 1, pos);
235 236
      break;
    case GIRARA_DELETE_TO_LINE_START:
Moritz Lipp's avatar
Moritz Lipp committed
237
      gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), 1, pos);
238 239
      break;
    case GIRARA_NEXT_CHAR:
Moritz Lipp's avatar
Moritz Lipp committed
240
      gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), pos + 1);
241 242
      break;
    case GIRARA_PREVIOUS_CHAR:
243
      gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), (pos == 1) ? 1 : pos - 1);
244
      break;
245
    case GIRARA_DELETE_CURR_CHAR:
246
      if (length != 1 && pos == 0 && (input[0] == ':' || input[0] == '/')){
Sebastian Ramacher's avatar
Sebastian Ramacher committed
247
        break;
248
      }
249
      if(length == 1 && pos == 0) {
250
        girara_isc_abort(session, argument, NULL, 0);
251
      }
Moritz Lipp's avatar
Moritz Lipp committed
252
      gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos, pos + 1);
253 254
      break;
    case GIRARA_DELETE_TO_LINE_END:
Moritz Lipp's avatar
Moritz Lipp committed
255
      gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos, length);
256 257
      break;
    case GIRARA_GOTO_START:
Moritz Lipp's avatar
Moritz Lipp committed
258
      gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), 1);
259 260
      break;
    case GIRARA_GOTO_END:
Moritz Lipp's avatar
Moritz Lipp committed
261
      gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
262
      break;
263 264 265 266 267 268 269 270
  }

  g_free(separator);
  g_free(input);

  return false;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
271 272
bool
girara_isc_command_history(girara_session_t* session, girara_argument_t*
Moritz Lipp's avatar
Moritz Lipp committed
273 274
    argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
275
  g_return_val_if_fail(session != NULL, false);
276

277
  char* temp = gtk_editable_get_chars(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
278
  const char* command = argument->n == GIRARA_NEXT ?
279 280
    girara_input_history_next(session->command_history, temp) :
    girara_input_history_previous(session->command_history, temp);
Marwan Tanager's avatar
Marwan Tanager committed
281 282
  g_free(temp);

283 284 285 286
  if (command != NULL) {
    gtk_entry_set_text(session->gtk.inputbar_entry, command);
    gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));
    gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
287 288
  }

Moritz Lipp's avatar
Moritz Lipp committed
289 290 291
  return true;
}

292 293
/* default shortcut implementation */
bool
294
girara_sc_focus_inputbar(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
295 296
{
  g_return_val_if_fail(session != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
297
  g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
298

Moritz Lipp's avatar
Moritz Lipp committed
299
  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
Moritz Lipp's avatar
Moritz Lipp committed
300
    gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
301 302
  }

Moritz Lipp's avatar
Moritz Lipp committed
303
  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
304 305 306
    gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
  }

Moritz Lipp's avatar
Moritz Lipp committed
307 308 309
  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));

  if (argument != NULL && argument->data != NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
310
    gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);
311 312 313 314

    /* we save the X clipboard that will be clear by "grab_focus" */
    gchar* x_clipboard_text = gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY));

Moritz Lipp's avatar
Moritz Lipp committed
315
    gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
316 317 318 319 320 321 322 323 324 325 326 327

    if (x_clipboard_text != NULL) {
      /* we reset the X clipboard with saved text */
      gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), x_clipboard_text, -1);
      g_free(x_clipboard_text);
    }
  }

  return true;
}

bool
328
girara_sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
329 330 331
{
  g_return_val_if_fail(session != NULL, false);

332
  girara_isc_abort(session, NULL, NULL, 0);
Moritz Lipp's avatar
Moritz Lipp committed
333

334 335
  gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));

Moritz Lipp's avatar
Moritz Lipp committed
336 337 338 339
  if (session->global.autohide_inputbar == false) {
    gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
  }

340 341 342 343
  return false;
}

bool
344
girara_sc_quit(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
345 346 347 348
{
  g_return_val_if_fail(session != NULL, false);

  girara_argument_t arg = { GIRARA_HIDE, NULL };
349
  girara_isc_completion(session, &arg, NULL, 0);
350 351 352 353 354 355 356 357 358 359 360 361 362

  gtk_main_quit();

  return false;
}

static void
girara_toggle_widget_visibility(GtkWidget* widget)
{
  if (widget == NULL) {
    return;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
363
  if (gtk_widget_get_visible(widget) == TRUE) {
364 365 366 367 368 369 370
    gtk_widget_hide(widget);
  } else {
    gtk_widget_show(widget);
  }
}

bool
371
girara_sc_toggle_inputbar(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
372 373 374 375 376 377 378 379 380
{
  g_return_val_if_fail(session != NULL, false);

  girara_toggle_widget_visibility(GTK_WIDGET(session->gtk.inputbar));

  return true;
}

bool
381
girara_sc_toggle_statusbar(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
382 383 384 385 386 387 388 389
{
  g_return_val_if_fail(session != NULL, false);

  girara_toggle_widget_visibility(GTK_WIDGET(session->gtk.statusbar));

  return true;
}

390 391
static girara_list_t*
argument_to_argument_list(girara_argument_t* argument) {
392 393
  girara_list_t* argument_list = girara_list_new();
  if (argument_list == NULL) {
394
    return NULL;
395 396 397 398 399 400 401
  }

  gchar** argv = NULL;
  gint argc    = 0;

  girara_list_set_free_function(argument_list, g_free);
  if (g_shell_parse_argv((const gchar*) argument->data, &argc, &argv, NULL) != FALSE) {
402
    for (int i = 0; i < argc; i++) {
403 404
      char* arg = g_strdup(argv[i]);
      girara_list_append(argument_list, arg);
405
    }
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425

    return argument_list;
  }

  girara_list_free(argument_list);
  return NULL;
}

bool
girara_sc_set(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);

  if (argument == NULL || argument->data == NULL) {
    return false;
  }

  /* create argument list */
  girara_list_t* argument_list = argument_to_argument_list(argument);
  if (argument_list == NULL) {
426 427 428 429 430 431 432 433 434 435 436 437
    return false;
  }

  /* call set */
  girara_cmd_set(session, argument_list);

  /* cleanup */
  girara_list_free(argument_list);

  return false;
}

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
bool
girara_sc_exec(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);

  if (argument == NULL || argument->data == NULL) {
    return false;
  }

  /* create argument list */
  girara_list_t* argument_list = argument_to_argument_list(argument);
  if (argument_list == NULL) {
    return false;
  }

  /* call exec */
  girara_cmd_exec(session, argument_list);

  /* cleanup */
  girara_list_free(argument_list);

  return false;
}

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
static bool
simulate_key_press(girara_session_t* session, int state, int key)
{
  if (session == NULL || session->gtk.box == NULL) {
    return false;
  }

  GdkEvent* event = gdk_event_new(GDK_KEY_PRESS);

  event->any.type       = GDK_KEY_PRESS;
  event->key.window     = g_object_ref(gtk_widget_get_parent_window(GTK_WIDGET(session->gtk.box)));
  event->key.send_event = false;
  event->key.time       = GDK_CURRENT_TIME;
  event->key.state      = state;
  event->key.keyval     = key;

  GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(session->gtk.box));
  GdkKeymapKey* keys  = NULL;
  gint number_of_keys = 0;

  if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_for_display(display),
        event->key.keyval, &keys, &number_of_keys) == FALSE) {
    gdk_event_free(event);
    return false;
  }

  event->key.hardware_keycode = keys[0].keycode;
  event->key.group            = keys[0].group;

  g_free(keys);

  gdk_event_put(event);
  gdk_event_free(event);

  gtk_main_iteration_do(FALSE);

  return true;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
501 502
bool
girara_sc_feedkeys(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
    girara_event_t* UNUSED(event), unsigned int t)
{
  if (session == NULL || argument == NULL) {
    return false;
  }

  typedef struct gdk_keyboard_button_s {
    char* identifier;
    int keyval;
  } gdk_keyboard_button_t;

  static const gdk_keyboard_button_t gdk_keyboard_buttons[] = {
    {"BackSpace", GDK_KEY_BackSpace},
    {"CapsLock",  GDK_KEY_Caps_Lock},
    {"Down",      GDK_KEY_Down},
    {"Esc",       GDK_KEY_Escape},
    {"F10",       GDK_KEY_F10},
    {"F11",       GDK_KEY_F11},
    {"F12",       GDK_KEY_F12},
    {"F1",        GDK_KEY_F1},
    {"F2",        GDK_KEY_F2},
    {"F3",        GDK_KEY_F3},
    {"F4",        GDK_KEY_F4},
    {"F5",        GDK_KEY_F5},
    {"F6",        GDK_KEY_F6},
    {"F7",        GDK_KEY_F7},
    {"F8",        GDK_KEY_F8},
    {"F9",        GDK_KEY_F9},
    {"Left",      GDK_KEY_Left},
    {"PageDown",  GDK_KEY_Page_Down},
    {"PageUp",    GDK_KEY_Page_Up},
Sebastian Ramacher's avatar
Sebastian Ramacher committed
534 535
    {"Home",      GDK_KEY_Home},
    {"End",       GDK_KEY_End},
Moritz Lipp's avatar
Moritz Lipp committed
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
    {"Return",    GDK_KEY_Return},
    {"Right",     GDK_KEY_Right},
    {"Space",     GDK_KEY_space},
    {"Super",     GDK_KEY_Super_L},
    {"Tab",       GDK_KEY_Tab},
    {"ShiftTab",  GDK_KEY_ISO_Left_Tab},
    {"Up",        GDK_KEY_Up}
  };

  char* input               = (char*) argument->data;
  unsigned int input_length = strlen(input);

  t = (t == 0) ? 1 : t;
  for (unsigned int c = 0; c < t; c++) {
    for (unsigned i = 0; i < input_length; i++) {
      int state  = 0;
      int keyval = input[i];

      /* possible special button */
      if ((input_length - i) >= 3 && input[i] == '<') {
        char* end = strchr(input + i, '>');
        if (end == NULL) {
          goto single_key;
        }

        int length = end - (input + i) - 1;
        char* tmp  = g_strndup(input + i + 1, length);
        bool found = false;

        /* Multi key shortcut */
        if (length > 2 && tmp[1] == '-') {
          switch (tmp[0]) {
            case 'S':
              state = GDK_SHIFT_MASK;
              break;
            case 'A':
              state = GDK_MOD1_MASK;
              break;
            case 'C':
              state = GDK_CONTROL_MASK;
              break;
            default:
              break;
          }

          if (length == 3) {
            keyval = tmp[2];
            found  = true;
          } else {
585 586 587
            for (unsigned int j = 0; j < LENGTH(gdk_keyboard_buttons); ++j) {
              if (g_strcmp0(tmp + 2, gdk_keyboard_buttons[j].identifier) == 0) {
                keyval = gdk_keyboard_buttons[j].keyval;
Moritz Lipp's avatar
Moritz Lipp committed
588 589 590 591 592 593 594
                found = true;
                break;
              }
            }
          }
        /* Possible special key */
        } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
595
          for (unsigned int j = 0; j < LENGTH(gdk_keyboard_buttons); ++j) {
596 597
            if (g_strcmp0(tmp, gdk_keyboard_buttons[j].identifier) == 0) {
              keyval = gdk_keyboard_buttons[j].keyval;
Moritz Lipp's avatar
Moritz Lipp committed
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
              found = true;
              break;
            }
          }
        }

        g_free(tmp);

        /* parsed special key */
        if (found == true) {
          i += length + 1;
        }
      }

single_key:

614
      update_state_by_keyval(&state, keyval);
Moritz Lipp's avatar
Moritz Lipp committed
615 616 617 618 619 620 621
      simulate_key_press(session, state, keyval);
    }
  }

  return true;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
622 623
bool
girara_shortcut_mapping_add(girara_session_t* session, const char* identifier, girara_shortcut_function_t function)
624
{
Moritz Lipp's avatar
Moritz Lipp committed
625
  g_return_val_if_fail(session  != NULL, false);
626 627 628 629 630

  if (function == NULL || identifier == NULL) {
    return false;
  }

631
  girara_session_private_t* session_private = session->private_data;
632 633 634
  bool found = false;

  GIRARA_LIST_FOREACH_BODY(session_private->config.shortcut_mappings, girara_shortcut_mapping_t*, data,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
635
    if (g_strcmp0(data->identifier, identifier) == 0) {
636
      data->function = function;
637 638
      found = true;
      break;
639
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
640
  );
641

642 643 644
  if (found == false) {
    /* add new config handle */
    girara_shortcut_mapping_t* mapping = g_slice_new(girara_shortcut_mapping_t);
645

646 647 648 649
    mapping->identifier = g_strdup(identifier);
    mapping->function   = function;
    girara_list_append(session_private->config.shortcut_mappings, mapping);
  }
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664

  return true;
}

void
girara_shortcut_mapping_free(girara_shortcut_mapping_t* mapping)
{
  if (mapping == NULL) {
    return;
  }

  g_free(mapping->identifier);
  g_slice_free(girara_shortcut_mapping_t, mapping);
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
665 666
bool
girara_argument_mapping_add(girara_session_t* session, const char* identifier, int value)
667 668 669 670 671 672 673
{
  g_return_val_if_fail(session  != NULL, false);

  if (identifier == NULL) {
    return false;
  }

674
  girara_session_private_t* session_private = session->private_data;
675 676 677
  bool found = false;

  GIRARA_LIST_FOREACH_BODY(session_private->config.argument_mappings, girara_argument_mapping_t*, mapping,
678 679
    if (g_strcmp0(mapping->identifier, identifier) == 0) {
      mapping->value = value;
680 681
      found = true;
      break;
682
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
683
  );
684

685 686 687
  if (found == false) {
    /* add new config handle */
    girara_argument_mapping_t* mapping = g_slice_new(girara_argument_mapping_t);
688

689 690 691 692
    mapping->identifier = g_strdup(identifier);
    mapping->value      = value;
    girara_list_append(session_private->config.argument_mappings, mapping);
  }
693 694 695 696 697 698 699 700 701 702

  return true;
}

void
girara_argument_mapping_free(girara_argument_mapping_t* argument_mapping)
{
  if (argument_mapping == NULL) {
    return;
  }
703

704 705 706 707 708
  g_free(argument_mapping->identifier);
  g_slice_free(girara_argument_mapping_t, argument_mapping);
}

bool
709 710 711
girara_mouse_event_add(girara_session_t* session, guint mask, guint button,
    girara_shortcut_function_t function, girara_mode_t mode, girara_event_type_t
    event_type, int argument_n, void* argument_data)
712 713 714 715 716
{
  g_return_val_if_fail(session  != NULL, false);
  g_return_val_if_fail(function != NULL, false);

  girara_argument_t argument = {argument_n, argument_data};
717
  bool found = false;
718 719

  /* search for existing binding */
720
  GIRARA_LIST_FOREACH_BODY(session->bindings.mouse_events, girara_mouse_event_t*, me_it,
721
    if (me_it->mask == mask && me_it->button == button &&
722
       me_it->mode == mode && me_it->event_type == event_type) {
723 724
      me_it->function = function;
      me_it->argument = argument;
725 726 727

      found = true;
      break;
728
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
729
  );
730

731 732 733 734 735 736 737 738 739 740 741 742
  if (found == false) {
    /* add new mouse event */
    girara_mouse_event_t* mouse_event = g_slice_new(girara_mouse_event_t);

    mouse_event->mask       = mask;
    mouse_event->button     = button;
    mouse_event->function   = function;
    mouse_event->mode       = mode;
    mouse_event->event_type = event_type;
    mouse_event->argument   = argument;
    girara_list_append(session->bindings.mouse_events, mouse_event);
  }
743 744 745 746

  return true;
}

747 748 749 750 751
bool
girara_mouse_event_remove(girara_session_t* session, guint mask, guint button, girara_mode_t mode)
{
  g_return_val_if_fail(session  != NULL, false);

752
  bool found = false;
753
  /* search for existing binding */
754
  GIRARA_LIST_FOREACH_BODY(session->bindings.mouse_events, girara_mouse_event_t*, me_it,
755
    if (me_it->mask == mask && me_it->button == button &&
756
       me_it->mode == mode) {
Moritz Lipp's avatar
Moritz Lipp committed
757
      girara_list_remove(session->bindings.mouse_events, me_it);
758 759
      found = true;
      break;
760
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
761
  );
762

763
  return found;
764 765
}

766 767 768 769 770 771 772 773
void
girara_mouse_event_free(girara_mouse_event_t* mouse_event)
{
  if (mouse_event == NULL) {
    return;
  }
  g_slice_free(girara_mouse_event_t, mouse_event);
}