/* SPDX-License-Identifier: Zlib */

#include "config.h"

#include <girara-gtk/settings.h>
#include <girara-gtk/session.h>
#include <girara-gtk/shortcuts.h>
#include <girara-gtk/config.h>
#include <girara-gtk/commands.h>
#include <girara/template.h>
#include <girara/utils.h>
#include <girara/log.h>
#include <glib/gi18n.h>

#include "callbacks.h"
#include "commands.h"
#include "completion.h"
#include "document-widget.h"
#include "marks.h"
#include "render.h"
#include "shortcuts.h"
#include "utils.h"
#include "zathura.h"

#define ZATHURA_RC "zathurarc"
#ifndef WITH_SANDBOX
#define DEFAULT_DB "sqlite"
#else
#define DEFAULT_DB "null"
#endif

#define INCREMENTAL_SEARCH false

static void cb_jumplist_change(girara_session_t* session, const char* UNUSED(name), girara_setting_type_t UNUSED(type),
                               const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  zathura_t* zathura = session->global.data;

  const int* ivalue = value;
  zathura_jumplist_set_max_size(zathura, MAX(0, *ivalue));
}

static void cb_color(girara_session_t* session, const char* name, girara_setting_type_t UNUSED(type), const void* value,
                     void* UNUSED(data)) {
  g_return_if_fail(session != NULL && value != NULL);
  GiraraTemplate* csstemplate = girara_session_get_template(session);

  const char* str_value = value;

  GdkRGBA color = {0, 0, 0, 0};
  gdk_rgba_parse(&color, str_value);

  g_autofree char* colorstr = gdk_rgba_to_string(&color);
  girara_template_set_variable_value(csstemplate, name, colorstr);
}

static void cb_color_change(girara_session_t* session, const char* name, girara_setting_type_t UNUSED(type),
                            const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  g_return_if_fail(name != NULL);
  zathura_t* zathura = session->global.data;

  const char* string_value = (const char*)value;
  if (g_strcmp0(name, "highlight-color") == 0) {
    parse_color(&zathura->ui.colors.highlight_color, string_value);
  } else if (g_strcmp0(name, "highlight-fg") == 0) {
    parse_color(&zathura->ui.colors.highlight_color_fg, string_value);
  } else if (g_strcmp0(name, "highlight-active-color") == 0) {
    parse_color(&zathura->ui.colors.highlight_color_active, string_value);
  } else if (g_strcmp0(name, "recolor-darkcolor") == 0) {
    if (zathura->sync.render_thread != NULL) {
      zathura_renderer_set_recolor_colors_str(zathura->sync.render_thread, NULL, string_value);
    }
  } else if (g_strcmp0(name, "recolor-lightcolor") == 0) {
    if (zathura->sync.render_thread != NULL) {
      zathura_renderer_set_recolor_colors_str(zathura->sync.render_thread, string_value, NULL);
    }
  } else if (g_strcmp0(name, "render-loading-bg") == 0) {
    parse_color(&zathura->ui.colors.render_loading_bg, string_value);
  } else if (g_strcmp0(name, "render-loading-fg") == 0) {
    parse_color(&zathura->ui.colors.render_loading_fg, string_value);
  } else if (g_strcmp0(name, "signature-success-color") == 0) {
    parse_color(&zathura->ui.colors.signature_success, string_value);
  } else if (g_strcmp0(name, "signature-warning-color") == 0) {
    parse_color(&zathura->ui.colors.signature_warning, string_value);
  } else if (g_strcmp0(name, "signature-error-color") == 0) {
    parse_color(&zathura->ui.colors.signature_error, string_value);
  }

  zathura_document_widget_render_all(zathura->ui.document_widget);
}

static void cb_nohlsearch_changed(girara_session_t* session, const char* UNUSED(name),
                                  girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  zathura_t* zathura = session->global.data;

  const bool* bvalue = value;
  document_draw_search_results(zathura, !*bvalue);
  zathura_document_widget_render_all(zathura->ui.document_widget);
}

static void cb_doubleclick_changed(girara_session_t* session, const char* UNUSED(name),
                                   girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  zathura_t* zathura = session->global.data;

  zathura->global.double_click_follow = *(const bool*)value;
}

static void cb_global_modifiers_changed(girara_session_t* session, const char* name, girara_setting_type_t UNUSED(type),
                                        const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  zathura_t* zathura = session->global.data;

  GdkModifierType* p;
  if (g_strcmp0(name, "synctex-edit-modifier") == 0) {
    p = &zathura->global.synctex_edit_modmask;
  } else if (g_strcmp0(name, "highlighter-modifier") == 0) {
    p = &zathura->global.highlighter_modmask;
  } else {
    girara_error("unreachable");
    return;
  }

  const char* modifier = value;
  if (g_strcmp0(modifier, "shift") == 0) {
    *p = GDK_SHIFT_MASK;
  } else if (g_strcmp0(modifier, "ctrl") == 0) {
    *p = GDK_CONTROL_MASK;
  } else if (g_strcmp0(modifier, "alt") == 0) {
    *p = GDK_MOD1_MASK;
  } else {
    girara_error("Invalid %s option: '%s'", name, modifier);
  }
}

static void cb_incsearch_changed(girara_session_t* session, const char* UNUSED(name),
                                 girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);

  bool inc_search = *(const bool*)value;
  girara_special_command_add(session, '/', cmd_search, inc_search, FORWARD, NULL);
  girara_special_command_add(session, '?', cmd_search, inc_search, BACKWARD, NULL);
}

static void cb_window_statbusbar_changed(girara_session_t* session, const char* name,
                                         girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  zathura_t* zathura = session->global.data;

  const bool is_window_setting = g_str_has_prefix(name, "window-");
  if (is_window_setting) {
    g_autofree char* formatted_filename = get_formatted_filename(zathura, !is_window_setting);
    girara_set_window_title(zathura->ui.session, formatted_filename);
  } else {
    statusbar_page_number_update(zathura);
  }
}

static void cb_show_signature_info(girara_session_t* session, const char* UNUSED(name),
                                   girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  zathura_t* zathura = session->global.data;

  if (zathura_has_document(zathura) == false) {
    return;
  }

  zathura_show_signature_information(zathura, *(const bool*)value);
  update_visible_pages(zathura);
}

static void cb_setting_recolor_adjust_lightness_change(girara_session_t* session, const char* name,
                                                       girara_setting_type_t UNUSED(type), const void* value,
                                                       void* UNUSED(data)) {
  g_return_if_fail(value != NULL);
  g_return_if_fail(session != NULL);
  g_return_if_fail(session->global.data != NULL);
  g_return_if_fail(name != NULL);
  zathura_t* zathura = session->global.data;

  const bool bool_value = *((const bool*)value);

  if (zathura->sync.render_thread != NULL &&
      zathura_renderer_recolor_adjust_lightness_enabled(zathura->sync.render_thread) != bool_value) {
    zathura_renderer_enable_recolor_adjust_lightness(zathura->sync.render_thread, bool_value);
    zathura_document_widget_render_all(zathura->ui.document_widget);
  }
}

static void cb_font(girara_session_t* session, const char* UNUSED(name), girara_setting_type_t UNUSED(type),
                    const void* value, void* UNUSED(data)) {
  g_return_if_fail(session != NULL && value != NULL);

  css_template_fill_font(session->private_data->csstemplate, value);
}

static void cb_guioptions(girara_session_t* session, const char* UNUSED(name), girara_setting_type_t UNUSED(type),
                          const void* value, void* UNUSED(data)) {
  g_return_if_fail(session != NULL && value != NULL);
  zathura_t* zathura = session->global.data;

  /* set default values */
  bool show_commandline = false;
  bool show_statusbar   = false;
  bool show_hscrollbar  = false;
  bool show_vscrollbar  = false;

  /* evaluate input */
  const char* input         = value;
  const size_t input_length = strlen(input);

  for (size_t i = 0; i < input_length; i++) {
    switch (input[i]) {
    /* command line */
    case 'c':
      show_commandline = true;
      break;
    /* statusbar */
    case 's':
      show_statusbar = true;
      break;
    case 'h':
      show_hscrollbar = true;
      break;
    case 'v':
      show_vscrollbar = true;
      break;
    }
  }

  /* apply settings */
  if (zathura->ui.view) {
    GtkPolicyType hpolicy = show_hscrollbar ? GTK_POLICY_AUTOMATIC : GTK_POLICY_EXTERNAL;
    GtkPolicyType vpolicy = show_vscrollbar ? GTK_POLICY_AUTOMATIC : GTK_POLICY_EXTERNAL;

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(zathura->ui.view), hpolicy, vpolicy);
  }

  /* apply settings */
  if (show_commandline == true) {
    session->global.autohide_inputbar = false;
    gtk_widget_show(session->gtk.inputbar);
  } else {
    session->global.autohide_inputbar = true;
    gtk_widget_hide(session->gtk.inputbar);
  }

  if (show_statusbar == true) {
    session->global.hide_statusbar = false;
    gtk_widget_show(session->gtk.statusbar);
  } else {
    session->global.hide_statusbar = true;
    gtk_widget_hide(session->gtk.statusbar);
  }
}

static void cb_scroll_step_value_changed(girara_session_t* session, const char* UNUSED(name),
                                         girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data)) {
  g_return_if_fail(session != NULL && value != NULL);
  zathura_t* zathura = session->global.data;
  if (zathura->ui.view == NULL) {
    return;
  }

  GtkAdjustment* v_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.view));
  if (v_adj) {
    gtk_adjustment_set_step_increment(v_adj, *(float*)value);
  }
}

static void add_default_shortcuts(girara_session_t* gsession, girara_mode_t mode) {
  girara_shortcut_add(gsession, 0, GDK_KEY_a, NULL, sc_adjust_window, mode, ZATHURA_ADJUST_BESTFIT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_s, NULL, sc_adjust_window, mode, ZATHURA_ADJUST_WIDTH, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_F, NULL, sc_display_link, mode, 0, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_c, NULL, sc_copy_link, mode, 0, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_slash, NULL, sc_focus_inputbar, mode, 0, "/");
  girara_shortcut_add(gsession, GDK_SHIFT_MASK, GDK_KEY_slash, NULL, sc_focus_inputbar, mode, 0, "/");
  girara_shortcut_add(gsession, 0, GDK_KEY_question, NULL, sc_focus_inputbar, mode, 0, "?");
  girara_shortcut_add(gsession, 0, GDK_KEY_colon, NULL, sc_focus_inputbar, mode, 0, ":");
  girara_shortcut_add(gsession, 0, GDK_KEY_o, NULL, sc_focus_inputbar, mode, 0, ":open ");
  girara_shortcut_add(gsession, 0, GDK_KEY_O, NULL, sc_focus_inputbar, mode, APPEND_FILEPATH, ":open ");

  girara_shortcut_add(gsession, 0, GDK_KEY_f, NULL, sc_follow, mode, 0, NULL);

  girara_shortcut_add(gsession, 0, 0, "gg", sc_goto, mode, TOP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Home, NULL, sc_goto, mode, TOP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Home, NULL, sc_goto, mode, TOP, NULL);
  girara_shortcut_add(gsession, 0, 0, "G", sc_goto, mode, BOTTOM, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_End, NULL, sc_goto, mode, BOTTOM, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_End, NULL, sc_goto, mode, BOTTOM, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_m, NULL, sc_mark_add, mode, 0, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_apostrophe, NULL, sc_mark_evaluate, mode, 0, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_J, NULL, sc_navigate, mode, NEXT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_K, NULL, sc_navigate, mode, PREVIOUS, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_Right, NULL, sc_navigate, mode, NEXT, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_KP_Right, NULL, sc_navigate, mode, NEXT, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_Left, NULL, sc_navigate, mode, PREVIOUS, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_KP_Left, NULL, sc_navigate, mode, PREVIOUS, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Page_Down, NULL, sc_navigate, mode, NEXT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Page_Down, NULL, sc_navigate, mode, NEXT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Page_Up, NULL, sc_navigate, mode, PREVIOUS, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Page_Up, NULL, sc_navigate, mode, PREVIOUS, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Forward, NULL, sc_navigate, mode, NEXT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Back, NULL, sc_navigate, mode, PREVIOUS, NULL);

  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_p, NULL, sc_print, mode, 0, NULL);

  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_r, NULL, sc_recolor, mode, 0, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_R, NULL, sc_reload, mode, 0, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_r, NULL, sc_rotate, mode, ROTATE_CW, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_h, NULL, sc_scroll, mode, LEFT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_j, NULL, sc_scroll, mode, DOWN, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_k, NULL, sc_scroll, mode, UP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_l, NULL, sc_scroll, mode, RIGHT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Left, NULL, sc_scroll, mode, LEFT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Left, NULL, sc_scroll, mode, LEFT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Up, NULL, sc_scroll, mode, UP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Up, NULL, sc_scroll, mode, UP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Down, NULL, sc_scroll, mode, DOWN, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Down, NULL, sc_scroll, mode, DOWN, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_H, NULL, sc_scroll, mode, PAGE_TOP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_L, NULL, sc_scroll, mode, PAGE_BOTTOM, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Right, NULL, sc_scroll, mode, RIGHT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Right, NULL, sc_scroll, mode, RIGHT, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_t, NULL, sc_scroll, mode, HALF_LEFT, NULL);

  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_k, NULL, sc_scroll, mode, PARTIAL_UP, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_j, NULL, sc_scroll, mode, PARTIAL_DOWN, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_Up, NULL, sc_scroll, mode, PARTIAL_UP, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_KP_Up, NULL, sc_scroll, mode, PARTIAL_UP, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_Down, NULL, sc_scroll, mode, PARTIAL_DOWN, NULL);
  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_KP_Down, NULL, sc_scroll, mode, PARTIAL_DOWN, NULL);

  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_d, NULL, sc_scroll, mode, HALF_DOWN, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_u, NULL, sc_scroll, mode, HALF_UP, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_y, NULL, sc_scroll, mode, HALF_RIGHT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_t, NULL, sc_scroll, mode, FULL_LEFT, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_f, NULL, sc_scroll, mode, FULL_DOWN, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_b, NULL, sc_scroll, mode, FULL_UP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_y, NULL, sc_scroll, mode, FULL_RIGHT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_space, NULL, sc_scroll, mode, FULL_DOWN, NULL);
  girara_shortcut_add(gsession, GDK_SHIFT_MASK, GDK_KEY_space, NULL, sc_scroll, mode, FULL_UP, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_Y, NULL, sc_copy_filepath, mode, 0, NULL);

  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_o, NULL, sc_jumplist, mode, BACKWARD, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_i, NULL, sc_jumplist, mode, FORWARD, NULL);

  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_j, NULL, sc_bisect, mode, FORWARD, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_k, NULL, sc_bisect, mode, BACKWARD, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_n, NULL, sc_search, mode, FORWARD, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_N, NULL, sc_search, mode, BACKWARD, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_P, NULL, sc_snap_to_page, mode, 0, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_Tab, NULL, sc_toggle_index, mode, 0, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_n, NULL, girara_sc_toggle_statusbar, mode, 0, NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_m, NULL, girara_sc_toggle_inputbar, mode, 0, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_d, NULL, sc_toggle_page_mode, mode, 0, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_D, NULL, sc_cycle_first_column, mode, 0, NULL);

  girara_shortcut_add(gsession, 0, GDK_KEY_plus, NULL, sc_zoom, mode, ZOOM_IN, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Add, NULL, sc_zoom, mode, ZOOM_IN, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_minus, NULL, sc_zoom, mode, ZOOM_OUT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_KP_Subtract, NULL, sc_zoom, mode, ZOOM_OUT, NULL);
  girara_shortcut_add(gsession, 0, GDK_KEY_equal, NULL, sc_zoom, mode, ZOOM_SPECIFIC, NULL);
  girara_shortcut_add(gsession, 0, 0, "zi", sc_zoom, mode, ZOOM_IN, NULL);
  girara_shortcut_add(gsession, 0, 0, "zI", sc_zoom, mode, ZOOM_IN, NULL);
  girara_shortcut_add(gsession, 0, 0, "zo", sc_zoom, mode, ZOOM_OUT, NULL);
  girara_shortcut_add(gsession, 0, 0, "zO", sc_zoom, mode, ZOOM_OUT, NULL);
  girara_shortcut_add(gsession, 0, 0, "z0", sc_zoom, mode, ZOOM_ORIGINAL, NULL);
  girara_shortcut_add(gsession, 0, 0, "zz", sc_zoom, mode, ZOOM_SPECIFIC, NULL);
  girara_shortcut_add(gsession, 0, 0, "zZ", sc_zoom, mode, ZOOM_SPECIFIC, NULL);

  girara_shortcut_add(gsession, GDK_MOD1_MASK, GDK_KEY_o, NULL, sc_file_chooser, mode, 0, NULL);
}

static void add_default_mouse_events(girara_session_t* gsession, girara_mode_t mode) {
  girara_mouse_event_add(gsession, 0, 0, sc_mouse_scroll, mode, GIRARA_EVENT_SCROLL_UP, UP, NULL);
  girara_mouse_event_add(gsession, 0, 0, sc_mouse_scroll, mode, GIRARA_EVENT_SCROLL_DOWN, DOWN, NULL);
  girara_mouse_event_add(gsession, 0, 0, sc_mouse_scroll, mode, GIRARA_EVENT_SCROLL_LEFT, LEFT, NULL);
  girara_mouse_event_add(gsession, 0, 0, sc_mouse_scroll, mode, GIRARA_EVENT_SCROLL_RIGHT, RIGHT, NULL);
  girara_mouse_event_add(gsession, 0, 0, sc_mouse_scroll, mode, GIRARA_EVENT_SCROLL_BIDIRECTIONAL, BIDIRECTIONAL, NULL);

  girara_mouse_event_add(gsession, GDK_SHIFT_MASK, 0, sc_mouse_scroll, mode, GIRARA_EVENT_SCROLL_UP, LEFT, NULL);
  girara_mouse_event_add(gsession, GDK_SHIFT_MASK, 0, sc_mouse_scroll, mode, GIRARA_EVENT_SCROLL_DOWN, RIGHT, NULL);

  girara_mouse_event_add(gsession, GDK_CONTROL_MASK, 0, sc_mouse_zoom, mode, GIRARA_EVENT_SCROLL_UP, UP, NULL);
  girara_mouse_event_add(gsession, GDK_CONTROL_MASK, 0, sc_mouse_zoom, mode, GIRARA_EVENT_SCROLL_DOWN, DOWN, NULL);
  girara_mouse_event_add(gsession, GDK_CONTROL_MASK, 0, sc_mouse_zoom, mode, GIRARA_EVENT_SCROLL_BIDIRECTIONAL,
                         BIDIRECTIONAL, NULL);
  girara_mouse_event_add(gsession, 0, GIRARA_MOUSE_BUTTON2, sc_mouse_scroll, mode, GIRARA_EVENT_BUTTON_PRESS, 0, NULL);
  girara_mouse_event_add(gsession, GDK_BUTTON2_MASK, GIRARA_MOUSE_BUTTON2, sc_mouse_scroll, mode,
                         GIRARA_EVENT_BUTTON_RELEASE, 0, NULL);
  girara_mouse_event_add(gsession, GDK_BUTTON2_MASK, 0, sc_mouse_scroll, mode, GIRARA_EVENT_MOTION_NOTIFY, 0, NULL);
}

void config_load_default(zathura_t* zathura) {
  g_return_if_fail(zathura != NULL && zathura->ui.session != NULL);

  int int_value              = 0;
  unsigned uint_value        = 0;
  float float_value          = 0;
  bool bool_value            = false;
  girara_session_t* gsession = zathura->ui.session;

  /* mode settings */
  zathura->modes.normal       = gsession->modes.normal;
  zathura->modes.fullscreen   = girara_mode_add(gsession, "fullscreen");
  zathura->modes.index        = girara_mode_add(gsession, "index");
  zathura->modes.insert       = girara_mode_add(gsession, "insert");
  zathura->modes.presentation = girara_mode_add(gsession, "presentation");

#define NORMAL zathura->modes.normal
#define INSERT zathura->modes.insert
#define INDEX zathura->modes.index
#define FULLSCREEN zathura->modes.fullscreen
#define PRESENTATION zathura->modes.presentation

  const girara_mode_t all_modes[] = {
      NORMAL, INSERT, INDEX, FULLSCREEN, PRESENTATION,
  };

  /* Set default mode */
  girara_mode_set(gsession, zathura->modes.normal);

  /* values */
  const int statusbar_h_padding         = 8;
  const int statusbar_v_padding         = 2;
  const unsigned int window_width       = 800;
  const unsigned int window_height      = 600;
  const unsigned int n_completion_items = 15;

  /* clang-format off */
  /* settings */
  girara_setting_add(gsession, "font",                     "monospace normal 9", STRING,  FALSE, _("Font"), cb_font, NULL);

  /* colors affecting the CSS */
  girara_setting_add(gsession, "default-fg",               "#DDDDDD", STRING, false, _("Default foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "default-bg",               "#000000", STRING, false, _("Default background color"), cb_color, NULL);
  girara_setting_add(gsession, "inputbar-fg",              "#9FBC00", STRING, false, _("Inputbar foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "inputbar-bg",              "#131313", STRING, false, _("Inputbar background color"), cb_color, NULL);
  girara_setting_add(gsession, "statusbar-fg",             "#FFFFFF", STRING, false, _("Statusbar foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "statusbar-bg",             "#000000", STRING, false, _("Statsubar background color"), cb_color, NULL);
  girara_setting_add(gsession, "completion-fg",            "#DDDDDD", STRING, false, _("Completion foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "completion-bg",            "#232323", STRING, false, _("Completion background color"), cb_color, NULL);
  girara_setting_add(gsession, "completion-group-fg",      "#DEDEDE", STRING, false, _("Completion group foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "completion-group-bg",      "#000000", STRING, false, _("Completion group background color"), cb_color, NULL);
  girara_setting_add(gsession, "completion-highlight-fg",  "#232323", STRING, false, _("Completion highlight foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "completion-highlight-bg",  "#9FBC00", STRING, false, _("Completion highlight background color"), cb_color, NULL);
  girara_setting_add(gsession, "notification-error-fg",    "#FFFFFF", STRING, false, _("Error notification foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "notification-error-bg",    "#FF1212", STRING, false, _("Error notification background color"), cb_color, NULL);
  girara_setting_add(gsession, "notification-warning-fg",  "#000000", STRING, false, _("Warning notification foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "notification-warning-bg",  "#F3F000", STRING, false, _("Warning notifaction background color"), cb_color, NULL);
  girara_setting_add(gsession, "notification-fg",          "#000000", STRING, false, _("Notification foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "notification-bg",          "#FFFFFF", STRING, false, _("Notification background color"), cb_color, NULL);
  girara_setting_add(gsession, "index-fg",                 "#DDDDDD", STRING, false, _("Index mode foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "index-bg",                 "#232323", STRING, false, _("Index mode background color"), cb_color, NULL);
  girara_setting_add(gsession, "index-active-fg",          "#232323", STRING, false, _("Index mode foreground color (active element)"), cb_color, NULL);
  girara_setting_add(gsession, "index-active-bg",          "#9FBC00", STRING, false, _("Index mode background color (active element)"), cb_color, NULL);
  girara_setting_add(gsession, "scrollbar-fg",             "#DDDDDD", STRING, false, _("Scrollbar foreground color"), cb_color, NULL);
  girara_setting_add(gsession, "scrollbar-bg",             "#000000", STRING, false, _("Scrollbar background color"), cb_color, NULL);

  girara_setting_add(gsession, "word-separator",           " /.-=&#?",           STRING,  TRUE,  NULL, NULL, NULL);
  girara_setting_add(gsession, "window-width",             &window_width,        UINT,    TRUE,  _("Initial window width"), NULL, NULL);
  girara_setting_add(gsession, "window-height",            &window_height,       UINT,    TRUE,  _("Initial window height"), NULL, NULL);
  girara_setting_add(gsession, "statusbar-h-padding",      &statusbar_h_padding, INT,     TRUE,  _("Horizontal padding for the status, input, and notification bars"), NULL, NULL);
  girara_setting_add(gsession, "statusbar-v-padding",      &statusbar_v_padding, INT,     TRUE,  _("Vertical padding for the status, input, and notification bars"), NULL, NULL);
  girara_setting_add(gsession, "n-completion-items",       &n_completion_items,  UINT,    TRUE,  _("Number of completion items"), NULL, NULL);
  girara_setting_add(gsession, "exec-command",             "",                   STRING,  FALSE, _("Command to execute in :exec"), NULL, NULL);
  girara_setting_add(gsession, "guioptions",               "s",                  STRING,  FALSE, _("Show or hide certain GUI elements"), cb_guioptions, NULL);

  girara_setting_add(gsession, "database",              DEFAULT_DB,   STRING, true,  _("Database backend"),         NULL, NULL);
  girara_setting_add(gsession, "filemonitor",           "glib",       STRING, true,  _("File monitor backend"),     NULL, NULL);
  uint_value = 10;
  girara_setting_add(gsession, "zoom-step",             &uint_value,  UINT,   false, _("Zoom step"),                NULL, NULL);
  int_value = 1;
  girara_setting_add(gsession, "page-h-padding",        &int_value,   INT,    false, _("Horizontal padding between pages"), cb_page_layout_value_changed, NULL);
  int_value = 1;
  girara_setting_add(gsession, "page-v-padding",        &int_value,   INT,    false, _("Vertical padding between pages"), cb_page_layout_value_changed, NULL);
  uint_value = 1;
  girara_setting_add(gsession, "pages-per-row",         &uint_value,  UINT,   false, _("Number of pages per row"),  cb_page_layout_value_changed, NULL);
  girara_setting_add(gsession, "first-page-column",     "1:2",        STRING, false, _("Column of the first page"), cb_page_layout_value_changed, NULL);
  bool_value = false;
  girara_setting_add(gsession, "page-right-to-left",    &bool_value,  BOOLEAN, false, _("Render pages from right to left"),  cb_page_layout_value_changed, NULL);
  float_value = 40;
  girara_setting_add(gsession, "scroll-step",           &float_value, FLOAT,  false, _("Scroll step"),              cb_scroll_step_value_changed, NULL);
  float_value = 40;
  girara_setting_add(gsession, "scroll-hstep",          &float_value, FLOAT,  false, _("Horizontal scroll step"),   NULL, NULL);
  float_value = 0.0;
  girara_setting_add(gsession, "scroll-full-overlap",   &float_value, FLOAT,  false, _("Full page scroll overlap"), NULL, NULL);
  uint_value = 10;
  girara_setting_add(gsession, "zoom-min",              &uint_value,  UINT,   false, _("Zoom minimum"), NULL, NULL);
  uint_value = 1000;
  girara_setting_add(gsession, "zoom-max",              &uint_value,  UINT,   false, _("Zoom maximum"), NULL, NULL);
  uint_value = ZATHURA_PAGE_CACHE_DEFAULT_SIZE;
  girara_setting_add(gsession, "page-cache-size",       &uint_value,  UINT,   true,  _("Maximum number of pages to keep in the cache"), NULL, NULL);
  uint_value = ZATHURA_PAGE_THUMBNAIL_DEFAULT_SIZE;
  girara_setting_add(gsession, "page-thumbnail-size",   &uint_value,  UINT,   true,  _("Maximum size in pixels of thumbnails to keep in the cache"), NULL, NULL);
  uint_value = 2000;
  girara_setting_add(gsession, "jumplist-size",         &uint_value,  UINT,   false, _("Number of positions to remember in the jumplist"), cb_jumplist_change, NULL);
  girara_setting_add(gsession, "recolor-darkcolor",     "#FFFFFF",    STRING, false, _("Recoloring (dark color)"), cb_color_change, NULL);
  girara_setting_add(gsession, "recolor-lightcolor",    "#000000",    STRING, false, _("Recoloring (light color)"), cb_color_change, NULL);
  girara_setting_add(gsession, "highlight-color",       NULL,         STRING, false, _("Color for highlighting"), cb_color_change, NULL);
  girara_setting_add(gsession, "highlight-fg",          NULL,         STRING, false, _("Foreground color for highlighting"),cb_color_change, NULL);
  girara_setting_add(gsession, "highlight-active-color",NULL,         STRING, false, _("Color for highlighting (active)"), cb_color_change, NULL);
  girara_setting_add(gsession, "render-loading-bg",     NULL,         STRING, false, _("'Loading ...' background color"), cb_color_change, NULL);
  girara_setting_add(gsession, "render-loading-fg",     NULL,         STRING, false, _("'Loading ...' foreground color"), cb_color_change, NULL);

  girara_setting_add(gsession, "signature-success-color",    NULL,         STRING,  false, _("Color used to highlight valid signatures"), cb_color_change, NULL);
  girara_setting_add(gsession, "signature-warning-color",    NULL,         STRING,  false, _("Color used to highlight signatures with warnings"), cb_color_change, NULL);
  girara_setting_add(gsession, "signature-error-color",      NULL,         STRING,  false, _("Color used to highlight invalid signatures"), cb_color_change, NULL);
  bool_value = false;
  girara_setting_add(gsession, "recolor",                    &bool_value,  BOOLEAN, false, _("Recolor pages"), cb_setting_recolor_change, NULL);
  bool_value = false;
  girara_setting_add(gsession, "recolor-keephue",            &bool_value,  BOOLEAN, false, _("When recoloring keep original hue and adjust lightness only"), cb_setting_recolor_keep_hue_change, NULL);
  bool_value = false;
  girara_setting_add(gsession, "recolor-reverse-video",      &bool_value,  BOOLEAN, false, _("When recoloring keep original image colors"), cb_setting_recolor_keep_reverse_video_change, NULL);
  bool_value = false;
  girara_setting_add(gsession, "recolor-adjust-lightness",   &bool_value,  BOOLEAN, false, _("When recoloring adjust lightness"), cb_setting_recolor_adjust_lightness_change, NULL);
  bool_value = false;
  girara_setting_add(gsession, "scroll-wrap",                &bool_value,  BOOLEAN, false, _("Wrap scrolling"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "scroll-page-aware",          &bool_value,  BOOLEAN, false, _("Page aware scrolling"), NULL, NULL);
  bool_value = true;
  girara_setting_add(gsession, "advance-pages-per-row",      &bool_value,  BOOLEAN, false, _("Advance number of pages per row"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "zoom-center",                &bool_value,  BOOLEAN, false, _("Horizontally centered zoom"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "vertical-center",            &bool_value,  BOOLEAN, false, _("Vertically center pages"), NULL, NULL);
  bool_value = true;
  girara_setting_add(gsession, "link-hadjust",               &bool_value,  BOOLEAN, false, _("Align link target to the left"), NULL, NULL);
  bool_value = true;
  girara_setting_add(gsession, "link-zoom",                  &bool_value,  BOOLEAN, false, _("Let zoom be changed when following links"), NULL, NULL);
  bool_value = true;
  girara_setting_add(gsession, "search-hadjust",             &bool_value,  BOOLEAN, false, _("Center result horizontally"), NULL, NULL);
  bool_value = true;
  girara_setting_add(gsession, "render-loading",             &bool_value,  BOOLEAN, false, _("Render 'Loading ...'"), NULL, NULL);
  girara_setting_add(gsession, "adjust-open",                "best-fit",   STRING,  false, _("Adjust to when opening file"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "show-hidden",                &bool_value,  BOOLEAN, false, _("Show hidden files and directories"), NULL, NULL);
  bool_value = true;
  girara_setting_add(gsession, "show-directories",           &bool_value,  BOOLEAN, false, _("Show directories"), NULL, NULL);
  int_value = 10;
  girara_setting_add(gsession, "show-recent",                &int_value,   INT,     false, _("Show recent files"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "open-first-page",            &bool_value,  BOOLEAN, false, _("Always open on first page"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "nohlsearch",                 &bool_value,  BOOLEAN, false, _("Disable highlighting of search results"), cb_nohlsearch_changed, NULL);
  bool_value = true;
  girara_setting_add(gsession, "double-click-follow",        &bool_value,  BOOLEAN, false, _("Double click to follow links"), cb_doubleclick_changed, NULL);
  bool_value = INCREMENTAL_SEARCH;
  girara_setting_add(gsession, "incremental-search",         &bool_value,  BOOLEAN, false, _("Enable incremental search"), cb_incsearch_changed, NULL);
  bool_value = true;
  girara_setting_add(gsession, "abort-clear-search",         &bool_value,  BOOLEAN, false, _("Clear search results on abort"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "window-title-basename",      &bool_value,  BOOLEAN, false, _("Use basename of the file in the window title"), cb_window_statbusbar_changed, NULL);
  bool_value = false;
  girara_setting_add(gsession, "window-title-home-tilde",    &bool_value,  BOOLEAN, false, _("Use ~ instead of $HOME in the filename in the window title"), cb_window_statbusbar_changed, NULL);
  bool_value = false;
  girara_setting_add(gsession, "window-title-page",          &bool_value,  BOOLEAN, false, _("Display the page number in the window title"), cb_window_statbusbar_changed, NULL);
  bool_value = false;
  girara_setting_add(gsession, "window-icon-document",       &bool_value,  BOOLEAN, false, _("Use first page of a document as window icon"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "statusbar-basename",         &bool_value,  BOOLEAN, false, _("Use basename of the file in the statusbar"), cb_window_statbusbar_changed, NULL);
  bool_value = false;
  girara_setting_add(gsession, "statusbar-home-tilde",       &bool_value,  BOOLEAN, false, _("Use ~ instead of $HOME in the filename in the statusbar"), cb_window_statbusbar_changed, NULL);
  bool_value = false;
  girara_setting_add(gsession, "statusbar-page-percent",     &bool_value,  BOOLEAN, false, _("Display (current page / total pages) as a percent in the statusbar"), cb_window_statbusbar_changed, NULL);
  bool_value = true;
  girara_setting_add(gsession, "synctex",                    &bool_value,  BOOLEAN, false, _("Enable SyncTeX support"), NULL, NULL);
  girara_setting_add(gsession, "synctex-editor-command",     "",           STRING,  false, _("SyncTeX editor command"), NULL, NULL);
  girara_setting_add(gsession, "synctex-edit-modifier",      "ctrl",       STRING,  false, _("SyncTeX edit modifier"), cb_global_modifiers_changed, NULL);
  girara_setting_add(gsession, "highlighter-modifier",       "shift",      STRING,  false, _("Highlighter modifier"), cb_global_modifiers_changed, NULL);
  bool_value = true;
  girara_setting_add(gsession, "dbus-service",               &bool_value,  BOOLEAN, false, _("Enable D-Bus service"), NULL, NULL);
  girara_setting_add(gsession, "dbus-raise-window",          &bool_value,  BOOLEAN, false, _("Raise window on certain D-Bus commands"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "continuous-hist-save",       &bool_value,  BOOLEAN, false, _("Save history at each page change"), NULL, NULL);
  girara_setting_add(gsession, "selection-clipboard",        "primary",    STRING,  false, _("The clipboard into which mouse-selected data will be written"), NULL, NULL);
  bool_value = true;
  girara_setting_add(gsession, "selection-notification",     &bool_value,  BOOLEAN, false, _("Enable notification after selecting text"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "selection-keep-highlight",   &bool_value,  BOOLEAN, false, _("Keep selection highlighted after mouse release"), NULL, NULL);
  bool_value = false;
  girara_setting_add(gsession, "show-signature-information", &bool_value,  BOOLEAN, false, _("Disable additional information for signatures embedded in the document."), cb_show_signature_info, NULL);

  // clang-format on
  // apply some color settings that need their callbacks to run
  girara_setting_set(gsession, "highlight-color", "rgba(159,251,0,0.5)");
  girara_setting_set(gsession, "highlight-fg", "rgba(0,0,0,0.5)");
  girara_setting_set(gsession, "highlight-active-color", "rgba(0,188,0,0.5)");
  girara_setting_set(gsession, "render-loading-bg", "#FFFFFF");
  girara_setting_set(gsession, "render-loading-fg", "#000000");
  girara_setting_set(gsession, "signature-success-color", "rgba(18%,80%,33%,0.9)");
  girara_setting_set(gsession, "signature-warning-color", "rgba(100%,84%,0%,0.9)");
  girara_setting_set(gsession, "signature-error-color", "rgba(92%,11%,14%,0.9)");
  // clang-format off

  /* Define mode-less shortcuts
   * girara adds them only for normal mode, so passing 0 as mode is currently
   * not enough. We need to add/override for every mode. */
  for (size_t idx = 0; idx != LENGTH(all_modes); ++idx) {
    girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_c,           NULL, sc_abort, all_modes[idx], 0, NULL);
    girara_shortcut_add(gsession, 0,                GDK_KEY_Escape,      NULL, sc_abort, all_modes[idx], 0, NULL);
    girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_bracketleft, NULL, sc_abort, all_modes[idx], 0, NULL);
    girara_shortcut_add(gsession, 0,                GDK_KEY_q,           NULL, sc_quit,  all_modes[idx], 0, NULL);
  }

  /* Normal mode */
  /* shortcuts */
  girara_shortcut_add(gsession, 0,                GDK_KEY_F5,          NULL, sc_toggle_presentation,    NORMAL, 0, NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_F11,         NULL, sc_toggle_fullscreen,      NORMAL, 0, NULL);

  add_default_shortcuts(gsession, NORMAL);

  /* Normal mode - Mouse events */
  add_default_mouse_events(gsession, NORMAL);

  /* Fullscreen mode */
  girara_shortcut_add(gsession, 0, GDK_KEY_F11, NULL, sc_toggle_fullscreen, FULLSCREEN, 0, NULL);

  add_default_shortcuts(gsession, FULLSCREEN);

  /* Fullscreen mode - Mouse events */
  add_default_mouse_events(gsession, FULLSCREEN);

  /* Index mode */
  girara_shortcut_add(gsession, 0,                GDK_KEY_Tab,         NULL, sc_toggle_index,   INDEX, 0,                  NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_k,           NULL, sc_navigate_index, INDEX, UP,                 NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_j,           NULL, sc_navigate_index, INDEX, DOWN,               NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_d,           NULL, sc_navigate_index, INDEX, HALF_DOWN,          NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_u,           NULL, sc_navigate_index, INDEX, HALF_UP,            NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_h,           NULL, sc_navigate_index, INDEX, COLLAPSE,           NULL);
  girara_shortcut_add(gsession, 0,                0,                   "zc", sc_navigate_index, INDEX, COLLAPSE,           NULL);
  girara_shortcut_add(gsession, 0,                0,                   "zC", sc_navigate_index, INDEX, COLLAPSE_RECURSIVE, NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_H,           NULL, sc_navigate_index, INDEX, COLLAPSE_ALL,       NULL);
  girara_shortcut_add(gsession, 0,                0,                   "zM", sc_navigate_index, INDEX, COLLAPSE_ALL,       NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_l,           NULL, sc_navigate_index, INDEX, EXPAND,             NULL);
  girara_shortcut_add(gsession, 0,                0,                   "zo", sc_navigate_index, INDEX, EXPAND,             NULL);
  girara_shortcut_add(gsession, 0,                0,                   "zO", sc_navigate_index, INDEX, EXPAND_RECURSIVE,   NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_L,           NULL, sc_navigate_index, INDEX, EXPAND_ALL,         NULL);
  girara_shortcut_add(gsession, 0,                0,                   "zR", sc_navigate_index, INDEX, EXPAND_ALL,         NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_Up,          NULL, sc_navigate_index, INDEX, UP,                 NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_KP_Up,       NULL, sc_navigate_index, INDEX, UP,                 NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_Down,        NULL, sc_navigate_index, INDEX, DOWN,               NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_KP_Down,     NULL, sc_navigate_index, INDEX, DOWN,               NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_Left,        NULL, sc_navigate_index, INDEX, COLLAPSE,           NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_KP_Left,     NULL, sc_navigate_index, INDEX, COLLAPSE,           NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_Right,       NULL, sc_navigate_index, INDEX, EXPAND,             NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_KP_Right,    NULL, sc_navigate_index, INDEX, EXPAND,             NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_space,       NULL, sc_navigate_index, INDEX, SELECT,             NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_Return,      NULL, sc_navigate_index, INDEX, SELECT,             NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_j,           NULL, sc_navigate_index, INDEX, SELECT,             NULL);
  girara_shortcut_add(gsession, 0,                0,                   "gg", sc_navigate_index, INDEX, TOP,                NULL);
  girara_shortcut_add(gsession, 0,                0,                   "G",  sc_navigate_index, INDEX, BOTTOM,             NULL);
  girara_shortcut_add(gsession, 0,                GDK_KEY_Escape,      NULL, sc_toggle_index,   INDEX, 0,                  NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_bracketleft, NULL, sc_toggle_index,   INDEX, 0,                  NULL);
  girara_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_c,           NULL, sc_toggle_index,   INDEX, 0,                  NULL);

  /* Presentation mode */
  girara_shortcut_add(gsession, 0,              GDK_KEY_J,             NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Down,          NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_KP_Down,       NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Right,         NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_KP_Right,      NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Page_Down,     NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_KP_Page_Down,  NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Forward,       NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_space,         NULL, sc_navigate,            PRESENTATION, NEXT,         NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_K,             NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Left,          NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_KP_Left,       NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Up,            NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_KP_Up,         NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Page_Up,       NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_KP_Page_Up,    NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_Back,          NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, GDK_SHIFT_MASK, GDK_KEY_space,         NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_BackSpace,     NULL, sc_navigate,            PRESENTATION, PREVIOUS,     NULL);
  girara_shortcut_add(gsession, 0,              GDK_KEY_F5,            NULL, sc_toggle_presentation, PRESENTATION, 0,            NULL);

  /* Presentation mode - Mouse events */
  girara_mouse_event_add(gsession, 0,                0,                    sc_mouse_scroll, PRESENTATION, GIRARA_EVENT_SCROLL_UP,    UP,       NULL);
  girara_mouse_event_add(gsession, 0,                0,                    sc_mouse_scroll, PRESENTATION, GIRARA_EVENT_SCROLL_DOWN,  DOWN,     NULL);
  girara_mouse_event_add(gsession, 0,                0,                    sc_mouse_scroll, PRESENTATION, GIRARA_EVENT_SCROLL_LEFT,  LEFT,     NULL);
  girara_mouse_event_add(gsession, 0,                0,                    sc_mouse_scroll, PRESENTATION, GIRARA_EVENT_SCROLL_RIGHT, RIGHT,    NULL);
  girara_mouse_event_add(gsession, 0,                GIRARA_MOUSE_BUTTON1, sc_navigate,     PRESENTATION, GIRARA_EVENT_BUTTON_PRESS, NEXT,     NULL);
  girara_mouse_event_add(gsession, 0,                GIRARA_MOUSE_BUTTON3, sc_navigate,     PRESENTATION, GIRARA_EVENT_BUTTON_PRESS, PREVIOUS, NULL);
  girara_mouse_event_add(gsession, GDK_SHIFT_MASK,   0,                    sc_mouse_scroll, PRESENTATION, GIRARA_EVENT_SCROLL_UP,    LEFT,     NULL);
  girara_mouse_event_add(gsession, GDK_SHIFT_MASK,   0,                    sc_mouse_scroll, PRESENTATION, GIRARA_EVENT_SCROLL_DOWN,  RIGHT,    NULL);
  girara_mouse_event_add(gsession, GDK_CONTROL_MASK, 0,                    sc_mouse_zoom,   PRESENTATION, GIRARA_EVENT_SCROLL_UP,    UP,       NULL);
  girara_mouse_event_add(gsession, GDK_CONTROL_MASK, 0,                    sc_mouse_zoom,   PRESENTATION, GIRARA_EVENT_SCROLL_DOWN,  DOWN,     NULL);

  /* inputbar shortcuts */
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_Escape,       sc_abort,                       0,                           NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_c,            sc_abort,                       0,                           NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_j,            girara_isc_activate,            0,                           NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_bracketleft,  girara_isc_abort,               0,                           NULL);
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_Tab,          girara_isc_completion,          GIRARA_NEXT,                 NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_Tab,          girara_isc_completion,          GIRARA_NEXT_GROUP,           NULL);
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_ISO_Left_Tab, girara_isc_completion,          GIRARA_PREVIOUS,             NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_ISO_Left_Tab, girara_isc_completion,          GIRARA_PREVIOUS_GROUP,       NULL);
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_BackSpace,    girara_isc_string_manipulation, GIRARA_DELETE_LAST_CHAR,     NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_h,            girara_isc_string_manipulation, GIRARA_DELETE_LAST_CHAR,     NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_u,            girara_isc_string_manipulation, GIRARA_DELETE_TO_LINE_START, NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_k,            girara_isc_string_manipulation, GIRARA_DELETE_TO_LINE_END,   NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_d,            girara_isc_string_manipulation, GIRARA_DELETE_CURR_CHAR,     NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_w,            girara_isc_string_manipulation, GIRARA_DELETE_LAST_WORD,     NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_f,            girara_isc_string_manipulation, GIRARA_NEXT_CHAR,            NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_b,            girara_isc_string_manipulation, GIRARA_PREVIOUS_CHAR,        NULL);
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_Right,        girara_isc_string_manipulation, GIRARA_NEXT_CHAR,            NULL);
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_Left,         girara_isc_string_manipulation, GIRARA_PREVIOUS_CHAR,        NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_a,            girara_isc_string_manipulation, GIRARA_GOTO_START,           NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_e,            girara_isc_string_manipulation, GIRARA_GOTO_END,             NULL);
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_Up,           girara_isc_command_history,     GIRARA_PREVIOUS,             NULL);
  girara_inputbar_shortcut_add(gsession, 0,                GDK_KEY_Down,         girara_isc_command_history,     GIRARA_NEXT,                 NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_p,            girara_isc_command_history,     GIRARA_PREVIOUS,             NULL);
  girara_inputbar_shortcut_add(gsession, GDK_CONTROL_MASK, GDK_KEY_n,            girara_isc_command_history,     GIRARA_NEXT,                 NULL);

  /* define default inputbar commands */
  girara_inputbar_command_add(gsession, "map",        "m",    girara_cmd_map,          NULL,          _("Map a key sequence"));
  girara_inputbar_command_add(gsession, "set",        "s",    girara_cmd_set,          girara_cc_set, _("Set an option"));
  girara_inputbar_command_add(gsession, "shortcut",   "sc",   girara_cmd_shortcut,     NULL,          _("Run a shortcut"));
  girara_inputbar_command_add(gsession, "unmap",      NULL,   girara_cmd_unmap,        NULL,          _("Unmap a key sequence"));
  girara_inputbar_command_add(gsession, "dump",       NULL,   girara_cmd_dump_config,  NULL,          _("Dump settings to a file"));
  girara_inputbar_command_add(gsession, "bmark",      NULL,   cmd_bookmark_create,     NULL,          _("Add a bookmark"));
  girara_inputbar_command_add(gsession, "bdelete",    NULL,   cmd_bookmark_delete,     cc_bookmarks,  _("Delete a bookmark"));
  girara_inputbar_command_add(gsession, "blist",      NULL,   cmd_bookmark_list,       NULL,          _("List all bookmarks"));
  girara_inputbar_command_add(gsession, "bjump",      NULL,   cmd_bookmark_open,       cc_bookmarks,  _("Jump to bookmark"));
  girara_inputbar_command_add(gsession, "jumplist",   NULL,   cmd_jumplist_list,       NULL,          _("Show recent jumps in jumplist"));
  girara_inputbar_command_add(gsession, "close",      NULL,   cmd_close,               NULL,          _("Close current file"));
  girara_inputbar_command_add(gsession, "info",       NULL,   cmd_info,                NULL,          _("Show file information"));
  girara_inputbar_command_add(gsession, "exec",       NULL,   cmd_exec,                NULL,          _("Execute a command"));
  girara_inputbar_command_add(gsession, "!",          NULL,   cmd_exec,                NULL,          _("Execute a command")); /* like vim */
  girara_inputbar_command_add(gsession, "help",       NULL,   cmd_help,                NULL,          _("Show help"));
  girara_inputbar_command_add(gsession, "open",       "o",    cmd_open,                cc_open,       _("Open document"));
  girara_inputbar_command_add(gsession, "quit",       "q",    cmd_quit,                NULL,          _("Close zathura"));
  girara_inputbar_command_add(gsession, "print",      NULL,   cmd_print,               NULL,          _("Print document"));
  girara_inputbar_command_add(gsession, "save",       NULL,   cmd_save,                cc_write,      _("Save document"));
  girara_inputbar_command_add(gsession, "save!",      NULL,   cmd_savef,               cc_write,      _("Save document (and force overwriting)"));
  girara_inputbar_command_add(gsession, "write",      NULL,   cmd_save,                cc_write,      _("Save document"));
  girara_inputbar_command_add(gsession, "write!",     NULL,   cmd_savef,               cc_write,      _("Save document (and force overwriting)"));
  girara_inputbar_command_add(gsession, "export",     NULL,   cmd_export,              cc_export,     _("Save attachments"));
  girara_inputbar_command_add(gsession, "offset",     NULL,   cmd_offset,              NULL,          _("Set page offset"));
  girara_inputbar_command_add(gsession, "mark",       NULL,   cmd_marks_add,           NULL,          _("Mark current location within the document"));
  girara_inputbar_command_add(gsession, "delmarks",   "delm", cmd_marks_delete,        NULL,          _("Delete the specified marks"));
  girara_inputbar_command_add(gsession, "nohlsearch", "nohl", cmd_nohlsearch,          NULL,          _("Remove highlights of current search results"));
  girara_inputbar_command_add(gsession, "hlsearch",   NULL,   cmd_hlsearch,            NULL,          _("Highlight current search results"));
  girara_inputbar_command_add(gsession, "version",    NULL,   cmd_version,             NULL,          _("Show version information"));
  girara_inputbar_command_add(gsession, "source",     NULL,   cmd_source,              NULL,          _("Source config file"));

  girara_special_command_add(gsession, '/', cmd_search, INCREMENTAL_SEARCH, FORWARD,  NULL);
  girara_special_command_add(gsession, '?', cmd_search, INCREMENTAL_SEARCH, BACKWARD, NULL);

  /* add shortcut mappings */
  girara_shortcut_mapping_add(gsession, "feedkeys",                 girara_sc_feedkeys);
  girara_shortcut_mapping_add(gsession, "focus_inputbar",           girara_sc_focus_inputbar);
  girara_shortcut_mapping_add(gsession, "set",                      girara_sc_set);
  girara_shortcut_mapping_add(gsession, "toggle_inputbar",          girara_sc_toggle_inputbar);
  girara_shortcut_mapping_add(gsession, "toggle_statusbar",         girara_sc_toggle_statusbar);
  girara_shortcut_mapping_add(gsession, "abort",                    sc_abort);
  girara_shortcut_mapping_add(gsession, "adjust_window",            sc_adjust_window);
  girara_shortcut_mapping_add(gsession, "bisect",                   sc_bisect);
  girara_shortcut_mapping_add(gsession, "change_mode",              sc_change_mode);
  girara_shortcut_mapping_add(gsession, "cycle_first_column",       sc_cycle_first_column);
  girara_shortcut_mapping_add(gsession, "display_link",             sc_display_link);
  girara_shortcut_mapping_add(gsession, "copy_link",                sc_copy_link);
  girara_shortcut_mapping_add(gsession, "copy_filepath",            sc_copy_filepath);
  girara_shortcut_mapping_add(gsession, "page_mode",                sc_equal_page_mode);
  girara_shortcut_mapping_add(gsession, "exec",                     sc_exec);
  girara_shortcut_mapping_add(gsession, "focus_inputbar",           sc_focus_inputbar);
  girara_shortcut_mapping_add(gsession, "follow",                   sc_follow);
  girara_shortcut_mapping_add(gsession, "file_chooser",             sc_file_chooser);
  girara_shortcut_mapping_add(gsession, "goto",                     sc_goto);
  girara_shortcut_mapping_add(gsession, "jumplist",                 sc_jumplist);
  girara_shortcut_mapping_add(gsession, "mark_add",                 sc_mark_add);
  girara_shortcut_mapping_add(gsession, "mark_evaluate",            sc_mark_evaluate);
  girara_shortcut_mapping_add(gsession, "navigate",                 sc_navigate);
  girara_shortcut_mapping_add(gsession, "navigate_index",           sc_navigate_index);
  girara_shortcut_mapping_add(gsession, "nohlsearch",               sc_nohlsearch);
  girara_shortcut_mapping_add(gsession, "print",                    sc_print);
  girara_shortcut_mapping_add(gsession, "quit",                     sc_quit);
  girara_shortcut_mapping_add(gsession, "recolor",                  sc_recolor);
  girara_shortcut_mapping_add(gsession, "reload",                   sc_reload);
  girara_shortcut_mapping_add(gsession, "rotate",                   sc_rotate);
  girara_shortcut_mapping_add(gsession, "scroll",                   sc_scroll);
  girara_shortcut_mapping_add(gsession, "search",                   sc_search);
  girara_shortcut_mapping_add(gsession, "snap_to_page",             sc_snap_to_page);
  girara_shortcut_mapping_add(gsession, "toggle_fullscreen",        sc_toggle_fullscreen);
  girara_shortcut_mapping_add(gsession, "toggle_index",             sc_toggle_index);
  girara_shortcut_mapping_add(gsession, "toggle_page_mode",         sc_toggle_page_mode);
  girara_shortcut_mapping_add(gsession, "toggle_presentation",      sc_toggle_presentation);
  girara_shortcut_mapping_add(gsession, "toggle_single_page_mode",  sc_toggle_single_page_mode);
  girara_shortcut_mapping_add(gsession, "zoom",                     sc_zoom);
  girara_shortcut_mapping_add(gsession, "zoom_page",                sc_zoom_page);

  /* add argument mappings */
  girara_argument_mapping_add(gsession, "backward",           BACKWARD);
  girara_argument_mapping_add(gsession, "bottom",             BOTTOM);
  girara_argument_mapping_add(gsession, "default",            DEFAULT);
  girara_argument_mapping_add(gsession, "collapse",           COLLAPSE);
  girara_argument_mapping_add(gsession, "collapse-recursive", COLLAPSE_RECURSIVE);
  girara_argument_mapping_add(gsession, "collapse-all",       COLLAPSE_ALL);
  girara_argument_mapping_add(gsession, "down",               DOWN);
  girara_argument_mapping_add(gsession, "expand",             EXPAND);
  girara_argument_mapping_add(gsession, "expand-recursive",   EXPAND_RECURSIVE);
  girara_argument_mapping_add(gsession, "expand-all",         EXPAND_ALL);
  girara_argument_mapping_add(gsession, "select",             SELECT);
  girara_argument_mapping_add(gsession, "toggle",             TOGGLE);
  girara_argument_mapping_add(gsession, "forward",            FORWARD);
  girara_argument_mapping_add(gsession, "full-down",          FULL_DOWN);
  girara_argument_mapping_add(gsession, "full-up",            FULL_UP);
  girara_argument_mapping_add(gsession, "half-down",          HALF_DOWN);
  girara_argument_mapping_add(gsession, "half-up",            HALF_UP);
  girara_argument_mapping_add(gsession, "partial-down",       PARTIAL_DOWN);
  girara_argument_mapping_add(gsession, "partial-up",         PARTIAL_UP);
  girara_argument_mapping_add(gsession, "full-right",         FULL_RIGHT);
  girara_argument_mapping_add(gsession, "full-left",          FULL_LEFT);
  girara_argument_mapping_add(gsession, "half-right",         HALF_RIGHT);
  girara_argument_mapping_add(gsession, "half-left",          HALF_LEFT);
  girara_argument_mapping_add(gsession, "in",                 ZOOM_IN);
  girara_argument_mapping_add(gsession, "left",               LEFT);
  girara_argument_mapping_add(gsession, "next",               NEXT);
  girara_argument_mapping_add(gsession, "out",                ZOOM_OUT);
  girara_argument_mapping_add(gsession, "page-top",           PAGE_TOP);
  girara_argument_mapping_add(gsession, "page-bottom",        PAGE_BOTTOM);
  girara_argument_mapping_add(gsession, "previous",           PREVIOUS);
  girara_argument_mapping_add(gsession, "right",              RIGHT);
  girara_argument_mapping_add(gsession, "specific",           ZOOM_SPECIFIC);
  girara_argument_mapping_add(gsession, "top",                TOP);
  girara_argument_mapping_add(gsession, "up",                 UP);
  girara_argument_mapping_add(gsession, "best-fit",           ZATHURA_ADJUST_BESTFIT);
  girara_argument_mapping_add(gsession, "width",              ZATHURA_ADJUST_WIDTH);
  girara_argument_mapping_add(gsession, "rotate-cw",          ROTATE_CW);
  girara_argument_mapping_add(gsession, "rotate-ccw",         ROTATE_CCW);
  girara_argument_mapping_add(gsession, "equal_none",         ZATHURA_EQUAL_NONE);
  girara_argument_mapping_add(gsession, "equal_width",        ZATHURA_EQUAL_WIDTH);
  girara_argument_mapping_add(gsession, "equal_height",       ZATHURA_EQUAL_HEIGHT);
  girara_argument_mapping_add(gsession, "smooth-up",          SMOOTH_UP);
  girara_argument_mapping_add(gsession, "smooth-down",        SMOOTH_DOWN);
  /* clang-format on */
}

void config_load_files(zathura_t* zathura) {
  g_return_if_fail(zathura != NULL);

  /* load global configuration files */
  g_autofree char* config_path = girara_get_xdg_path(XDG_CONFIG_DIRS);
  if (config_path != NULL && config_path[0] != '\0') {
    char** config_dirs = g_strsplit(config_path, ":", 0);
    ssize_t size       = g_strv_length(config_dirs) - 1;
    for (; size >= 0; --size) {
      const char* dir       = config_dirs[size];
      g_autofree char* file = g_build_filename(dir, ZATHURA_RC, NULL);
      girara_config_parse(zathura->ui.session, file);
    }
    g_strfreev(config_dirs);
  }

  girara_config_parse(zathura->ui.session, SYSCONFDIR "/" ZATHURA_RC);

  /* load local configuration files */
  g_autofree char* configuration_file = g_build_filename(zathura->config.config_dir, ZATHURA_RC, NULL);
  girara_config_parse(zathura->ui.session, configuration_file);
}
