shortcuts.c 21.9 KB
Newer Older
1
/* SPDX-License-Identifier: Zlib */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
2 3

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
5
#include "datastructures.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
6
#include "input-history.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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>
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
22

Moritz Lipp's avatar
Moritz Lipp committed
23 24
  girara_argument_t argument = {argument_n, (argument_data != NULL) ?
    g_strdup(argument_data) : NULL};
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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)))
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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;
      }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
45
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
46
  );
Sebastian Ramacher's avatar
Sebastian Ramacher committed
47

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
104 105

  /* search for existing special command */
106
  GIRARA_LIST_FOREACH_BODY(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, inp_sh_it,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
113
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
114
  );
Sebastian Ramacher's avatar
Sebastian Ramacher committed
115

116 117 118
  if (found == false) {
    /* create new inputbar shortcut */
    girara_inputbar_shortcut_t* inputbar_shortcut = g_slice_new(girara_inputbar_shortcut_t);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
127

Moritz Lipp's avatar
Moritz Lipp committed
128
  return true;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
}

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

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

  /* clear inputbar */
Moritz Lipp's avatar
Moritz Lipp committed
170
  gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), 0, -1);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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));
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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;
Moritz Lipp's avatar
Moritz Lipp committed
187
  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
188

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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))
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
202 203 204 205
  int i;

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

      i = pos - 1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
214 215

      /* remove trailing spaces */
216 217
      for (; i >= 0 && input[i] == ' '; i--) {
      }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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]))) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
233
      }
Moritz Lipp's avatar
Moritz Lipp committed
234
      gtk_editable_delete_text(GTK_EDITABLE(session->gtk.inputbar_entry), pos - 1, pos);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
241 242
      break;
    case GIRARA_PREVIOUS_CHAR:
243
      gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), (pos == 1) ? 1 : pos - 1);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
244
      break;
245
    case GIRARA_DELETE_CURR_CHAR:
246
      if (length != 1 && pos == 0 && (input[0] == ':' || input[0] == '/')){
Sebastian Ramacher's avatar
CS  
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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
263 264 265 266 267 268 269 270
  }

  g_free(separator);
  g_free(input);

  return false;
}

Sebastian Ramacher's avatar
CS  
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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))
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
301 302
  }

Moritz Lipp's avatar
Moritz Lipp committed
303
  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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))
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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));
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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))
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
CS  
Sebastian Ramacher committed
363
  if (gtk_widget_get_visible(widget) == TRUE) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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))
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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))
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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++) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
CS  
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 {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
CS  
Sebastian Ramacher committed
622 623
bool
girara_shortcut_mapping_add(girara_session_t* session, const char* identifier, girara_shortcut_function_t function)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
624
{
Moritz Lipp's avatar
Moritz Lipp committed
625
  g_return_val_if_fail(session  != NULL, false);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
636
      data->function = function;
637 638
      found = true;
      break;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
639
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
640
  );
Sebastian Ramacher's avatar
Sebastian Ramacher committed
641

642 643 644
  if (found == false) {
    /* add new config handle */
    girara_shortcut_mapping_t* mapping = g_slice_new(girara_shortcut_mapping_t);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
645

646 647 648 649
    mapping->identifier = g_strdup(identifier);
    mapping->function   = function;
    girara_list_append(session_private->config.shortcut_mappings, mapping);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
CS  
Sebastian Ramacher committed
665 666
bool
girara_argument_mapping_add(girara_session_t* session, const char* identifier, int value)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
678 679
    if (g_strcmp0(mapping->identifier, identifier) == 0) {
      mapping->value = value;
680 681
      found = true;
      break;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
682
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
683
  );
Sebastian Ramacher's avatar
Sebastian Ramacher committed
684

685 686 687
  if (found == false) {
    /* add new config handle */
    girara_argument_mapping_t* mapping = g_slice_new(girara_argument_mapping_t);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
688

689 690 691 692
    mapping->identifier = g_strdup(identifier);
    mapping->value      = value;
    girara_list_append(session_private->config.argument_mappings, mapping);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
704 705 706 707 708
  g_free(argument_mapping->identifier);
  g_slice_free(girara_argument_mapping_t, argument_mapping);
}

bool
Moritz Lipp's avatar
Moritz Lipp committed
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)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
718 719

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

      found = true;
      break;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
728
    }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
729
  );
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
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
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
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);
}