diff --git a/zathura/callbacks.c b/zathura/callbacks.c index bd70efae..6cb6f13d 100644 --- a/zathura/callbacks.c +++ b/zathura/callbacks.c @@ -23,6 +23,8 @@ #include "adjustment.h" #include "synctex.h" #include "dbus-interface.h" +#include "zathura/commands.h" +#include "zathura/types.h" gboolean cb_destroy(GtkWidget* UNUSED(widget), zathura_t* zathura) { if (zathura_has_document(zathura) == true) { @@ -334,19 +336,46 @@ void cb_index_row_activated(GtkTreeView* tree_view, GtkTreePath* path, GtkTreeVi GtkTreeIter iter; g_object_get(G_OBJECT(tree_view), "model", &model, NULL); - if (gtk_tree_model_get_iter(model, &iter, path)) { zathura_index_element_t* index_element; gtk_tree_model_get(model, &iter, 3, &index_element, -1); - if (index_element == NULL) { return; } sc_toggle_index(zathura->ui.session, NULL, NULL, 0); + zathura_link_evaluate(zathura, index_element->link); } + g_object_unref(model); +} + +void cb_explorer_row_activated(girara_session_t* session, GtkTreeView* tree_view, GtkTreePath* path, GtkTreeViewColumn* UNUSED(column), void* data){ + zathura_t* zathura = data; + if (tree_view == NULL || zathura == NULL || zathura->ui.session == NULL){ + return; + } + + GtkTreeModel* model; + GtkTreeIter iter; + + g_object_get(G_OBJECT(tree_view), "model", &model, NULL); + if (gtk_tree_model_get_iter(model, &iter, path)){ + zathura_explorer_element_s* explorer_element; + gtk_tree_model_get(model, &iter, 3, &explorer_element, -1); + if(explorer_element == NULL){ + return; + } + if(explorer_element->type == ZATHURA_EXPLORER_TYPE_DIR || explorer_element->type == ZATHURA_EXPLORER_TYPE_FILE_INVALID){ + return; + } + zathura_link_target_t target = zathura_link_get_target(explorer_element->link); + document_open_idle(session->global.data, target.value, NULL, 0, NULL, NULL, NULL, NULL); + } + + zathura->ui.file_explorer = NULL; + zathura->ui.index = NULL; g_object_unref(model); } diff --git a/zathura/callbacks.h b/zathura/callbacks.h index 9d774f69..380a3a8a 100644 --- a/zathura/callbacks.h +++ b/zathura/callbacks.h @@ -155,6 +155,16 @@ void cb_page_layout_value_changed(girara_session_t* session, const char* name, g */ void cb_index_row_activated(GtkTreeView* tree_view, GtkTreePath* path, GtkTreeViewColumn* column, void* zathura); +/** + * Called when an explorer element is activated (e.g.: double click) + * + * @param tree_view Tree view + * @param path Path + * @param column Column + * @param zathura Zathura session + */ +void cb_explorer_row_activated(girara_session_t* session, GtkTreeView* tree_view, GtkTreePath* path, GtkTreeViewColumn* column, void* zathura); + /** * Called when input has been passed to the sc_follow dialog * diff --git a/zathura/commands.c b/zathura/commands.c index 9f16e57e..b9b6dcf7 100644 --- a/zathura/commands.c +++ b/zathura/commands.c @@ -20,12 +20,14 @@ #include "shortcuts.h" #include "utils.h" #include "zathura.h" +#include "callbacks.h" #include #include #include #include #include +#include bool cmd_bookmark_create(girara_session_t* session, girara_list_t* argument_list) { g_return_val_if_fail(session != NULL, false); @@ -682,3 +684,109 @@ bool cmd_source(girara_session_t* session, girara_list_t* argument_list) { return true; } + +bool cmd_toggle_explorer(girara_session_t* session, girara_list_t* UNUSED(argument_list)){ + g_return_val_if_fail(session != NULL, false); + g_return_val_if_fail(session-> global.data != NULL, false); + zathura_t* zathura = session->global.data; + + girara_tree_node_t* explorer = NULL; + GtkWidget* treeview = NULL; + GtkTreeModel* model = NULL; + GtkCellRenderer* renderer = NULL; + GtkCellRenderer* renderer2 = NULL; + + + if (zathura->ui.file_explorer == NULL){ + /* create new file explorer widget */ + zathura->ui.file_explorer = gtk_scrolled_window_new(NULL, NULL); + + if (zathura->ui.file_explorer == NULL){ + return false; + } + + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(zathura->ui.file_explorer), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + /* create file explorer */ + explorer = zathura_explorer_generate(session, NULL); + if(explorer == NULL){ + girara_notify(session, GIRARA_WARNING, _("Unable to retrieve the file explorer")); + goto error_free; + } + + model = GTK_TREE_MODEL(gtk_tree_store_new(4, G_TYPE_STRING, 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; + } + + gtk_style_context_add_class(gtk_widget_get_style_context(treeview), "indexmode"); + + 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; + } + + file_explorer_build(session, model, NULL, explorer); + girara_node_free(explorer); + + /* 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), 2, "(alt)", renderer2, "text", 2, 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_search_equal_func(GTK_TREE_VIEW(treeview), search_equal_func_index, treeview, NULL); + gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE); + g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(cb_index_row_activated), zathura); + + gtk_container_add(GTK_CONTAINER(zathura->ui.file_explorer), treeview); + } + + if (zathura->ui.file_explorer && gtk_widget_get_visible(GTK_WIDGET(zathura->ui.file_explorer))){ + /* toggle off */ + } else{ + /* save document position in jumplist */ + zathura_jumplist_add(zathura); + + const zathura_adjust_mode_t adjust_mode = zathura_document_get_adjust_mode(zathura->document); + + if (adjust_mode == ZATHURA_ADJUST_INPUTBAR) { + zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_NONE); + } + + /* set the view */ + girara_set_view(session, zathura->ui.file_explorer); + GtkTreeView* tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.file_explorer))->data; + gtk_widget_grab_focus(GTK_WIDGET(tree_view)); + girara_mode_set(zathura->ui.session, zathura->modes.index); + } + + return true; + + error_free: + + if(zathura->ui.file_explorer != NULL){ + g_object_ref_sink(zathura->ui.file_explorer); + zathura->ui.file_explorer = NULL; + } + + if(explorer != NULL){ + girara_node_free(explorer); + } + + return true; +} diff --git a/zathura/commands.h b/zathura/commands.h index 7c1b14c7..12451c39 100644 --- a/zathura/commands.h +++ b/zathura/commands.h @@ -196,4 +196,15 @@ bool cmd_version(girara_session_t* session, girara_list_t* argument_list); */ bool cmd_source(girara_session_t* session, girara_list_t* argument_list); +/** + * Toggles the file explorer + * + * @param session The used girara session + * @param argument_list List of passed arguments + * @return true if no error occurred + */ + +bool cmd_toggle_explorer(girara_session_t* session, girara_list_t* argument_list); + + #endif // COMMANDS_H diff --git a/zathura/config.c b/zathura/config.c index b7621fca..c4ddcd3e 100644 --- a/zathura/config.c +++ b/zathura/config.c @@ -563,6 +563,7 @@ void config_load_default(zathura_t* zathura) { 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_inputbar_command_add(gsession, "explore", "fe", cmd_toggle_explorer, NULL, _("Toggle the file explorer")); girara_special_command_add(gsession, '/', cmd_search, INCREMENTAL_SEARCH, FORWARD, NULL); girara_special_command_add(gsession, '?', cmd_search, INCREMENTAL_SEARCH, BACKWARD, NULL); diff --git a/zathura/shortcuts.c b/zathura/shortcuts.c index d5e5a2e6..2b0b3d5e 100644 --- a/zathura/shortcuts.c +++ b/zathura/shortcuts.c @@ -84,7 +84,6 @@ bool sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument), gi g_return_val_if_fail(session->global.data != NULL, false); zathura_t* zathura = session->global.data; zathura_document_t* document = zathura_get_document(zathura); - bool clear_search = true; girara_setting_get(session, "abort-clear-search", &clear_search); @@ -1002,11 +1001,18 @@ bool sc_navigate_index(girara_session_t* session, girara_argument_t* argument, g g_return_val_if_fail(argument != NULL, false); g_return_val_if_fail(zathura->document != NULL, false); - if (zathura->ui.index == NULL) { + if (zathura->ui.index == NULL && zathura->ui.file_explorer == NULL) { return false; } - GtkTreeView* tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.index))->data; + GtkTreeView* tree_view = NULL; + if (zathura->ui.index && gtk_widget_get_visible(GTK_WIDGET(zathura->ui.index))){ + tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.index))->data; + }else if (zathura->ui.file_explorer && gtk_widget_get_visible(GTK_WIDGET(zathura->ui.file_explorer))){ + tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.file_explorer))->data; + }else{ + return false; + } GtkTreePath* path; GtkTreePath* start_path; GtkTreePath* end_path; @@ -1131,7 +1137,11 @@ bool sc_navigate_index(girara_session_t* session, girara_argument_t* argument, g } break; case SELECT: - cb_index_row_activated(tree_view, path, NULL, zathura); + if(gtk_widget_get_visible(zathura->ui.file_explorer)){ + cb_explorer_row_activated(session, tree_view, path, NULL, zathura); + }else{ + cb_index_row_activated(tree_view, path, NULL, zathura); + } goto free_and_return; } @@ -1157,6 +1167,17 @@ bool sc_toggle_index(girara_session_t* session, girara_argument_t* UNUSED(argume return false; } + if (zathura->ui.file_explorer && gtk_widget_get_visible(GTK_WIDGET(zathura->ui.file_explorer))){ + + girara_set_view(session, zathura->ui.document_widget); + gtk_widget_hide(GTK_WIDGET(zathura->ui.file_explorer)); + girara_mode_set(zathura->ui.session, zathura->modes.normal); + + /* refresh view */ + refresh_view(zathura); + return true; + } + girara_tree_node_t* document_index = NULL; GtkWidget* treeview = NULL; GtkTreeModel* model = NULL; @@ -1223,7 +1244,7 @@ bool sc_toggle_index(girara_session_t* session, girara_argument_t* UNUSED(argume gtk_container_add(GTK_CONTAINER(zathura->ui.index), treeview); } - if (gtk_widget_get_visible(GTK_WIDGET(zathura->ui.index))) { + if (zathura->ui.index && gtk_widget_get_visible(GTK_WIDGET(zathura->ui.index))) { girara_set_view(session, zathura->ui.document_widget); gtk_widget_hide(GTK_WIDGET(zathura->ui.index)); girara_mode_set(zathura->ui.session, zathura->modes.normal); diff --git a/zathura/shortcuts.h b/zathura/shortcuts.h index 2d753ae3..40aa64a2 100644 --- a/zathura/shortcuts.h +++ b/zathura/shortcuts.h @@ -260,6 +260,8 @@ bool sc_navigate_index(girara_session_t* session, girara_argument_t* argument, g */ bool sc_toggle_index(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t); +bool sc_toggle_file_explorer(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t); + /** * Toggle multi page mode * diff --git a/zathura/types.c b/zathura/types.c index b30b4ed0..e40d0a82 100644 --- a/zathura/types.c +++ b/zathura/types.c @@ -34,6 +34,16 @@ void zathura_index_element_free(zathura_index_element_t* index) { g_free(index); } +void zathura_explorer_element_free(zathura_explorer_element_s* index) { + if (index == NULL) { + return; + } + + g_free(index->title); + zathura_link_free(index->link); + g_free(index); +} + zathura_image_buffer_t* zathura_image_buffer_create(unsigned int width, unsigned int height) { g_return_val_if_fail(width != 0, NULL); g_return_val_if_fail(height != 0, NULL); diff --git a/zathura/types.h b/zathura/types.h index 3b8b76b9..f0b38723 100644 --- a/zathura/types.h +++ b/zathura/types.h @@ -198,6 +198,22 @@ typedef struct zathura_index_element_s { zathura_link_t* link; } zathura_index_element_t; +typedef enum zathura_explorer_type_e{ + ZATHURA_EXPLORER_TYPE_FILE_VALID, + ZATHURA_EXPLORER_TYPE_FILE_INVALID, + ZATHURA_EXPLORER_TYPE_DIR, +} zathura_explorer_type_e; + +/** + * Index element + */ +typedef struct zathura_explorer_element_s{ + char* title; /**< Title of the element */ + zathura_explorer_type_e type; + zathura_link_t* link; +} zathura_explorer_element_s; + + /** * Form type */ @@ -238,6 +254,13 @@ ZATHURA_PLUGIN_API zathura_index_element_t* zathura_index_element_new(const char */ ZATHURA_PLUGIN_API void zathura_index_element_free(zathura_index_element_t* index); +/** + * Free index element + * + * @param index The index element + */ +ZATHURA_PLUGIN_API void zathura_explorer_element_free(zathura_explorer_element_s* index); + /** * Creates a list that should be used to store \ref * zathura_document_information_entry_t entries diff --git a/zathura/utils.c b/zathura/utils.c index 3db1a13f..35e4dabc 100644 --- a/zathura/utils.c +++ b/zathura/utils.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "links.h" #include "utils.h" @@ -19,6 +20,7 @@ #include "page.h" #include "plugin.h" #include "content-type.h" +#include "zathura/types.h" double zathura_correct_zoom_value(girara_session_t* session, const double zoom) { if (session == NULL) { @@ -108,6 +110,47 @@ void document_index_build(girara_session_t* session, GtkTreeModel* model, GtkTre } } +static void explorer_element_free(void* data, GObject* UNUSED(object)) { + zathura_explorer_element_s* element = data; + zathura_explorer_element_free(element); +} + + +void file_explorer_build(girara_session_t* session, GtkTreeModel* model, GtkTreeIter* parent, girara_tree_node_t* tree){ + girara_list_t* list = girara_node_get_children(tree); + + for(size_t idx = 0; idx != girara_list_size(list); ++idx){ + girara_tree_node_t* node = girara_list_nth(list, idx); + zathura_explorer_element_s* fe_element = girara_node_get_data(node); + + if(strcmp(fe_element->title, "") == 0 || strcmp(fe_element->title, "`") == 0){ + continue; + } + + gchar* description = NULL; + gchar* description2 = NULL; + + if(fe_element->type == ZATHURA_EXPLORER_TYPE_FILE_VALID){ + description = fe_element->title; + description2 = "FILE"; + } else { + description = fe_element->title; + description2 = "DIRECTORY"; + } + + GtkTreeIter tree_iter; + gtk_tree_store_append(GTK_TREE_STORE(model), &tree_iter, parent); + gchar* markup = g_markup_escape_text(fe_element->title, -1); + gtk_tree_store_set(GTK_TREE_STORE(model), &tree_iter, 0, markup, 1, description, 2, description2, 3, fe_element, -1); + g_free(markup); + g_object_weak_ref(G_OBJECT(model), explorer_element_free, fe_element); + + if(girara_node_get_num_children(node) > 0){ + file_explorer_build(session, model, &tree_iter, node); + } + } +} + typedef struct { GtkTreeIter current_iter; unsigned int current_page; @@ -620,3 +663,119 @@ girara_list_t* flatten_rectangles(girara_list_t* rectangles) { girara_list_free(points); return new_rectangles; } + +zathura_explorer_type_e get_type(const char *path){ + if (path == NULL){ + return -1; + } + + const char *dot = strchr(path, '.'); + if(dot == NULL){ + // dir or otherwise invalid + return ZATHURA_EXPLORER_TYPE_DIR; + }else{ + if (strcmp(dot, ".pdf") == 0){ + // pdf file + return ZATHURA_EXPLORER_TYPE_FILE_VALID; + }else{ + // not a pdf file + return ZATHURA_EXPLORER_TYPE_FILE_INVALID; + } + } +} + +girara_tree_node_t* zathura_explorer_generate(girara_session_t* session, zathura_error_t* error){ + zathura_t* zathura = session->global.data; + /* Get CWD data */ + const char* file_path = zathura_document_get_path(zathura->document); + if (file_path == NULL){ + return NULL; + } + + char *path = g_path_get_dirname(file_path); + + zathura_explorer_element_s *cwd_data = g_try_malloc(sizeof(zathura_explorer_element_s)); + cwd_data->title = path; + cwd_data->type = ZATHURA_EXPLORER_TYPE_DIR; + zathura_rectangle_t r = {0}; + zathura_link_target_t t = {0}; + cwd_data->link = zathura_link_new(ZATHURA_LINK_NONE, r, t); + girara_tree_node_t* root = girara_node_new(cwd_data); + zathura_explorer_generate_r(error, root, path, 0); + if (error != NULL){ + girara_error("Error generating explorer"); + return NULL; + } + + return root; +} + +// takes a dir and gets its children and adds it to the parent +bool zathura_explorer_generate_r(zathura_error_t* error, girara_tree_node_t* root, char *path, int trace){ + /* Only 3 recursive calls to prevent it from getting too big */ + if (trace > 5){ + return false; + } + if (root == NULL || path == NULL){ + return false; + } + + DIR *dir; + struct dirent *dp; + + + dir = opendir(path); + if (dir == NULL){ + // girara_error("Failed to open directory "); + return false; + } + + while((dp = readdir(dir))){ + char *fp = dp->d_name; + if((strcmp(fp, "..") == 0) || (strcmp(fp, ".") == 0)){ + continue; + } + char full_path[PATH_MAX]; + snprintf(full_path, sizeof(full_path), "%s/%s", path, dp->d_name); + + unsigned char type = dp->d_type; + + zathura_explorer_type_e zathura_type = get_type(fp); + if(zathura_type == ZATHURA_EXPLORER_TYPE_FILE_VALID){ + + zathura_explorer_element_s *valid_file = g_try_malloc(sizeof(zathura_explorer_element_s)); + valid_file->title = strdup(fp); + valid_file->type = ZATHURA_EXPLORER_TYPE_FILE_VALID; + + zathura_rectangle_t rect = {0,0,0,0}; + zathura_link_target_t target = { .value = strdup(full_path), .destination_type = ZATHURA_LINK_DESTINATION_XYZ}; + zathura_link_t* link = zathura_link_new(ZATHURA_LINK_GOTO_DEST, rect, target); + valid_file->link = link; + girara_tree_node_t *file_node = girara_node_new(valid_file); + girara_node_append(root, file_node); + }else if (zathura_type == ZATHURA_EXPLORER_TYPE_FILE_INVALID || type != DT_DIR){ + continue; + }else{ + /* DIRECTORY */ + zathura_explorer_element_s *valid_dir = g_try_malloc(sizeof(zathura_explorer_element_s)); + valid_dir->title = fp; + valid_dir->type = ZATHURA_EXPLORER_TYPE_DIR; + zathura_rectangle_t r = {0}; + zathura_link_target_t t = {0}; + valid_dir->link = zathura_link_new(ZATHURA_LINK_NONE, r, t); + girara_tree_node_t *dir_node = girara_node_new(valid_dir); + + char path_ext[PATH_MAX]; + sprintf(path_ext, "%s/%s", path, fp); + bool ok = zathura_explorer_generate_r(error, dir_node, path_ext, trace + 1); + if (ok){ + girara_node_append(root, dir_node); + } + else{ + continue; + } + } + } + closedir(dir); + return true; +} diff --git a/zathura/utils.h b/zathura/utils.h index b53b919c..451c0e2f 100644 --- a/zathura/utils.h +++ b/zathura/utils.h @@ -38,6 +38,17 @@ bool file_valid_extension(zathura_t* zathura, const char* path); void document_index_build(girara_session_t* session, GtkTreeModel* model, GtkTreeIter* parent, girara_tree_node_t* tree); +/** + * Generates the file explorer based upon the list retrieved from the system call + * + * @param session The session + * @param model The tree model + * @param parent The tree iterator parent + * @param tree The Tree iterator + */ +void file_explorer_build(girara_session_t* session, GtkTreeModel* model, GtkTreeIter* parent, + girara_tree_node_t* tree); + /** * A custom search equal function for the index tree view, so that * when interactively searching, the string will be recursively compared @@ -181,4 +192,8 @@ bool parse_color(GdkRGBA* color, const char* str); */ girara_list_t* flatten_rectangles(girara_list_t* rectangles); +girara_tree_node_t* zathura_explorer_generate(girara_session_t* session, zathura_error_t* error); + +bool zathura_explorer_generate_r(zathura_error_t* error, girara_tree_node_t* root, char *path, int trace); + #endif // UTILS_H diff --git a/zathura/zathura.c b/zathura/zathura.c index fbb9e1df..46d9098f 100644 --- a/zathura/zathura.c +++ b/zathura/zathura.c @@ -1296,6 +1296,8 @@ void document_open_idle(zathura_t* zathura, const char* path, const char* passwo } gdk_threads_add_idle(document_info_open, document_info); + + girara_mode_set(zathura->ui.session, zathura->modes.normal); } bool document_save(zathura_t* zathura, const char* path, bool overwrite) { diff --git a/zathura/zathura.h b/zathura/zathura.h index 3e775e44..1beba18b 100644 --- a/zathura/zathura.h +++ b/zathura/zathura.h @@ -116,6 +116,7 @@ struct zathura_s { GtkWidget* document_widget; /**< Widget that contains all rendered pages */ GtkWidget* index; /**< Widget to show the index of the document */ + GtkWidget* file_explorer; /**< Widget to show the file explorer of the CWD */ } ui; struct {