shortcuts.c 23.6 KB
Newer Older
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1 2 3 4 5 6 7 8
/* See LICENSE file for license and copyright information */

#include "shortcuts.h"
#include "datastructures.h"
#include "internal.h"
#include "session.h"
#include "settings.h"
#include "tabs.h"
9
#include "input-history.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
10 11 12 13 14

#include <string.h>
#include <gtk/gtk.h>

static void girara_toggle_widget_visibility(GtkWidget* widget);
Moritz Lipp's avatar
Moritz Lipp committed
15
static bool simulate_key_press(girara_session_t* session, int state, int key);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
16 17

bool
Sebastian Ramacher's avatar
Sebastian Ramacher committed
18
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
19
{
Moritz Lipp's avatar
Moritz Lipp committed
20 21 22
  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
23

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

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

Moritz Lipp's avatar
Moritz Lipp committed
38 39 40 41 42 43 44 45
      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
46 47 48
    }
  GIRARA_LIST_FOREACH_END(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it);

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
53 54 55 56 57
  /* add new shortcut */
  girara_shortcut_t* shortcut = g_slice_new(girara_shortcut_t);

  shortcut->mask             = modifier;
  shortcut->key              = key;
58
  shortcut->buffered_command = g_strdup(buffer);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
59 60 61 62 63
  shortcut->function         = function;
  shortcut->mode             = mode;
  shortcut->argument         = argument;
  girara_list_append(session->bindings.shortcuts, shortcut);

Moritz Lipp's avatar
Moritz Lipp committed
64
  return true;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
65 66
}

67 68 69
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
70 71
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(buffer || key || modifier, false);
72 73 74 75 76 77 78 79 80

  /* search for existing binding */
  GIRARA_LIST_FOREACH(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it)
    if (((shortcuts_it->mask == modifier && shortcuts_it->key == key && (modifier != 0 || key != 0)) ||
       (buffer && shortcuts_it->buffered_command && !strcmp(shortcuts_it->buffered_command, buffer)))
        && shortcuts_it->mode == mode)
    {
      girara_list_remove(session->bindings.shortcuts, shortcuts_it);
      girara_list_iterator_free(iter);
Moritz Lipp's avatar
Moritz Lipp committed
81
      return true;
82 83 84
    }
  GIRARA_LIST_FOREACH_END(session->bindings.shortcuts, girara_shortcut_t*, iter, shortcuts_it);

Moritz Lipp's avatar
Moritz Lipp committed
85
  return false;
86 87
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
88 89 90
void
girara_shortcut_free(girara_shortcut_t* shortcut)
{
Moritz Lipp's avatar
Moritz Lipp committed
91
  g_return_if_fail(shortcut != NULL);
92
  g_free(shortcut->buffered_command);
Moritz Lipp's avatar
Moritz Lipp committed
93
  g_free(shortcut->argument.data);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
  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};

  /* search for existing special command */
  GIRARA_LIST_FOREACH(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it)
    if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
      inp_sh_it->function = function;
      inp_sh_it->argument = argument;

      girara_list_iterator_free(iter);
Moritz Lipp's avatar
Moritz Lipp committed
112
      return true;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
113 114 115 116 117 118 119 120 121 122 123 124
    }
  GIRARA_LIST_FOREACH_END(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it);

  /* create new inputbar shortcut */
  girara_inputbar_shortcut_t* inputbar_shortcut = g_slice_new(girara_inputbar_shortcut_t);

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

128 129 130 131 132 133 134 135 136 137
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 */
  GIRARA_LIST_FOREACH(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it)
    if (inp_sh_it->mask == modifier && inp_sh_it->key == key) {
      girara_list_remove(session->bindings.inputbar_shortcuts, inp_sh_it);
      girara_list_iterator_free(iter);
Moritz Lipp's avatar
Moritz Lipp committed
138
      return true;
139 140 141
    }
  GIRARA_LIST_FOREACH_END(session->bindings.inputbar_shortcuts, girara_inputbar_shortcut_t*, iter, inp_sh_it);

Moritz Lipp's avatar
Moritz Lipp committed
142
  return true;
143 144
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
145 146 147 148 149
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
150

151 152 153 154
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
155
    return true;
156 157
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
158
bool
159
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
160
{
Moritz Lipp's avatar
Moritz Lipp committed
161 162
  g_return_val_if_fail(session != NULL, false);

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

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

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

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

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

182 183 184
  /* reset custom functions */
  session->signals.inputbar_custom_activate        = NULL;
  session->signals.inputbar_custom_key_press_event = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
185
  gtk_entry_set_visibility(session->gtk.inputbar_entry, TRUE);
186

Sebastian Ramacher's avatar
Sebastian Ramacher committed
187 188 189 190
  return true;
}

bool
191
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
192
{
Moritz Lipp's avatar
Moritz Lipp committed
193 194
  g_return_val_if_fail(session != NULL, false);

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

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

      i = pos - 1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
212 213 214 215 216 217 218 219 220

      /* remove trailing spaces */
      for (; i >= 0 && input[i] == ' '; i--);

      /* find the beginning of the word */
      while ((i == (pos - 1)) || ((i > 0) && !strchr(separator, input[i]))) {
        i--;
      }

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

  g_free(separator);
  g_free(input);

  return false;
}

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

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

280 281 282 283
  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);
284 285
  }

Moritz Lipp's avatar
Moritz Lipp committed
286 287 288
  return true;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
289 290
/* default shortcut implementation */
bool
291
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
292 293
{
  g_return_val_if_fail(session != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
294
  g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
295

Moritz Lipp's avatar
Moritz Lipp committed
296
  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
Moritz Lipp's avatar
Moritz Lipp committed
297
    gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
Sebastian Ramacher's avatar
Sebastian Ramacher committed
298 299
  }

Moritz Lipp's avatar
Moritz Lipp committed
300
  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
301 302 303
    gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
  }

Moritz Lipp's avatar
Moritz Lipp committed
304 305 306
  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));

  if (argument != NULL && argument->data != NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
307
    gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
308 309 310 311

    /* 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
312
    gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
313 314 315 316 317 318 319 320 321 322 323 324

    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
325
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
326 327 328
{
  g_return_val_if_fail(session != NULL, false);

329
  girara_isc_abort(session, NULL, NULL, 0);
Moritz Lipp's avatar
Moritz Lipp committed
330

Sebastian Ramacher's avatar
Sebastian Ramacher committed
331 332
  gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
337 338 339 340
  return false;
}

bool
341
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
342 343 344 345
{
  g_return_val_if_fail(session != NULL, false);

  girara_argument_t arg = { GIRARA_HIDE, NULL };
346
  girara_isc_completion(session, &arg, NULL, 0);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
347 348 349 350 351 352 353

  gtk_main_quit();

  return false;
}

bool
354
girara_sc_tab_close(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
355 356 357 358 359 360 361 362 363 364 365 366 367
{
  g_return_val_if_fail(session != NULL, false);

  girara_tab_t* tab = girara_tab_current_get(session);

  if (tab != NULL) {
    girara_tab_remove(session, tab);
  }

  return false;
}

bool
368
girara_sc_tab_navigate(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int t)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
369 370 371
{
  g_return_val_if_fail(session != NULL, false);

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
372 373
  const unsigned int number_of_tabs = girara_get_number_of_tabs(session);
  if (number_of_tabs == 0) {
374
    return false;
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
375
  }
376

Sebastian Ramacher's avatar
Sebastian Ramacher committed
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
  unsigned int current_tab    = girara_tab_position_get(session, girara_tab_current_get(session));
  unsigned int step           = (argument->n == GIRARA_PREVIOUS) ? -1 : 1;
  unsigned int new_tab        = (current_tab + step) % number_of_tabs;

  if (t != 0 && t <= number_of_tabs) {
    new_tab = t - 1;
  }

  girara_tab_t* tab = girara_tab_get(session, new_tab);

  if (tab != NULL) {
    girara_tab_current_set(session, tab);
  }

  girara_tab_update(session);

  return false;
}

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
bool
girara_sc_tab_navigate_next(girara_session_t* session,
                            girara_argument_t* argument,
                            girara_event_t* event, unsigned int t)
{
  argument->n = GIRARA_NEXT;
  return girara_sc_tab_navigate(session, argument, event, t);
}

bool
girara_sc_tab_navigate_prev(girara_session_t* session,
                            girara_argument_t* argument,
                            girara_event_t* event, unsigned int t)
{
  argument->n = GIRARA_PREVIOUS;
  return girara_sc_tab_navigate(session, argument, event, t);
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
414 415 416 417 418 419 420
static void
girara_toggle_widget_visibility(GtkWidget* widget)
{
  if (widget == NULL) {
    return;
  }

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
421
  if (gtk_widget_get_visible(widget) == TRUE) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
422 423 424 425 426 427 428
    gtk_widget_hide(widget);
  } else {
    gtk_widget_show(widget);
  }
}

bool
429
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
430 431 432 433 434 435 436 437 438
{
  g_return_val_if_fail(session != NULL, false);

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

  return true;
}

bool
439
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
440 441 442 443 444 445 446 447 448
{
  g_return_val_if_fail(session != NULL, false);

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

  return true;
}

bool
449
girara_sc_toggle_tabbar(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
450 451 452 453 454 455 456 457
{
  g_return_val_if_fail(session != NULL, false);

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

  return true;
}

458
bool
459
girara_sc_set(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
{
  g_return_val_if_fail(session  != NULL, false);

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

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

  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) {
    for(int i = 0; i < argc; i++) {
      char* argument = g_strdup(argv[i]);
Moritz Lipp's avatar
Moritz Lipp committed
480
      girara_list_append(argument_list, (void*) argument);
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
    }
  } else {
    girara_list_free(argument_list);
    return false;
  }

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

  /* cleanup */
  girara_list_free(argument_list);

  return false;
}

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
496 497
bool
girara_sc_feedkeys(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
498 499 500 501 502 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
    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
529 530
    {"Home",      GDK_KEY_Home},
    {"End",       GDK_KEY_End},
Moritz Lipp's avatar
Moritz Lipp committed
531 532 533 534 535 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 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
    {"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 {
            for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
              if (g_strcmp0(tmp + 2, gdk_keyboard_buttons[i].identifier) == 0) {
                keyval = gdk_keyboard_buttons[i].keyval;
                found = true;
                break;
              }
            }
          }
        /* Possible special key */
        } else {
          for (unsigned int i = 0; i < LENGTH(gdk_keyboard_buttons); i++) {
            if (g_strcmp0(tmp, gdk_keyboard_buttons[i].identifier) == 0) {
              keyval = gdk_keyboard_buttons[i].keyval;
              found = true;
              break;
            }
          }
        }

        g_free(tmp);

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

single_key:

609
      update_state_by_keyval(&state, keyval);
Moritz Lipp's avatar
Moritz Lipp committed
610 611 612 613 614 615 616
      simulate_key_press(session, state, keyval);
    }
  }

  return true;
}

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
617 618
bool
girara_shortcut_mapping_add(girara_session_t* session, const char* identifier, girara_shortcut_function_t function)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
619
{
Moritz Lipp's avatar
Moritz Lipp committed
620
  g_return_val_if_fail(session  != NULL, false);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654

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

  GIRARA_LIST_FOREACH(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, data)
    if (strcmp(data->identifier, identifier) == 0) {
      data->function = function;
      girara_list_iterator_free(iter);
      return true;
    }
  GIRARA_LIST_FOREACH_END(session->config.shortcut_mappings, girara_shortcut_mapping_t*, iter, data);

  /* add new config handle */
  girara_shortcut_mapping_t* mapping = g_slice_new(girara_shortcut_mapping_t);

  mapping->identifier = g_strdup(identifier);
  mapping->function   = function;
  girara_list_append(session->config.shortcut_mappings, mapping);

  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
655 656
bool
girara_argument_mapping_add(girara_session_t* session, const char* identifier, int value)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
{
  g_return_val_if_fail(session  != NULL, false);

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

  GIRARA_LIST_FOREACH(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping);
    if (g_strcmp0(mapping->identifier, identifier) == 0) {
      mapping->value = value;
      girara_list_iterator_free(iter);
      return true;
    }
  GIRARA_LIST_FOREACH_END(session->config.argument_mappings, girara_argument_mapping_t*, iter, mapping);

  /* add new config handle */
  girara_argument_mapping_t* mapping = g_slice_new(girara_argument_mapping_t);

  mapping->identifier = g_strdup(identifier);
  mapping->value      = value;
  girara_list_append(session->config.argument_mappings, mapping);

  return true;
}

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
689 690 691 692 693
  g_free(argument_mapping->identifier);
  g_slice_free(girara_argument_mapping_t, argument_mapping);
}

bool
Moritz Lipp's avatar
Moritz Lipp committed
694 695 696
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
697 698 699 700 701 702 703 704 705
{
  g_return_val_if_fail(session  != NULL, false);
  g_return_val_if_fail(function != NULL, false);

  girara_argument_t argument = {argument_n, argument_data};

  /* search for existing binding */
  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it)
    if (me_it->mask == mask && me_it->button == button &&
Moritz Lipp's avatar
Moritz Lipp committed
706
       me_it->mode == mode && me_it->event_type == event_type)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
707 708 709 710 711 712 713 714 715 716 717
    {
      me_it->function = function;
      me_it->argument = argument;
      girara_list_iterator_free(iter);
      return true;
    }
  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it);

  /* add new mouse event */
  girara_mouse_event_t* mouse_event = g_slice_new(girara_mouse_event_t);

Moritz Lipp's avatar
Moritz Lipp committed
718 719 720 721 722 723
  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;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
724 725 726 727 728
  girara_list_append(session->bindings.mouse_events, mouse_event);

  return true;
}

729 730 731 732 733 734 735 736 737 738
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);

  /* search for existing binding */
  GIRARA_LIST_FOREACH(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it)
    if (me_it->mask == mask && me_it->button == button &&
       me_it->mode == mode)
    {
Moritz Lipp's avatar
Moritz Lipp committed
739
      girara_list_remove(session->bindings.mouse_events, me_it);
740 741 742 743 744 745 746 747
      girara_list_iterator_free(iter);
      return true;
    }
  GIRARA_LIST_FOREACH_END(session->bindings.mouse_events, girara_mouse_event_t*, iter, me_it);

  return false;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
748 749 750 751 752 753 754 755
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);
}
Moritz Lipp's avatar
Moritz Lipp committed
756 757 758 759

static bool
simulate_key_press(girara_session_t* session, int state, int key)
{
Moritz Lipp's avatar
Moritz Lipp committed
760 761 762
  if (session == NULL || session->gtk.box == NULL) {
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791

  GdkEvent* event = gdk_event_new(GDK_KEY_PRESS);

  event->key.type       = GDK_KEY_PRESS;
  event->key.window     = 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;

  g_object_ref(event->key.window);

  GdkKeymapKey* keys;
  gint number_of_keys;

  if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(),
        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);

792 793
  gtk_main_iteration_do(FALSE);

Moritz Lipp's avatar
Moritz Lipp committed
794
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
795
}