diff --git a/include/modes/modes.h b/include/modes/modes.h index 72b8b6ee..f5c2e9a7 100644 --- a/include/modes/modes.h +++ b/include/modes/modes.h @@ -39,6 +39,7 @@ #include "modes/dmenu.h" #include "modes/drun.h" #include "modes/filebrowser.h" +#include "modes/recursivebrowser.h" #include "modes/help-keys.h" #include "modes/run.h" #include "modes/script.h" diff --git a/include/modes/recursivebrowser.h b/include/modes/recursivebrowser.h new file mode 100644 index 00000000..7c5c9a22 --- /dev/null +++ b/include/modes/recursivebrowser.h @@ -0,0 +1,58 @@ +/* + * rofi + * + * MIT/X11 License + * Copyright © 2013-2023 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef ROFI_MODE_RECURSIVE_BROWSER_H +#define ROFI_MODE_RECURSIVE_BROWSER_H +#include "mode.h" +/** + * @defgroup FileBrowserMode FileBrowser + * @ingroup MODES + * + * + * @{ + */ +/** #Mode object representing the run dialog. */ +extern Mode recursive_browser_mode; + +/** + * Create a new filebrowser. + * @returns a new filebrowser structure. + */ +Mode *create_new_recursive_browser(void); +/** + * @param sw Mode object. + * @param mretv return value passed in. + * @param input The user input string. + * @param selected_line The user selected line. + * @param path The full path as output. + * + * @returns the state the user selected. + */ +ModeMode recursive_browser_mode_completer(Mode *sw, int mretv, char **input, + unsigned int selected_line, char **path); +/**@}*/ +#endif // ROFI_MODE_RECURSIVE_BROWSER_H diff --git a/meson.build b/meson.build index 9415b999..2d889503 100644 --- a/meson.build +++ b/meson.build @@ -209,6 +209,7 @@ rofi_sources = files( 'source/modes/script.c', 'source/modes/help-keys.c', 'source/modes/filebrowser.c', + 'source/modes/recursivebrowser.c', 'include/display.h', 'include/xcb.h', 'include/xcb-internal.h', diff --git a/source/modes/recursivebrowser.c b/source/modes/recursivebrowser.c new file mode 100644 index 00000000..5a69bc31 --- /dev/null +++ b/source/modes/recursivebrowser.c @@ -0,0 +1,739 @@ +/** + * rofi-recursive_browser + * + * MIT/X11 License + * Copyright (c) 2017 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "helper.h" +#include "history.h" +#include "mode-private.h" +#include "mode.h" +#include "modes/recursivebrowser.h" +#include "rofi.h" +#include "theme.h" + +#include + +#include "rofi-icon-fetcher.h" + +#define FILEBROWSER_CACHE_FILE "rofi3.recursivebrowsercache" +#define DEFAULT_OPEN "xdg-open" + +#if defined(__APPLE__) +#define st_atim st_atimespec +#define st_ctim st_ctimespec +#define st_mtim st_mtimespec +#endif + +/** + * The internal data structure holding the private data of the TEST Mode. + */ +enum FBFileType { + UP, + DIRECTORY, + RFILE, + NUM_FILE_TYPES, +}; + +/** + * Possible sorting methods + */ +enum FBSortingMethod { + FB_SORT_NAME, + FB_SORT_TIME, +}; + +/** + * Type of time to sort by + */ +enum FBSortingTime { + FB_MTIME, + FB_ATIME, + FB_CTIME, +}; + +/** Icons to use for the file type */ +const char *rb_icon_name[NUM_FILE_TYPES] = {"go-up", "folder", "gtk-file"}; +typedef struct { + char *name; + char *path; + enum FBFileType type; + uint32_t icon_fetch_uid; + uint32_t icon_fetch_size; + gboolean link; + time_t time; +} FBFile; + +typedef struct { + char *command; + GFile *current_dir; + FBFile *array; + unsigned int array_length; + unsigned int array_length_real; +} FileBrowserModePrivateData; + +/** + * The sorting settings used in file-browser. + */ +struct { + /** Field to sort on. */ + enum FBSortingMethod sorting_method; + /** If sorting on time, what time entry. */ + enum FBSortingTime sorting_time; + /** If we want to display directories above files. */ + gboolean directories_first; + /** If we want to show hidden files. */ + gboolean show_hidden; +} recursive_browser_config = { + .sorting_method = FB_SORT_NAME, + .sorting_time = FB_MTIME, + .directories_first = TRUE, + .show_hidden = FALSE, +}; + +static void free_list(FileBrowserModePrivateData *pd) { + for (unsigned int i = 0; i < pd->array_length; i++) { + FBFile *fb = &(pd->array[i]); + g_free(fb->name); + g_free(fb->path); + } + g_free(pd->array); + pd->array = NULL; + pd->array_length = 0; + pd->array_length_real = 0; +} +#include +#include + +static gint compare_name(gconstpointer a, gconstpointer b, + G_GNUC_UNUSED gpointer data) { + FBFile *fa = (FBFile *)a; + FBFile *fb = (FBFile *)b; + + if (recursive_browser_config.directories_first && fa->type != fb->type) { + return fa->type - fb->type; + } + + return g_strcmp0(fa->name, fb->name); +} + +static gint compare_time(gconstpointer a, gconstpointer b, + G_GNUC_UNUSED gpointer data) { + FBFile *fa = (FBFile *)a; + FBFile *fb = (FBFile *)b; + + if (recursive_browser_config.directories_first && fa->type != fb->type) { + return fa->type - fb->type; + } + + if (fa->time < 0) { + return -1; + } + + if (fb->time < 0) { + return 1; + } + + return fb->time - fa->time; +} + +static gint compare(gconstpointer a, gconstpointer b, gpointer data) { + GCompareDataFunc comparator = NULL; + + switch (recursive_browser_config.sorting_method) { + case FB_SORT_NAME: + comparator = compare_name; + break; + case FB_SORT_TIME: + comparator = compare_time; + break; + default: + comparator = compare_name; + break; + } + + return comparator(a, b, data); +} + +static time_t get_time(const GStatBuf *statbuf) { + switch (recursive_browser_config.sorting_time) { + case FB_MTIME: + return statbuf->st_mtim.tv_sec; + case FB_ATIME: + return statbuf->st_atim.tv_sec; + case FB_CTIME: + return statbuf->st_ctim.tv_sec; + default: + return 0; + } +} + +static void set_time(FBFile *file) { + // GError *error = NULL; + // gchar *path = g_filename_from_utf8(file->path, -1, NULL, NULL, &error); + // if (error) { + // g_warning("Failed to convert filename: %s: %s", file->path, + // error->message); g_error_free(error); return; + // } + + GStatBuf statbuf; + + if (g_lstat(file->path, &statbuf) == 0) { + file->time = get_time(&statbuf); + } else { + g_warning("Failed to stat file: %s, %s", file->path, strerror(errno)); + } + + // g_free(path); +} + +inline static void fb_resize_array(FileBrowserModePrivateData *pd) { + if ((pd->array_length + 1) > pd->array_length_real) { + pd->array_length_real += 1024; + pd->array = + g_realloc(pd->array, (pd->array_length_real + 1) * sizeof(FBFile)); + } +} + +static void get_recursive_browser(Mode *sw, GFile *scan_dir) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + /** + * Get the entries to display. + * this gets called on plugin initialization. + */ + char *cdir = g_file_get_path(scan_dir); + DIR *dir = opendir(cdir); + if (dir) { + struct dirent *rd = NULL; + while ((rd = readdir(dir)) != NULL) { + if (g_strcmp0(rd->d_name, "..") == 0) { + continue; + //fb_resize_array(pd); + //// Rofi expects utf-8, so lets convert the filename. + //pd->array[pd->array_length].name = g_strdup(".."); + //pd->array[pd->array_length].path = NULL; + //pd->array[pd->array_length].type = UP; + //pd->array[pd->array_length].icon_fetch_uid = 0; + //pd->array[pd->array_length].icon_fetch_size = 0; + //pd->array[pd->array_length].link = FALSE; + //pd->array[pd->array_length].time = -1; + //pd->array_length++; + } + if (g_strcmp0(rd->d_name, ".") == 0) { + continue; + } + if (rd->d_name[0] == '.' && recursive_browser_config.show_hidden == FALSE) { + continue; + } + + switch (rd->d_type) { + case DT_BLK: + case DT_CHR: + case DT_FIFO: + case DT_UNKNOWN: + case DT_SOCK: + default: + break; + case DT_REG: + fb_resize_array(pd); + // Rofi expects utf-8, so lets convert the filename. + pd->array[pd->array_length].path = + g_build_filename(cdir, rd->d_name, NULL); + pd->array[pd->array_length].name = + g_filename_to_utf8(pd->array[pd->array_length].path, -1, NULL, NULL, NULL); + if (pd->array[pd->array_length].name == NULL) { + pd->array[pd->array_length].name = rofi_force_utf8(rd->d_name, -1); + } + pd->array[pd->array_length].type = + (rd->d_type == DT_DIR) ? DIRECTORY : RFILE; + pd->array[pd->array_length].icon_fetch_uid = 0; + pd->array[pd->array_length].icon_fetch_size = 0; + pd->array[pd->array_length].link = FALSE; + + if (recursive_browser_config.sorting_method == FB_SORT_TIME) { + set_time(&pd->array[pd->array_length]); + } + + pd->array_length++; + break; + case DT_DIR: + + char *d = g_build_filename(cdir, rd->d_name, NULL); + GFile *dirp = g_file_new_for_path(d); + get_recursive_browser(sw,dirp); + g_object_unref(dirp); + g_free(d); + break; + case DT_LNK: + fb_resize_array(pd); + // Rofi expects utf-8, so lets convert the filename. + pd->array[pd->array_length].name = + g_filename_to_utf8(rd->d_name, -1, NULL, NULL, NULL); + if (pd->array[pd->array_length].name == NULL) { + pd->array[pd->array_length].name = rofi_force_utf8(rd->d_name, -1); + } + pd->array[pd->array_length].path = + g_build_filename(cdir, rd->d_name, NULL); + pd->array[pd->array_length].icon_fetch_uid = 0; + pd->array[pd->array_length].icon_fetch_size = 0; + pd->array[pd->array_length].link = TRUE; + // Default to file. + pd->array[pd->array_length].type = RFILE; + if (0){ + // If we have link, use a stat to fine out what it is, if we fail, we + // mark it as file. + // TODO have a 'broken link' mode? + // Convert full path to right encoding. + // DD: Path should be in file encoding, not utf-8 + // char *file = + // g_filename_from_utf8(pd->array[pd->array_length].path, + // -1, NULL, NULL, NULL); + if (pd->array[pd->array_length].path) { + GStatBuf statbuf; + if (g_stat(pd->array[pd->array_length].path, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + pd->array[pd->array_length].type = DIRECTORY; + } else if (S_ISREG(statbuf.st_mode)) { + pd->array[pd->array_length].type = RFILE; + } + + if (recursive_browser_config.sorting_method == FB_SORT_TIME) { + pd->array[pd->array_length].time = get_time(&statbuf); + } + } else { + g_warning("Failed to stat file: %s, %s", + pd->array[pd->array_length].path, strerror(errno)); + } + + // g_free(file); + } + } + pd->array_length++; + break; + } + } + closedir(dir); + } + g_free(cdir); + //g_qsort_with_data(pd->array, pd->array_length, sizeof(FBFile), compare, NULL); +} + +static void recursive_browser_mode_init_config(Mode *sw) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + char *msg = NULL; + gboolean found_error = FALSE; + + ThemeWidget *wid = rofi_config_find_widget(sw->name, NULL, TRUE); + + Property *p = rofi_theme_find_property(wid, P_STRING, "sorting-method", TRUE); + if (p != NULL && p->type == P_STRING) { + if (g_strcmp0(p->value.s, "name") == 0) { + recursive_browser_config.sorting_method = FB_SORT_NAME; + } else if (g_strcmp0(p->value.s, "mtime") == 0) { + recursive_browser_config.sorting_method = FB_SORT_TIME; + recursive_browser_config.sorting_time = FB_MTIME; + } else if (g_strcmp0(p->value.s, "atime") == 0) { + recursive_browser_config.sorting_method = FB_SORT_TIME; + recursive_browser_config.sorting_time = FB_ATIME; + } else if (g_strcmp0(p->value.s, "ctime") == 0) { + recursive_browser_config.sorting_method = FB_SORT_TIME; + recursive_browser_config.sorting_time = FB_CTIME; + } else { + found_error = TRUE; + + msg = g_strdup_printf("\"%s\" is not a valid recursivebrowser sorting method", + p->value.s); + } + } + + p = rofi_theme_find_property(wid, P_BOOLEAN, "directories-first", TRUE); + if (p != NULL && p->type == P_BOOLEAN) { + recursive_browser_config.directories_first = p->value.b; + } + + p = rofi_theme_find_property(wid, P_BOOLEAN, "show-hidden", TRUE); + if (p != NULL && p->type == P_BOOLEAN) { + recursive_browser_config.show_hidden = p->value.b; + } + + p = rofi_theme_find_property(wid, P_STRING, "command", TRUE); + if (p != NULL && p->type == P_STRING) { + pd->command = g_strdup(p->value.s); + } else { + pd->command = g_strdup(DEFAULT_OPEN); + } + + if (found_error) { + rofi_view_error_dialog(msg, FALSE); + + g_free(msg); + } +} + +static void recursive_browser_mode_init_current_dir(Mode *sw) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + + ThemeWidget *wid = rofi_config_find_widget(sw->name, NULL, TRUE); + + Property *p = rofi_theme_find_property(wid, P_STRING, "directory", TRUE); + + gboolean config_has_valid_dir = p != NULL && p->type == P_STRING && + g_file_test(p->value.s, G_FILE_TEST_IS_DIR); + + if (config_has_valid_dir) { + pd->current_dir = g_file_new_for_path(p->value.s); + } else { + char *current_dir = NULL; + char *cache_file = + g_build_filename(cache_dir, FILEBROWSER_CACHE_FILE, NULL); + + if (g_file_get_contents(cache_file, ¤t_dir, NULL, NULL)) { + if (g_file_test(current_dir, G_FILE_TEST_IS_DIR)) { + pd->current_dir = g_file_new_for_path(current_dir); + } + + g_free(current_dir); + } + + // Store it based on the unique identifiers (desktop_id). + g_free(cache_file); + } + + if (pd->current_dir == NULL) { + pd->current_dir = g_file_new_for_path(g_get_home_dir()); + } +} + +static int recursive_browser_mode_init(Mode *sw) { + /** + * Called on startup when enabled (in modes list) + */ + if (mode_get_private_data(sw) == NULL) { + FileBrowserModePrivateData *pd = g_malloc0(sizeof(*pd)); + mode_set_private_data(sw, (void *)pd); + + recursive_browser_mode_init_config(sw); + recursive_browser_mode_init_current_dir(sw); + + // Load content. + get_recursive_browser(sw,pd->current_dir); + } + return TRUE; +} +static unsigned int recursive_browser_mode_get_num_entries(const Mode *sw) { + const FileBrowserModePrivateData *pd = + (const FileBrowserModePrivateData *)mode_get_private_data(sw); + return pd->array_length; +} + +static ModeMode recursive_browser_mode_result(Mode *sw, int mretv, char **input, + unsigned int selected_line) { + ModeMode retv = MODE_EXIT; + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + + if ((mretv & MENU_CANCEL) == MENU_CANCEL) { + ThemeWidget *wid = rofi_config_find_widget(sw->name, NULL, TRUE); + Property *p = + rofi_theme_find_property(wid, P_BOOLEAN, "cancel-returns-1", TRUE); + if (p && p->type == P_BOOLEAN && p->value.b == TRUE) { + rofi_set_return_code(1); + } + return MODE_EXIT; + } + gboolean special_command = + ((mretv & MENU_CUSTOM_ACTION) == MENU_CUSTOM_ACTION); + if (mretv & MENU_NEXT) { + retv = NEXT_DIALOG; + } else if (mretv & MENU_PREVIOUS) { + retv = PREVIOUS_DIALOG; + } else if (mretv & MENU_QUICK_SWITCH) { + retv = (mretv & MENU_LOWER_MASK); + } else if (mretv & MENU_CUSTOM_COMMAND) { + retv = (mretv & MENU_LOWER_MASK); + } else if ((mretv & MENU_OK)) { + if (selected_line < pd->array_length) { + if (pd->array[selected_line].type == UP) { + GFile *new = g_file_get_parent(pd->current_dir); + if (new) { + g_object_unref(pd->current_dir); + pd->current_dir = new; + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + return RESET_DIALOG; + } + } else if ((pd->array[selected_line].type == RFILE) || + (pd->array[selected_line].type == DIRECTORY && + special_command)) { + char *d_esc = g_shell_quote(pd->array[selected_line].path); + char *cmd = g_strdup_printf("%s %s", pd->command, d_esc); + g_free(d_esc); + char *cdir = g_file_get_path(pd->current_dir); + helper_execute_command(cdir, cmd, FALSE, NULL); + g_free(cdir); + g_free(cmd); + return MODE_EXIT; + } else if (pd->array[selected_line].type == DIRECTORY) { + char *path = g_build_filename(cache_dir, FILEBROWSER_CACHE_FILE, NULL); + g_file_set_contents(path, pd->array[selected_line].path, -1, NULL); + g_free(path); + GFile *new = g_file_new_for_path(pd->array[selected_line].path); + g_object_unref(pd->current_dir); + pd->current_dir = new; + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + return RESET_DIALOG; + } + } + retv = RELOAD_DIALOG; + } else if ((mretv & MENU_CUSTOM_INPUT)) { + if (special_command) { + GFile *new = g_file_get_parent(pd->current_dir); + if (new) { + g_object_unref(pd->current_dir); + pd->current_dir = new; + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + } + return RESET_DIALOG; + } + if (*input) { + char *p = rofi_expand_path(*input); + char *dir = g_filename_from_utf8(p, -1, NULL, NULL, NULL); + g_free(p); + if (g_file_test(dir, G_FILE_TEST_EXISTS)) { + if (g_file_test(dir, G_FILE_TEST_IS_DIR)) { + g_object_unref(pd->current_dir); + pd->current_dir = g_file_new_for_path(dir); + g_free(dir); + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + return RESET_DIALOG; + } + } + g_free(dir); + retv = RELOAD_DIALOG; + } + } else if ((mretv & MENU_ENTRY_DELETE) == MENU_ENTRY_DELETE) { + recursive_browser_config.show_hidden = !recursive_browser_config.show_hidden; + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + retv = RELOAD_DIALOG; + } + return retv; +} + +static void recursive_browser_mode_destroy(Mode *sw) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + if (pd != NULL) { + g_object_unref(pd->current_dir); + g_free(pd->command); + free_list(pd); + g_free(pd); + mode_set_private_data(sw, NULL); + } +} + +static char *_get_display_value(const Mode *sw, unsigned int selected_line, + G_GNUC_UNUSED int *state, + G_GNUC_UNUSED GList **attr_list, + int get_entry) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + + // Only return the string if requested, otherwise only set state. + if (!get_entry) { + return NULL; + } + if (pd->array[selected_line].type == UP) { + return g_strdup(" .."); + } + if (pd->array[selected_line].link) { + return g_strconcat("@", pd->array[selected_line].name, NULL); + } + return g_strdup(pd->array[selected_line].name); +} + +/** + * @param sw The mode object. + * @param tokens The tokens to match against. + * @param index The index in this plugin to match against. + * + * Match the entry. + * + * @returns try when a match. + */ +static int recursive_browser_token_match(const Mode *sw, rofi_int_matcher **tokens, + unsigned int index) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + + // Call default matching function. + return helper_token_match(tokens, pd->array[index].name); +} + +static cairo_surface_t *_get_icon(const Mode *sw, unsigned int selected_line, + unsigned int height) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + g_return_val_if_fail(pd->array != NULL, NULL); + FBFile *dr = &(pd->array[selected_line]); + if (dr->icon_fetch_uid > 0 && dr->icon_fetch_size == height) { + return rofi_icon_fetcher_get(dr->icon_fetch_uid); + } + if (rofi_icon_fetcher_file_is_image(dr->path)) { + dr->icon_fetch_uid = rofi_icon_fetcher_query(dr->path, height); + } else { + dr->icon_fetch_uid = rofi_icon_fetcher_query(rb_icon_name[dr->type], height); + } + dr->icon_fetch_size = height; + return rofi_icon_fetcher_get(dr->icon_fetch_uid); +} + +static char *_get_message(const Mode *sw) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + if (pd->current_dir) { + char *dirname = g_file_get_parse_name(pd->current_dir); + char *str = + g_markup_printf_escaped("Current directory: %s", dirname); + g_free(dirname); + return str; + } + return "n/a"; +} + +static char *_get_completion(const Mode *sw, unsigned int index) { + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + + char *d = g_strescape(pd->array[index].path, NULL); + return d; +} + +Mode *create_new_recursive_browser(void) { + Mode *sw = g_malloc0(sizeof(Mode)); + + *sw = recursive_browser_mode; + + sw->private_data = NULL; + return sw; +} + +#if 1 +ModeMode recursive_browser_mode_completer(Mode *sw, int mretv, char **input, + unsigned int selected_line, char **path) { + ModeMode retv = MODE_EXIT; + FileBrowserModePrivateData *pd = + (FileBrowserModePrivateData *)mode_get_private_data(sw); + if (mretv & MENU_NEXT) { + retv = NEXT_DIALOG; + } else if (mretv & MENU_PREVIOUS) { + retv = PREVIOUS_DIALOG; + } else if (mretv & MENU_QUICK_SWITCH) { + retv = (mretv & MENU_LOWER_MASK); + } else if ((mretv & MENU_OK)) { + if (selected_line < pd->array_length) { + if (pd->array[selected_line].type == UP) { + GFile *new = g_file_get_parent(pd->current_dir); + if (new) { + g_object_unref(pd->current_dir); + pd->current_dir = new; + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + return RESET_DIALOG; + } + } else if (pd->array[selected_line].type == DIRECTORY) { + GFile *new = g_file_new_for_path(pd->array[selected_line].path); + g_object_unref(pd->current_dir); + pd->current_dir = new; + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + return RESET_DIALOG; + } else if (pd->array[selected_line].type == RFILE) { + *path = g_strescape(pd->array[selected_line].path, NULL); + return MODE_EXIT; + } + } + retv = RELOAD_DIALOG; + } else if ((mretv & MENU_CUSTOM_INPUT) && *input) { + char *p = rofi_expand_path(*input); + char *dir = g_filename_from_utf8(p, -1, NULL, NULL, NULL); + g_free(p); + if (g_file_test(dir, G_FILE_TEST_EXISTS)) { + if (g_file_test(dir, G_FILE_TEST_IS_DIR)) { + g_object_unref(pd->current_dir); + pd->current_dir = g_file_new_for_path(dir); + g_free(dir); + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + return RESET_DIALOG; + } + } + g_free(dir); + retv = RELOAD_DIALOG; + } else if ((mretv & MENU_ENTRY_DELETE) == MENU_ENTRY_DELETE) { + recursive_browser_config.show_hidden = !recursive_browser_config.show_hidden; + free_list(pd); + get_recursive_browser(sw,pd->current_dir); + retv = RELOAD_DIALOG; + } + return retv; +} +#endif + +Mode recursive_browser_mode = { + .display_name = NULL, + .abi_version = ABI_VERSION, + .name = "recursivebrowser", + .cfg_name_key = "display-recursivebrowser", + ._init = recursive_browser_mode_init, + ._get_num_entries = recursive_browser_mode_get_num_entries, + ._result = recursive_browser_mode_result, + ._destroy = recursive_browser_mode_destroy, + ._token_match = recursive_browser_token_match, + ._get_display_value = _get_display_value, + ._get_icon = _get_icon, + ._get_message = _get_message, + ._get_completion = _get_completion, + ._preprocess_input = NULL, + .private_data = NULL, + .free = NULL, +}; diff --git a/source/rofi.c b/source/rofi.c index aa9c686b..8ea16158 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -608,6 +608,7 @@ static void rofi_collect_modes(void) { rofi_collectmodes_add(&combi_mode); rofi_collectmodes_add(&help_keys_mode); rofi_collectmodes_add(&file_browser_mode); + rofi_collectmodes_add(&recursive_browser_mode); if (find_arg("-no-plugins") < 0) { find_arg_str("-plugin-path", &(config.plugin_path));