/* See LICENSE file for license and copyright information */

#include <girara/session.h>
#include <girara/settings.h>
#include <girara/datastructures.h>
#include <girara/shortcuts.h>
#include <girara/utils.h>
#include <gtk/gtk.h>
#include <libgen.h>
#include <glib/gi18n.h>

#include "callbacks.h"
#include "shortcuts.h"
#include "document.h"
#include "zathura.h"
#include "render.h"
#include "utils.h"
#include "page.h"
#include "page-widget.h"

static void
readjust_view_after_zooming(zathura_t *zathura, float old_zoom) {
  if (zathura == NULL || zathura->document == NULL) {
    return;
  }

  GtkScrolledWindow *window = GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view);
  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(window);
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(window);

  double scale = zathura_document_get_scale(zathura->document);
  gdouble valx = gtk_adjustment_get_value(hadjustment) / old_zoom * scale;
  gdouble valy = gtk_adjustment_get_value(vadjustment) / old_zoom * scale;
  set_adjustment(hadjustment, valx);
  set_adjustment(vadjustment, valy);
}

bool
sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument),
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->document != NULL) {
    unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
    for (unsigned int page_id = 0; page_id < number_of_pages; ++page_id) {
      zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
      if (page == NULL) {
        continue;
      }

      g_object_set(zathura_page_get_widget(zathura, page), "draw-links", FALSE, NULL);
    }
  }

  girara_mode_set(session, session->modes.normal);
  girara_sc_abort(session, NULL, NULL, 0);

  return false;
}

bool
sc_adjust_window(girara_session_t* session, girara_argument_t* argument,
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);

  unsigned int pages_per_row = 1;
  girara_setting_get(session, "pages-per-row", &pages_per_row);

  if (zathura->ui.page_widget == NULL || zathura->document == NULL) {
    goto error_ret;
  }

  float old_zoom = zathura_document_get_scale(zathura->document);
  zathura_document_set_adjust_mode(zathura->document, argument->n);
  if (argument->n == ZATHURA_ADJUST_NONE) {
    /* there is nothing todo */
    goto error_ret;
  }

  /* get window size */
  GtkAllocation allocation;
  gtk_widget_get_allocation(session->gtk.view, &allocation);
  gint width  = allocation.width;
  gint height = allocation.height;

  /* calculate total width and max-height */
  double total_width  = 0;
  double total_height = 0;
  double max_height   = 0;
  double max_width    = 0;

  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  for (unsigned int page_id = 0; page_id < pages_per_row; page_id++) {
    if (page_id == number_of_pages) {
      break;
    }

    zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
    if (page == NULL) {
      goto error_ret;
    }

    unsigned int page_height = zathura_page_get_height(page);
    unsigned int page_width  = zathura_page_get_width(page);

    if (page_height > max_height) {
      max_height = page_height;
    }

    if (page_width > max_width) {
      max_width = page_width;
    }

    total_width  += page_width;
    total_height += page_height;
  }

  unsigned int rotation = zathura_document_get_rotation(zathura->document);
  if (argument->n == ZATHURA_ADJUST_WIDTH) {
    if (rotation == 0 || rotation == 180) {
      zathura_document_set_scale(zathura->document, width / total_width);
    } else {
      zathura_document_set_scale(zathura->document, width / total_height);
    }
  } else if (argument->n == ZATHURA_ADJUST_BESTFIT) {
    if (rotation == 0 || rotation == 180) {
      zathura_document_set_scale(zathura->document, height / max_height);
    } else {
      zathura_document_set_scale(zathura->document, width / total_height);
    }
  } else {
    goto error_ret;
  }

  /* keep position */
  readjust_view_after_zooming(zathura, old_zoom);

  /* re-render all pages */
  render_all(zathura);

error_ret:

  return false;
}

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

  girara_mode_set(session, argument->n);

  return false;
}

bool
sc_focus_inputbar(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);

  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
    gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
  }

  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
    gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
  }

  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));

  if (argument->data != NULL) {
    gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);

    /* append filepath */
    if (argument->n == APPEND_FILEPATH && zathura->document != NULL) {
      const char* file_path = zathura_document_get_path(zathura->document);
      if (file_path == NULL) {
        return false;
      }

      char* path = dirname(g_strdup(file_path));
      char* tmp  = g_strdup_printf("%s%s/", (char*) argument->data, (g_strcmp0(path, "/") == 0) ? "" : path);

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

      gtk_entry_set_text(session->gtk.inputbar_entry, tmp);
      g_free(tmp);
    }

    /* 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));

    gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);

    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
sc_follow(girara_session_t* session, girara_argument_t* UNUSED(argument),
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->document == NULL || zathura->ui.session == NULL) {
    return false;
  }

  /* set pages to draw links */
  bool show_links = false;
  unsigned int page_offset = 0;
  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
    if (page == NULL) {
      continue;
    }

    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
    g_object_set(page_widget, "search-results", NULL, NULL);
    if (zathura_page_get_visibility(page) == true) {
      g_object_set(page_widget, "draw-links", TRUE, NULL);

      int number_of_links = 0;
      g_object_get(page_widget, "number-of-links", &number_of_links, NULL);
      if (number_of_links != 0) {
        show_links = true;
      }
      g_object_set(page_widget, "offset-links", page_offset, NULL);
      page_offset += number_of_links;
    } else {
      g_object_set(page_widget, "draw-links", FALSE, NULL);
    }
  }

  /* ask for input */
  if (show_links == true) {
    girara_dialog(zathura->ui.session, "Follow link:", FALSE, NULL, (girara_callback_inputbar_activate_t) cb_sc_follow, zathura->ui.session);
  }

  return false;
}

bool
sc_goto(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

  if (t != 0) {
    /* add offset */
    unsigned int page_offset = zathura_document_get_page_offset(zathura->document);
    if (page_offset > 0) {
      t += page_offset;
    }

    page_set(zathura, t - 1);
  } else if (argument->n == TOP) {
    page_set(zathura, 0);
  } else if (argument->n == BOTTOM) {
    page_set(zathura, zathura_document_get_number_of_pages(zathura->document) - 1);
  }

  return false;
}

bool
sc_mouse_scroll(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(event != NULL, false);

  if (zathura->document == NULL) {
    return false;
  }

  static int x = 0;
  static int y = 0;

  GtkAdjustment* x_adj = NULL;
  GtkAdjustment* y_adj = NULL;

  switch (event->type) {
    /* scroll */
    case GIRARA_EVENT_SCROLL_UP:
    case GIRARA_EVENT_SCROLL_DOWN:
    case GIRARA_EVENT_SCROLL_LEFT:
    case GIRARA_EVENT_SCROLL_RIGHT:
      return sc_scroll(session, argument, NULL, t);

    /* drag */
    case GIRARA_EVENT_BUTTON_PRESS:
      x = event->x;
      y = event->y;
      break;
    case GIRARA_EVENT_BUTTON_RELEASE:
      x = 0;
      y = 0;
      break;
    case GIRARA_EVENT_MOTION_NOTIFY:
      x_adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
      y_adj =gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));

      if (x_adj == NULL || y_adj == NULL) {
        return false;
      }

      set_adjustment(x_adj, gtk_adjustment_get_value(x_adj) - (event->x - x));
      set_adjustment(y_adj, gtk_adjustment_get_value(y_adj) - (event->y - y));

      x = event->x;
      y = event->y;
      break;

    /* unhandled events */
    default:
      break;
  }

  return false;
}

bool
sc_mouse_zoom(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(event != NULL, false);

  if (zathura->document == NULL) {
    return false;
  }

  /* scroll event */
  switch (event->type) {
    case GIRARA_EVENT_SCROLL_UP:
      argument->n = ZOOM_IN;
      break;
    case GIRARA_EVENT_SCROLL_DOWN:
      argument->n = ZOOM_OUT;
      break;
    default:
      return false;
  }

  return sc_zoom(session, argument, NULL, t);
}

bool
sc_navigate(girara_session_t* session, girara_argument_t* argument,
    girara_event_t* UNUSED(event), unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

  int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  int new_page        = zathura_document_get_current_page_number(zathura->document);

  bool scroll_wrap = false;
  girara_setting_get(session, "scroll-wrap", &scroll_wrap);

  bool columns_per_row_offset = false;
  girara_setting_get(session, "advance-pages-per-row", &columns_per_row_offset);

  int offset = 1;
  if (columns_per_row_offset == true) {
    girara_setting_get(session, "pages-per-row", &offset);
  }

  t = (t == 0) ? offset : t;
  if (argument->n == NEXT) {
    if (scroll_wrap == true) {
      new_page = new_page + t;
    } else {
      new_page = (new_page + t) % number_of_pages;
    }
  } else if (argument->n == PREVIOUS) {
    if (scroll_wrap == true) {
      new_page = new_page - t;
    } else {
      new_page = (new_page + number_of_pages - t) % number_of_pages;
    }
  }

  if (scroll_wrap == true && (new_page < 0 || new_page >= number_of_pages)) {
    return false;
  }

  page_set(zathura, new_page);

  return false;
}

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

  bool value = false;
  girara_setting_get(session, "recolor", &value);
  value = !value;
  girara_setting_set(session, "recolor", &value);

  return false;
}

bool
sc_reload(girara_session_t* session, girara_argument_t* UNUSED(argument),
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->file_monitor.file_path == NULL) {
    return false;
  }

  /* close current document */
  document_close(zathura, true);

  /* reopen document */
  document_open(zathura, zathura->file_monitor.file_path, zathura->file_monitor.password);

  return false;
}

bool
sc_rotate(girara_session_t* session, girara_argument_t* argument,
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(zathura->document != NULL, false);

  unsigned int page_number = zathura_document_get_current_page_number(zathura->document);

  int angle = 90;
  if (argument != NULL && argument->n == ROTATE_CCW) {
    angle = 270;
  }

  /* update rotate value */
  unsigned int rotation = zathura_document_get_rotation(zathura->document);
  zathura_document_set_rotation(zathura->document, (rotation + angle) % 360);

  /* update scale */
  girara_argument_t new_argument = { zathura_document_get_adjust_mode(zathura->document), NULL };
  sc_adjust_window(zathura->ui.session, &new_argument, NULL, 0);

  /* render all pages again */
  render_all(zathura);

  page_set_delayed(zathura, page_number);

  return false;
}

bool
sc_scroll(girara_session_t* session, girara_argument_t* argument,
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  if (zathura->document == NULL) {
    return false;
  }

  GtkAdjustment* adjustment = NULL;
  if ( (argument->n == LEFT) || (argument->n == FULL_LEFT) || (argument->n == HALF_LEFT) ||
  (argument->n == RIGHT) || (argument->n == FULL_RIGHT) || (argument->n == HALF_RIGHT)) {
    adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
  } else {
    adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
  }


  gdouble view_size                  = gtk_adjustment_get_page_size(adjustment);
  gdouble value                      = gtk_adjustment_get_value(adjustment);
  gdouble max                        = gtk_adjustment_get_upper(adjustment) - view_size;
  zathura->global.update_page_number = true;

  float scroll_step = 40;
  girara_setting_get(session, "scroll-step", &scroll_step);
  int padding = 1;
  girara_setting_get(session, "page-padding", &padding);

  gdouble new_value;

  switch(argument->n) {
    case FULL_UP:
    case FULL_LEFT:
      new_value = value - view_size - padding;
      break;
    case FULL_DOWN:
    case FULL_RIGHT:
      new_value = value + view_size + padding;
      break;
    case HALF_UP:
    case HALF_LEFT:
      new_value = value - ((view_size + padding) / 2);
      break;
    case HALF_DOWN:
    case HALF_RIGHT:
      new_value = value + ((view_size + padding) / 2);
      break;
    case LEFT:
    case UP:
      new_value = value - scroll_step;
      break;
    case RIGHT:
    case DOWN:
      new_value = value + scroll_step;
      break;
    case TOP:
      new_value = 0;
      break;
    case BOTTOM:
      new_value = max;
      break;
    default:
      new_value = value;
  }

  set_adjustment(adjustment, new_value);

  return false;
}

bool
sc_search(girara_session_t* session, girara_argument_t* argument,
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

  const int num_pages = zathura_document_get_number_of_pages(zathura->document);
  const int cur_page  = zathura_document_get_current_page_number(zathura->document);
  int diff = argument->n == FORWARD ? 1 : -1;

  zathura_page_t* target_page = NULL;
  int target_idx = 0;

  for (int page_id = 0; page_id < num_pages; ++page_id) {
    int tmp = cur_page + diff * page_id;
    zathura_page_t* page = zathura_document_get_page(zathura->document, (tmp + num_pages) % num_pages);
    if (page == NULL) {
      continue;
    }

    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);

    int num_search_results = 0, current = -1;
    g_object_get(page_widget, "search-current", &current,
        "search-length", &num_search_results, NULL);
    if (num_search_results == 0 || current == -1) {
      continue;
    }

    if (diff == 1 && current < num_search_results - 1) {
      /* the next result is on the same page */
      target_page = page;
      target_idx = current + 1;
    } else if (diff == -1 && current > 0) {
      target_page = page;
      target_idx = current - 1;
    } else {
      /* the next result is on a different page */
      g_object_set(page_widget, "search-current", -1, NULL);

      for (int npage_id = 1; page_id < num_pages; ++npage_id) {
        int ntmp = cur_page + diff * (page_id + npage_id);
        zathura_page_t* npage = zathura_document_get_page(zathura->document, (ntmp + 2*num_pages) % num_pages);
        zathura_document_set_current_page_number(zathura->document, zathura_page_get_index(npage));
        GtkWidget* npage_page_widget = zathura_page_get_widget(zathura, npage);
        g_object_get(npage_page_widget, "search-length", &num_search_results, NULL);
        if (num_search_results != 0) {
          target_page = npage;
          target_idx = diff == 1 ? 0 : num_search_results - 1;
          break;
        }
      }
    }

    break;
  }

  if (target_page != NULL) {
    girara_list_t* results = NULL;
    GtkWidget* page_widget = zathura_page_get_widget(zathura, target_page);
    g_object_set(page_widget, "search-current", target_idx, NULL);
    g_object_get(page_widget, "search-results", &results, NULL);

    zathura_rectangle_t* rect = girara_list_nth(results, target_idx);
    zathura_rectangle_t rectangle = recalc_rectangle(target_page, *rect);
    page_offset_t offset;
    page_calculate_offset(zathura, target_page, &offset);

    GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
    GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));

    int x = offset.x - gtk_adjustment_get_page_size(view_hadjustment) / 2 + rectangle.x1;
    int y = offset.y - gtk_adjustment_get_page_size(view_vadjustment) / 2 + rectangle.y1;
    set_adjustment(view_hadjustment, x);
    set_adjustment(view_vadjustment, y);
  }

  return false;
}

bool
sc_navigate_index(girara_session_t* session, girara_argument_t* argument,
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

  if(zathura->ui.index == NULL) {
    return false;
  }

  GtkTreeView *tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.index))->data;
  GtkTreePath *path;

  gtk_tree_view_get_cursor(tree_view, &path, NULL);
  if (path == NULL) {
    return false;
  }

  GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
  GtkTreeIter   iter;
  GtkTreeIter   child_iter;

  gboolean is_valid_path = TRUE;

  switch(argument->n) {
    case UP:
      if (gtk_tree_path_prev(path) == FALSE) {
        is_valid_path = gtk_tree_path_up(path);
      } else { /* row above */
        while(gtk_tree_view_row_expanded(tree_view, path)) {
          gtk_tree_model_get_iter(model, &iter, path);
          /* select last child */
          gtk_tree_model_iter_nth_child(model, &child_iter, &iter,
            gtk_tree_model_iter_n_children(model, &iter)-1);
          gtk_tree_path_free(path);
          path = gtk_tree_model_get_path(model, &child_iter);
        }
      }
      break;
    case COLLAPSE:
      if (gtk_tree_view_collapse_row(tree_view, path) == FALSE
        && gtk_tree_path_get_depth(path) > 1) {
        gtk_tree_path_up(path);
        gtk_tree_view_collapse_row(tree_view, path);
      }
      break;
    case DOWN:
      if (gtk_tree_view_row_expanded(tree_view, path) == TRUE) {
        gtk_tree_path_down(path);
      } else {
        do {
          gtk_tree_model_get_iter(model, &iter, path);
          if (gtk_tree_model_iter_next(model, &iter)) {
            path = gtk_tree_model_get_path(model, &iter);
            break;
          }
        } while((is_valid_path = (gtk_tree_path_get_depth(path) > 1))
          && gtk_tree_path_up(path));
      }
      break;
    case EXPAND:
      if (gtk_tree_view_expand_row(tree_view, path, FALSE)) {
        gtk_tree_path_down(path);
      }
      break;
    case EXPAND_ALL:
      gtk_tree_view_expand_all(tree_view);
      break;
    case COLLAPSE_ALL:
      gtk_tree_view_collapse_all(tree_view);
      gtk_tree_path_free(path);
      path = gtk_tree_path_new_first();
      gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE);
      break;
    case SELECT:
      cb_index_row_activated(tree_view, path, NULL, zathura);
      return false;
  }

  if (is_valid_path) {
    gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE);
  }

  gtk_tree_path_free(path);

  return false;
}

bool
sc_toggle_index(girara_session_t* session, girara_argument_t* UNUSED(argument),
    girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  if (zathura->document == NULL) {
    return false;
  }

  girara_tree_node_t* document_index = NULL;
  GtkWidget* treeview                = NULL;
  GtkTreeModel* model                = NULL;
  GtkCellRenderer* renderer          = NULL;
  GtkCellRenderer* renderer2         = NULL;

  if (zathura->ui.index == NULL) {
    /* create new index widget */
    zathura->ui.index = gtk_scrolled_window_new(NULL, NULL);

    if (zathura->ui.index == NULL) {
      goto error_ret;
    }

    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(zathura->ui.index),
      GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    /* create index */
    document_index = zathura_document_index_generate(zathura->document, NULL);
    if (document_index == NULL) {
      girara_notify(session, GIRARA_WARNING, _("This document does not contain any index"));
      goto error_free;
    }

    model = GTK_TREE_MODEL(gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER));
    if (model == NULL) {
      goto error_free;
    }

    treeview = gtk_tree_view_new_with_model(model);
    if (treeview == NULL) {
      goto error_free;
    }

    g_object_unref(model);

    renderer = gtk_cell_renderer_text_new();
    if (renderer == NULL) {
      goto error_free;
    }

    renderer2 = gtk_cell_renderer_text_new();
    if (renderer2 == NULL) {
      goto error_free;
    }

    document_index_build(model, NULL, document_index);
    girara_node_free(document_index);

    /* setup widget */
    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (treeview), 0, "Title", renderer, "markup", 0, NULL);
    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (treeview), 1, "Target", renderer2, "text", 1, NULL);

    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
    g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 0)), "expand", TRUE, NULL);
    gtk_tree_view_column_set_alignment(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 1), 1.0f);
    gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), gtk_tree_path_new_first(), NULL, FALSE);
    g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(cb_index_row_activated), zathura);

    gtk_container_add(GTK_CONTAINER(zathura->ui.index), treeview);
    gtk_widget_show(treeview);
  }

  GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
  GtkAdjustment* hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
  static double vvalue = 0;
  static double hvalue = 0;

  if (gtk_widget_get_visible(GTK_WIDGET(zathura->ui.index))) {
    girara_set_view(session, zathura->ui.page_widget_alignment);
    gtk_widget_hide(GTK_WIDGET(zathura->ui.index));
    girara_mode_set(zathura->ui.session, zathura->modes.normal);

    /* reset adjustment */
    gtk_adjustment_set_value(vadjustment, vvalue);
    gtk_adjustment_set_value(hadjustment, hvalue);

    gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view), vadjustment);
    gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view), hadjustment);
  } else {
    /* save adjustment */
    vvalue = gtk_adjustment_get_value(vadjustment);
    hvalue = gtk_adjustment_get_value(hadjustment);

    girara_set_view(session, zathura->ui.index);
    gtk_widget_show(GTK_WIDGET(zathura->ui.index));
    girara_mode_set(zathura->ui.session, zathura->modes.index);
  }

  return false;

error_free:

  if (zathura->ui.index != NULL) {
    g_object_ref_sink(zathura->ui.index);
    zathura->ui.index = NULL;
  }

  if (document_index != NULL) {
    girara_node_free(document_index);
  }

error_ret:

  return false;
}

bool
sc_toggle_fullscreen(girara_session_t* session, girara_argument_t*
    UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->document == NULL) {
    girara_notify(session, GIRARA_WARNING, _("No document opened."));
    return false;
  }

  static bool fullscreen = false;
  static int pages_per_row = 1;
  static double zoom = 1.0;

  if (fullscreen == true) {
    /* reset pages per row */
    girara_setting_set(session, "pages-per-row", &pages_per_row);

    /* show status bar */
    gtk_widget_show(GTK_WIDGET(session->gtk.statusbar));

    /* set full screen */
    gtk_window_unfullscreen(GTK_WINDOW(session->gtk.window));

    /* reset scale */
    zathura_document_set_scale(zathura->document, zoom);
    render_all(zathura);
    page_set_delayed(zathura, zathura_document_get_current_page_number(zathura->document));
  } else {
    /* backup pages per row */
    girara_setting_get(session, "pages-per-row", &pages_per_row);

    /* set single view */
    int int_value = 1;
    girara_setting_set(session, "pages-per-row", &int_value);

    /* back up zoom */
    zoom = zathura_document_get_scale(zathura->document);

    /* adjust window */
    girara_argument_t argument = { ZATHURA_ADJUST_BESTFIT, NULL };
    sc_adjust_window(session, &argument, NULL, 0);

    /* hide status and inputbar */
    gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
    gtk_widget_hide(GTK_WIDGET(session->gtk.statusbar));

    /* set full screen */
    gtk_window_fullscreen(GTK_WINDOW(session->gtk.window));
    page_set_delayed(zathura, zathura_document_get_current_page_number(zathura->document));
  }

  fullscreen = fullscreen ? false : true;

  return false;
}

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

  girara_argument_t arg = { GIRARA_HIDE, NULL };
  girara_isc_completion(session, &arg, NULL, 0);

  cb_destroy(NULL, NULL);

  return false;
}

bool
sc_zoom(girara_session_t* session, girara_argument_t* argument, girara_event_t*
    UNUSED(event), unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

  zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_NONE);

  /* retreive zoom step value */
  int value = 1;
  girara_setting_get(zathura->ui.session, "zoom-step", &value);

  t = (t == 0) ? 1 : t;
  float zoom_step = value / 100.0f * t;
  float old_zoom = zathura_document_get_scale(zathura->document);

  /* specify new zoom value */
  if (argument->n == ZOOM_IN) {
    zathura_document_set_scale(zathura->document, old_zoom + zoom_step);
  } else if (argument->n == ZOOM_OUT) {
    zathura_document_set_scale(zathura->document, old_zoom - zoom_step);
  } else if (argument->n == ZOOM_SPECIFIC) {
    zathura_document_set_scale(zathura->document, t / 100.0f);
  } else {
    zathura_document_set_scale(zathura->document, 1.0);
  }

  /* zoom limitations */
  int zoom_min_int = 10;
  int zoom_max_int = 1000;
  girara_setting_get(session, "zoom-min", &zoom_min_int);
  girara_setting_get(session, "zoom-max", &zoom_max_int);

  float zoom_min = zoom_min_int * 0.01f;
  float zoom_max = zoom_max_int * 0.01f;

  float scale = zathura_document_get_scale(zathura->document);
  if (scale < zoom_min) {
    zathura_document_set_scale(zathura->document, zoom_min);
  } else if (scale > zoom_max) {
    zathura_document_set_scale(zathura->document, zoom_max);
  }

  /* keep position */
  readjust_view_after_zooming(zathura, old_zoom);

  render_all(zathura);

  return false;
}