Keyboard navigation for the index

This patch provides the functionality to browse through the index
via the keyboard hence the mouse is not needed anymore. The original
author of this patch is int3 <jezreel@gmail.com>, in addition there
has been introduced a fix solving problems with named destinations.
This commit is contained in:
Moritz Lipp 2010-04-27 16:07:49 +02:00
parent 273ba0c642
commit e4f80b864b
2 changed files with 143 additions and 43 deletions

View file

@ -68,6 +68,7 @@ Shortcut shortcuts[] = {
{GDK_SHIFT_MASK, GDK_slash, sc_focus_inputbar, NORMAL, { .data = "/" } },
{GDK_SHIFT_MASK, GDK_question, sc_focus_inputbar, NORMAL, { .data = "?" } },
{0, GDK_Tab, sc_toggle_index, NORMAL, {0} },
{0, GDK_Tab, sc_toggle_index, INDEX, {0} },
{0, GDK_J, sc_navigate, NORMAL, { NEXT } },
{0, GDK_K, sc_navigate, NORMAL, { PREVIOUS } },
{GDK_MOD1_MASK, GDK_Right, sc_navigate, NORMAL, { NEXT } },
@ -95,6 +96,12 @@ Shortcut shortcuts[] = {
{0, GDK_a, sc_adjust_window, NORMAL, { ADJUST_BESTFIT } },
{0, GDK_s, sc_adjust_window, NORMAL, { ADJUST_WIDTH } },
{0, GDK_BackSpace, sc_change_buffer, -1, { DELETE_LAST } },
{0, GDK_k, sc_navigate_index, INDEX, { UP } },
{0, GDK_j, sc_navigate_index, INDEX, { DOWN } },
{0, GDK_h, sc_navigate_index, INDEX, { COLLAPSE } },
{0, GDK_l, sc_navigate_index, INDEX, { EXPAND } },
{0, GDK_space, sc_navigate_index, INDEX, { SELECT } },
{0, GDK_Return, sc_navigate_index, INDEX, { SELECT } },
};
/* inputbar shortcuts */

179
zathura.c
View file

@ -27,7 +27,7 @@ enum { NEXT, PREVIOUS, LEFT, RIGHT, UP, DOWN,
ZOOM_IN, ZOOM_OUT, ZOOM_ORIGINAL, ZOOM_SPECIFIC,
FORWARD, BACKWARD, ADJUST_BESTFIT, ADJUST_WIDTH,
ADJUST_NONE, CONTINUOUS, DELETE_LAST, ADD_MARKER,
EVAL_MARKER };
EVAL_MARKER, INDEX, EXPAND, COLLAPSE, SELECT };
/* typedefs */
struct CElement
@ -288,6 +288,7 @@ void sc_recolor(Argument*);
void sc_rotate(Argument*);
void sc_scroll(Argument*);
void sc_search(Argument*);
void sc_navigate_index(Argument*);
void sc_toggle_index(Argument*);
void sc_toggle_inputbar(Argument*);
void sc_toggle_statusbar(Argument*);
@ -333,7 +334,7 @@ gboolean scmd_search(char*, Argument*);
/* callback declarations */
gboolean cb_destroy(GtkWidget*, gpointer);
gboolean cb_draw(GtkWidget*, GdkEventExpose*, gpointer);
gboolean cb_index_selection_changed(GtkTreeSelection*, GtkWidget*);
gboolean cb_index_row_activated(GtkTreeView*, GtkTreePath*, GtkTreeViewColumn*, gpointer);
gboolean cb_inputbar_kb_pressed(GtkWidget*, GdkEventKey*, gpointer);
gboolean cb_inputbar_activate(GtkEntry*, gpointer);
gboolean cb_inputbar_form_activate(GtkEntry*, gpointer);
@ -1089,10 +1090,10 @@ search(void* parameter)
g_static_mutex_lock(&(Zathura.Lock.document_lock));
if(!Zathura.PDF.document || !search_item || !strlen(search_item))
{
g_static_mutex_lock(&(Zathura.Lock.document_lock));
g_static_mutex_lock(&(Zathura.Lock.search_lock));
Zathura.Thread.search_thread_running = FALSE;
g_static_mutex_unlock(&(Zathura.Lock.search_lock));
g_static_mutex_lock(&(Zathura.Lock.document_lock));
g_thread_exit(NULL);
}
g_static_mutex_unlock(&(Zathura.Lock.document_lock));
@ -1101,6 +1102,9 @@ search(void* parameter)
if(argument->n)
direction = (argument->n == BACKWARD) ? -1 : 1;
int number_of_pages = Zathura.PDF.number_of_pages;
int page_number = Zathura.PDF.page_number;
for(page_counter = 1; page_counter <= Zathura.PDF.number_of_pages; page_counter++)
{
g_static_mutex_lock(&(Zathura.Lock.search_lock));
@ -1111,9 +1115,8 @@ search(void* parameter)
}
g_static_mutex_unlock(&(Zathura.Lock.search_lock));
// probably should wrap this in a mutex, but I'm lazy
next_page = (Zathura.PDF.number_of_pages + Zathura.PDF.page_number +
page_counter * direction) % Zathura.PDF.number_of_pages;
next_page = (number_of_pages + page_number +
page_counter * direction) % number_of_pages;
g_static_mutex_lock(&(Zathura.Lock.pdflib_lock));
PopplerPage* page = poppler_document_get_page(Zathura.PDF.document, next_page);
@ -1152,7 +1155,7 @@ search(void* parameter)
g_static_mutex_unlock(&(Zathura.Lock.search_lock));
g_thread_exit(NULL);
return NULL; // suppress GCC warnings about return value
return NULL;
}
void*
@ -1431,6 +1434,124 @@ sc_search(Argument* argument)
g_static_mutex_unlock(&(Zathura.Lock.search_lock));
}
gboolean cb_index_row_activated(GtkTreeView* treeview, GtkTreePath* path,
GtkTreeViewColumn* column, gpointer user_data)
{
GtkTreeModel *model;
GtkTreeIter iter;
g_object_get(treeview, "model", &model, NULL);
if(gtk_tree_model_get_iter(model, &iter, path))
{
PopplerAction* action;
PopplerDest* destination;
gtk_tree_model_get(model, &iter, 1, &action, -1);
if(!action)
return TRUE;
if(action->type == POPPLER_ACTION_GOTO_DEST)
{
destination = action->goto_dest.dest;
int page_number = destination->page_num;
if(action->goto_dest.dest->type == POPPLER_DEST_NAMED)
{
PopplerDest* d = poppler_document_find_dest(Zathura.PDF.document, action->goto_dest.dest->named_dest);
if(d)
{
page_number = d->page_num;
poppler_dest_free(d);
}
}
set_page(page_number - 1);
update_status();
gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
}
}
Zathura.Global.mode = NORMAL;
g_object_unref(model);
return TRUE;
}
void
sc_navigate_index(Argument* argument)
{
GtkTreeView *treeview = gtk_container_get_children(GTK_CONTAINER(Zathura.UI.index))->data;
GtkTreePath *path;
gtk_tree_view_get_cursor(treeview, &path, NULL);
if(!path)
return;
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
GtkTreeIter iter;
GtkTreeIter child_iter;
gboolean is_valid_path = TRUE;
switch(argument->n)
{
case UP:
if(!gtk_tree_path_prev(path))
is_valid_path = gtk_tree_path_up(path);
else /* row above */
{
while(gtk_tree_view_row_expanded(treeview, 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(treeview, path)
&& gtk_tree_path_get_depth(path) > 1)
{
gtk_tree_path_up(path);
gtk_tree_view_collapse_row(treeview, path);
}
break;
case DOWN:
if(gtk_tree_view_row_expanded(treeview, path))
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(treeview, path, FALSE))
gtk_tree_path_down(path);
break;
case SELECT:
cb_index_row_activated(treeview, path, NULL, NULL);
return;
}
if (is_valid_path)
gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
gtk_tree_path_free(path);
}
void
sc_toggle_index(Argument* argument)
{
@ -1440,7 +1561,6 @@ sc_toggle_index(Argument* argument)
GtkWidget *treeview;
GtkTreeModel *model;
GtkCellRenderer *renderer;
GtkTreeSelection *selection;
PopplerIndexIter *iter;
if(!Zathura.UI.index)
@ -1473,9 +1593,8 @@ sc_toggle_index(Argument* argument)
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);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_select_path(selection, gtk_tree_path_new_first());
g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(cb_index_selection_changed), NULL);
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), NULL);
gtk_container_add (GTK_CONTAINER (Zathura.UI.index), treeview);
gtk_widget_show (treeview);
@ -1484,9 +1603,15 @@ sc_toggle_index(Argument* argument)
static gboolean show = FALSE;
if(!show)
{
switch_view(Zathura.UI.index);
Zathura.Global.mode = INDEX;
}
else
{
switch_view(Zathura.UI.drawing_area);
Zathura.Global.mode = NORMAL;
}
show = !show;
}
@ -2783,38 +2908,6 @@ gboolean cb_draw(GtkWidget* widget, GdkEventExpose* expose, gpointer data)
return TRUE;
}
gboolean
cb_index_selection_changed(GtkTreeSelection* treeselection, GtkWidget* action_view)
{
GtkTreeModel *model;
GtkTreeIter iter;
if(gtk_tree_selection_get_selected(treeselection, &model, &iter))
{
PopplerAction* action;
PopplerDest* destination;
gtk_tree_model_get(model, &iter, 1, &action, -1);
if(!action)
return TRUE;
if(action->type == POPPLER_ACTION_GOTO_DEST)
{
destination = action->goto_dest.dest;
int page_number = destination->page_num;
if(page_number >= 0 && page_number <= Zathura.PDF.number_of_pages)
{
set_page(page_number - 1);
update_status();
gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
}
}
}
return TRUE;
}
gboolean
cb_inputbar_kb_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
{