From b710a3aee57ba9450fc0cc6b0f6873d5179ccdcb Mon Sep 17 00:00:00 2001 From: Scoopta Date: Wed, 13 Nov 2019 01:17:45 -0800 Subject: [PATCH] Added support for alternative desktop actions --- inc/wofi.h | 2 +- modes/dmenu.c | 4 +- modes/drun.c | 110 ++++++++++++++++++++++++++--- modes/run.c | 6 +- src/wofi.c | 189 ++++++++++++++++++++++++++++++-------------------- 5 files changed, 223 insertions(+), 88 deletions(-) diff --git a/inc/wofi.h b/inc/wofi.h index ddb8af0..ce6bbe6 100644 --- a/inc/wofi.h +++ b/inc/wofi.h @@ -45,7 +45,7 @@ void wofi_init(struct map* config); struct wl_list* wofi_read_cache(char* mode); -void wofi_insert_widget(char* mode, char* text, char* search_text, char* action); +void wofi_insert_widget(char* mode, char** text, char* search_text, char** actions, size_t action_count); bool wofi_allow_images(); diff --git a/modes/dmenu.c b/modes/dmenu.c index ebe3269..c4cf03a 100644 --- a/modes/dmenu.c +++ b/modes/dmenu.c @@ -23,7 +23,7 @@ void wofi_dmenu_init() { struct cache_line* node, *tmp; wl_list_for_each_safe(node, tmp, cache, link) { - wofi_insert_widget("dmenu", node->line, node->line, node->line); + wofi_insert_widget("dmenu", &node->line, node->line, &node->line, 1); map_put(cached, node->line, "true"); free(node->line); wl_list_remove(&node->link); @@ -42,7 +42,7 @@ void wofi_dmenu_init() { if(map_contains(cached, line)) { continue; } - wofi_insert_widget("dmenu", line, line, line); + wofi_insert_widget("dmenu", &line, line, &line, 1); } free(line); map_free(cached); diff --git a/modes/drun.c b/modes/drun.c index d8249dd..a83e27b 100644 --- a/modes/drun.c +++ b/modes/drun.c @@ -17,12 +17,17 @@ #include -static char* get_text(char* file) { +static char* get_text(char* file, char* action) { GDesktopAppInfo* info = g_desktop_app_info_new_from_filename(file); if(info == NULL || g_desktop_app_info_get_is_hidden(info) || g_desktop_app_info_get_nodisplay(info)) { return NULL; } - const char* name = g_app_info_get_display_name(G_APP_INFO(info)); + const char* name; + if(action == NULL) { + name = g_app_info_get_display_name(G_APP_INFO(info)); + } else { + name = g_desktop_app_info_get_action_name(info, action); + } if(name == NULL) { return NULL; } @@ -60,6 +65,62 @@ static char* get_search_text(char* file) { return utils_concat(6, name, file, exec == NULL ? "" : exec, description == NULL ? "" : description, categories == NULL ? "" : categories, keywords == NULL ? (const char* const*) "" : keywords); } +static const gchar* const* get_actions(char* file, size_t* action_count) { + *action_count = 0; + GDesktopAppInfo* info = g_desktop_app_info_new_from_filename(file); + if(info == NULL) { + return NULL; + } + const gchar* const* actions = g_desktop_app_info_list_actions(info); + if(actions[0] == NULL) { + return NULL; + } + + for(; actions[*action_count] != NULL; ++*action_count); + + return actions; +} + +static char** get_action_text(char* file, size_t* text_count) { + *text_count = 0; + + char* tmp = get_text(file, NULL); + if(tmp == NULL) { + return NULL; + } + + const gchar* const* action_names = get_actions(file, text_count); + + ++*text_count; + char** text = malloc(*text_count * sizeof(char*)); + text[0] = tmp; + + for(size_t count = 1; count < *text_count; ++count) { + text[count] = get_text(file, (gchar*) action_names[count - 1]); + } + return text; +} + +static char** get_action_actions(char* file, size_t* action_count) { + *action_count = 0; + + char* tmp = strdup(file); + if(tmp == NULL) { + return NULL; + } + + const gchar* const* action_names = get_actions(file, action_count); + + ++*action_count; + char** actions = malloc(*action_count * sizeof(char*)); + actions[0] = tmp; + + for(size_t count = 1; count < *action_count; ++count) { + actions[count] = utils_concat(3, file, " ", (gchar*) action_names[count - 1]); + } + return actions; +} + static void insert_dir(char* app_dir, struct map* cached, struct map* entries) { DIR* dir = opendir(app_dir); if(dir == NULL) { @@ -82,18 +143,30 @@ static void insert_dir(char* app_dir, struct map* cached, struct map* entries) { free(full_path); continue; } - char* text = get_text(full_path); + if(map_contains(entries, entry->d_name)) { + free(full_path); + continue; + } + size_t action_count; + char** text = get_action_text(full_path, &action_count); if(text == NULL) { free(full_path); continue; } - if(map_contains(entries, entry->d_name)) { - continue; - } map_put(entries, entry->d_name, "true"); + + char** actions = get_action_actions(full_path, &action_count); + char* search_text = get_search_text(full_path); - wofi_insert_widget("drun", text, search_text, full_path); + wofi_insert_widget("drun", text, search_text, actions, action_count); + + for(size_t count = 0; count < action_count; ++count) { + free(actions[count]); + free(text[count]); + } + free(text); + free(actions); free(search_text); free(full_path); } @@ -107,15 +180,26 @@ void wofi_drun_init() { struct cache_line* node, *tmp; wl_list_for_each_safe(node, tmp, cache, link) { - char* text = get_text(node->line); + size_t action_count; + char** text = get_action_text(node->line, &action_count); if(text == NULL) { goto cache_cont; } + + char** actions = get_action_actions(node->line, &action_count); + char* search_text = get_search_text(node->line); - wofi_insert_widget("drun", text, search_text, node->line); + wofi_insert_widget("drun", text, search_text, actions, action_count); map_put(cached, node->line, "true"); + free(search_text); + + for(size_t count = 0; count < action_count; ++count) { + free(text[count]); + free(actions[count]); + } free(text); + free(actions); cache_cont: free(node->line); @@ -164,6 +248,14 @@ void wofi_drun_exec(const gchar* cmd) { GDesktopAppInfo* info = g_desktop_app_info_new_from_filename(cmd); if(G_IS_DESKTOP_APP_INFO(info)) { g_app_info_launch_uris_async(G_APP_INFO(info), NULL, NULL, NULL, launch_done, (gchar*) cmd); + } else if(strrchr(cmd, ' ') != NULL) { + char* space = strrchr(cmd, ' '); + *space = 0; + info = g_desktop_app_info_new_from_filename(cmd); + char* action = space + 1; + g_desktop_app_info_launch_action(info, action, NULL); + utils_sleep_millis(500); + exit(0); } else { fprintf(stderr, "%s cannot be executed\n", cmd); exit(1); diff --git a/modes/run.c b/modes/run.c index 089c8ce..f4b3916 100644 --- a/modes/run.c +++ b/modes/run.c @@ -34,7 +34,7 @@ void wofi_run_init() { search_prefix = text; } char* search_text = utils_concat(2, search_prefix, node->line); - wofi_insert_widget("run", text, search_text, node->line); + wofi_insert_widget("run", &text, search_text, &node->line, 1); map_put(cached, node->line, "true"); free(search_text); free(node->line); @@ -63,8 +63,10 @@ void wofi_run_init() { stat(full_path, &info); if(access(full_path, X_OK) == 0 && S_ISREG(info.st_mode) && !map_contains(cached, full_path)) { char* search_text = utils_concat(2, entry->d_name, full_path); - wofi_insert_widget("run", entry->d_name, search_text, full_path); + char* text = strdup(entry->d_name); + wofi_insert_widget("run", &text, search_text, &full_path, 1); free(search_text); + free(text); } free(full_path); } diff --git a/src/wofi.c b/src/wofi.c index ddfdc15..22d2226 100644 --- a/src/wofi.c +++ b/src/wofi.c @@ -38,7 +38,8 @@ static bool exec_search; static struct map* modes; struct node { - char* mode, *text, *search_text, *action; + size_t action_count; + char* mode, **text, *search_text, **actions; }; struct mode { @@ -142,20 +143,6 @@ static GtkWidget* create_label(char* mode, char* text, char* search_text, char* return box; } -static gboolean _insert_widget(gpointer data) { - struct node* node = data; - GtkWidget* box = create_label(node->mode, node->text, node->search_text, node->action); - gtk_container_add(GTK_CONTAINER(inner_box), box); - gtk_widget_show_all(box); - - free(node->mode); - free(node->text); - free(node->search_text); - free(node->action); - free(node); - return FALSE; -} - static char* get_cache_path(const gchar* mode) { if(cache_file != NULL) { return strdup(cache_file); @@ -169,6 +156,100 @@ static char* get_cache_path(const gchar* mode) { return cache_path; } +static void execute_action(const gchar* mode, const gchar* cmd, bool primary_action) { + struct mode* mode_ptr = map_get(modes, mode); + if(primary_action) { + char* cache_path = get_cache_path(mode); + struct wl_list lines; + wl_list_init(&lines); + bool inc_count = false; + if(access(cache_path, R_OK) == 0) { + FILE* file = fopen(cache_path, "r"); + char* line = NULL; + size_t size = 0; + while(getline(&line, &size, file) != -1) { + struct cache_line* node = malloc(sizeof(struct cache_line)); + if(strstr(line, cmd) != NULL) { + uint64_t count = strtol(line, NULL, 10) + 1; + char num[6]; + snprintf(num, 5, "%" PRIu64, count); + node->line = utils_concat(4, num, " ", cmd, "\n"); + inc_count = true; + } else { + node->line = strdup(line); + } + wl_list_insert(&lines, &node->link); + } + free(line); + fclose(file); + } + if(!inc_count) { + struct cache_line* node = malloc(sizeof(struct cache_line)); + node->line = utils_concat(3, "1 ", cmd, "\n"); + wl_list_insert(&lines, &node->link); + } + + FILE* file = fopen(cache_path, "w"); + struct cache_line* node, *tmp; + wl_list_for_each_safe(node, tmp, &lines, link) { + fwrite(node->line, 1, strlen(node->line), file); + free(node->line); + wl_list_remove(&node->link); + free(node); + } + + fclose(file); + + free(cache_path); + } + mode_ptr->mode_exec(cmd); +} + +static void activate_item(GtkFlowBox* flow_box, GtkFlowBoxChild* row, gpointer data) { + (void) flow_box; + (void) data; + GtkWidget* box = gtk_bin_get_child(GTK_BIN(row)); + bool primary_action = GTK_IS_EXPANDER(box); + if(primary_action) { + box = gtk_expander_get_label_widget(GTK_EXPANDER(box)); + } + execute_action(wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "mode"), wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "action"), primary_action); +} + +static gboolean _insert_widget(gpointer data) { + struct node* node = data; + GtkWidget* parent; + if(node->action_count > 1) { + parent = gtk_expander_new(""); + GtkWidget* box = create_label(node->mode, node->text[0], node->search_text, node->actions[0]); + gtk_expander_set_label_widget(GTK_EXPANDER(parent), box); + GtkWidget* exp_box = gtk_list_box_new(); + g_signal_connect(exp_box, "row-activated", G_CALLBACK(activate_item), NULL); + gtk_container_add(GTK_CONTAINER(parent), exp_box); + for(size_t count = 1; count < node->action_count; ++count) { + box = create_label(node->mode, node->text[count], node->search_text, node->actions[count]); + gtk_container_add(GTK_CONTAINER(exp_box), box); + } + } else { + parent = create_label(node->mode, node->text[0], node->search_text, node->actions[0]); + } + gtk_container_add(GTK_CONTAINER(inner_box), parent); + gtk_widget_show_all(parent); + + free(node->mode); + for(size_t count = 0; count < node->action_count; ++count) { + free(node->text[count]); + } + free(node->text); + free(node->search_text); + for(size_t count = 0; count < node->action_count; ++count) { + free(node->actions[count]); + } + free(node->actions); + free(node); + return FALSE; +} + struct wl_list* wofi_read_cache(char* mode) { char* cache_path = get_cache_path(mode); struct wl_list* cache = malloc(sizeof(struct wl_list)); @@ -211,12 +292,19 @@ struct wl_list* wofi_read_cache(char* mode) { return cache; } -void wofi_insert_widget(char* mode, char* text, char* search_text, char* action) { +void wofi_insert_widget(char* mode, char** text, char* search_text, char** actions, size_t action_count) { struct node* widget = malloc(sizeof(struct node)); widget->mode = strdup(mode); - widget->text = strdup(text); + widget->text = malloc(action_count * sizeof(char*)); + for(size_t count = 0; count < action_count; ++count) { + widget->text[count] = strdup(text[count]); + } widget->search_text = strdup(search_text); - widget->action = strdup(action); + widget->action_count = action_count; + widget->actions = malloc(action_count * sizeof(char*)); + for(size_t count = 0; count < action_count; ++count) { + widget->actions[count] = strdup(actions[count]); + } g_idle_add(_insert_widget, widget); utils_sleep_millis(1); } @@ -245,60 +333,6 @@ void wofi_term_run(const char* cmd) { exit(1); } -static void execute_action(const gchar* mode, const gchar* cmd) { - struct mode* mode_ptr = map_get(modes, mode); - char* cache_path = get_cache_path(mode); - struct wl_list lines; - wl_list_init(&lines); - bool inc_count = false; - if(access(cache_path, R_OK) == 0) { - FILE* file = fopen(cache_path, "r"); - char* line = NULL; - size_t size = 0; - while(getline(&line, &size, file) != -1) { - struct cache_line* node = malloc(sizeof(struct cache_line)); - if(strstr(line, cmd) != NULL) { - uint64_t count = strtol(line, NULL, 10) + 1; - char num[6]; - snprintf(num, 5, "%" PRIu64, count); - node->line = utils_concat(4, num, " ", cmd, "\n"); - inc_count = true; - } else { - node->line = strdup(line); - } - wl_list_insert(&lines, &node->link); - } - free(line); - fclose(file); - } - if(!inc_count) { - struct cache_line* node = malloc(sizeof(struct cache_line)); - node->line = utils_concat(3, "1 ", cmd, "\n"); - wl_list_insert(&lines, &node->link); - } - - FILE* file = fopen(cache_path, "w"); - struct cache_line* node, *tmp; - wl_list_for_each_safe(node, tmp, &lines, link) { - fwrite(node->line, 1, strlen(node->line), file); - free(node->line); - wl_list_remove(&node->link); - free(node); - } - - fclose(file); - - free(cache_path); - mode_ptr->mode_exec(cmd); -} - -static void activate_item(GtkFlowBox* flow_box, GtkFlowBoxChild* row, gpointer data) { - (void) flow_box; - (void) data; - GtkWidget* box = gtk_bin_get_child(GTK_BIN(row)); - execute_action(wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "mode"), wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "action")); -} - static void select_item(GtkFlowBox* flow_box, gpointer data) { (void) data; if(previous_selection != NULL) { @@ -335,16 +369,23 @@ static void activate_search(GtkEntry* entry, gpointer data) { (void) data; GtkWidget* child = get_first_child(GTK_CONTAINER(inner_box)); if(exec_search || child == NULL) { - execute_action(mode, gtk_entry_get_text(entry)); + execute_action(mode, gtk_entry_get_text(entry), true); } else { GtkWidget* box = gtk_bin_get_child(GTK_BIN(child)); - execute_action(wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "mode"), wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "action")); + bool primary_action = GTK_IS_EXPANDER(box); + if(primary_action) { + box = gtk_expander_get_label_widget(GTK_EXPANDER(box)); + } + execute_action(wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "mode"), wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "action"), primary_action); } } static gboolean do_filter(GtkFlowBoxChild* row, gpointer data) { (void) data; GtkWidget* box = gtk_bin_get_child(GTK_BIN(row)); + if(GTK_IS_EXPANDER(box)) { + box = gtk_expander_get_label_widget(GTK_EXPANDER(box)); + } const gchar* text = wofi_property_box_get_property(WOFI_PROPERTY_BOX(box), "filter"); if(filter == NULL || strcmp(filter, "") == 0) { return TRUE;