diff options
author | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-06-14 22:27:18 +0200 |
---|---|---|
committer | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-17 23:10:59 +0100 |
commit | 140d9ebd912ed803dd916c6ab3783a79a2a724e3 (patch) | |
tree | 04d1ec36e60f94adc63b79491fc8d05e7bd2c9f4 /src/menu.cc | |
parent | 0f49ee5f746592352b6ac26449e87339cc3702a8 (diff) |
Add support for user actions in the link menu
Allows the user to define additional entries in the link menu which will
execute the given program/script. Each actions is defined using the
"link_action" option. The link URL is stored in the $url enviroment
variable and the current page in $origin, so the user can customize how
do the handling.
Here is a simple example to add three new entries:
link_action="Debug variables:echo url=$url origin=$origin"
link_action="Open in MPV:mpv $url"
link_action="Open in Firefox:firefox $url"
The command is spawned in a forked process using the system() call,
which uses the shell to expand any variable. In particular, the $url
variable is set to the current URL being opened.
Fixes: https://github.com/dillo-browser/dillo/issues/3
Diffstat (limited to 'src/menu.cc')
-rw-r--r-- | src/menu.cc | 121 |
1 files changed, 110 insertions, 11 deletions
diff --git a/src/menu.cc b/src/menu.cc index 7cea24a7..3cdc2f77 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -18,9 +18,12 @@ #include <FL/Fl.H> #include <FL/Fl_Menu_Item.H> +#include <unistd.h> +#include <errno.h> #include "lout/misc.hh" /* SimpleVector */ #include "msg.h" #include "menu.hh" +#include "actions.h" #include "uicmd.hh" #include "history.h" #include "html.hh" @@ -430,30 +433,123 @@ void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url, a_Timeout_add(0.0, Menu_popup_cb, (void*)&page_data); } -static Fl_Menu_Item link_menu[] = { +static Fl_Menu_Item link_menu_[] = { {"Open link in new tab", 0, Menu_open_url_nt_cb,0,0,0,0,0,0}, {"Open link in new window", 0, Menu_open_url_nw_cb,0,FL_MENU_DIVIDER,0,0, 0,0}, {"Bookmark this link", 0, Menu_add_bookmark_cb,0,0,0,0,0,0}, {"Copy link location", 0, Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0}, - {"Save link as...", 0, Menu_save_link_cb,0,0,0,0,0,0}, + {"Save link as...", 0, Menu_save_link_cb,0,FL_MENU_DIVIDER,0,0,0,0}, {0,0,0,0,0,0,0,0,0} }; -static void Menu_set_link_menu_user_data(void *user_data) +/* As we can only provide a pointer to the link menu items, we need to + * create an auxiliary structure to hold the current URL and the program + * that should run on each item. */ +struct link_menu_item { + const DilloUrl *url; + const DilloUrl *origin; + Action *action; +}; + +/** + * Open URL following a custom action + */ +static void Menu_open_url_action_cb(Fl_Widget*, void *user_data) +{ + /* Don't use popup_url because it is used for the image URL when coming from + * the image menu. We should get rid of the global variables and pass them + * via the user_data. */ + + struct link_menu_item *mitem = (struct link_menu_item *) user_data; + const DilloUrl *url = mitem->url; + const DilloUrl *origin = mitem->origin; + Action *action = mitem->action; + + /* Set the environment variables */ + setenv("url", URL_STR(url), 1); + setenv("origin", URL_STR(origin), 1); + + if (fork() == 0) { + /* Child */ + errno = 0; + int ret = system(action->cmd); + if (ret == -1) { + MSG("Cannot run '%s': %s\n", action->cmd, strerror(errno)); + exit(1); + } else if (ret != 0) { + MSG("Command exited with '%d': %s\n", ret, action->cmd); + exit(1); + } else { + /* All good, terminate the child */ + exit(0); + } + } +} + +static Fl_Menu_Item *get_link_menu(void) { - int i; + static Fl_Menu_Item *link_menu = NULL; + static struct link_menu_item *link_menu_item = NULL; + + /* Already initialized */ + if (link_menu != NULL) + return link_menu; - for (i = 0; link_menu[i].label(); i++) - link_menu[i].user_data(user_data); + Dlist *actions = a_Actions_link_get(); + int nactions = dList_length(actions); + + /* Count static menu entries */ + int nstatic = 0; + while (link_menu_[nstatic].text) + nstatic++; + + int ntotal = nstatic + nactions; + link_menu = (Fl_Menu_Item *) calloc(ntotal + 1, sizeof(Fl_Menu_Item)); + link_menu_item = (struct link_menu_item *) calloc(nactions, sizeof(struct link_menu_item)); + + /* Just copy the static entries */ + for (int i = 0; i < nstatic; i++) { + memcpy(&link_menu[i], &link_menu_[i], sizeof(Fl_Menu_Item)); + } + + /* And append the dynamic ones */ + for (int i = 0; i < nactions; i++) { + Action *action = (Action *) dList_nth_data(actions, i); + struct link_menu_item *mitem = &link_menu_item[i]; + mitem->url = NULL; /* Not known yet */ + mitem->action = action; + + Fl_Menu_Item *item = &link_menu[nstatic + i]; + item->text = action->label; + item->callback_ = Menu_open_url_action_cb; + item->user_data_ = mitem; + } + + return link_menu; +} + +static void Menu_set_link_menu_user_data(const DilloUrl *url, const DilloUrl *page_url) +{ + Fl_Menu_Item *link_menu = get_link_menu(); + for (int i = 0; link_menu[i].label(); i++) { + if (link_menu[i].callback_ == Menu_open_url_action_cb) { + struct link_menu_item *mitem = (struct link_menu_item *) link_menu[i].user_data_; + /* Set the url and origin */ + mitem->url = url; + mitem->origin = page_url; + } else { + link_menu[i].user_data_ = (void *) url; + } + } } /** * Link popup menu (construction & popup) */ -void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url) +void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url, const DilloUrl *page_url) { - static Menu_popup_data_t link_data = {"Link menu", NULL, link_menu}; + static Menu_popup_data_t link_data = {"Link menu", NULL, NULL}; popup_x = Fl::event_x(); popup_y = Fl::event_y(); @@ -461,7 +557,10 @@ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url) a_Url_free(popup_url); popup_url = a_Url_dup(url); - Menu_set_link_menu_user_data(popup_url); + Fl_Menu_Item *link_menu = get_link_menu(); + link_data.menu = link_menu; + + Menu_set_link_menu_user_data(popup_url, page_url); a_Timeout_add(0.0, Menu_popup_cb, (void*)&link_data); } @@ -484,7 +583,7 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url, {"Bookmark this image", 0, Menu_add_bookmark_cb,0,0,0,0,0,0}, {"Copy image location", 0,Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0}, {"Save image as...", 0, Menu_save_link_cb, 0, FL_MENU_DIVIDER,0,0,0,0}, - {"Link menu", 0, Menu_nop_cb, link_menu, FL_SUBMENU_POINTER,0,0,0,0}, + {"Link menu", 0, Menu_nop_cb, get_link_menu(), FL_SUBMENU_POINTER,0,0,0,0}, {0,0,0,0,0,0,0,0,0} }; static Menu_popup_data_t image_data = {"Image menu", NULL, pm}; @@ -517,7 +616,7 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url, if (link_url) { pm[7].activate(); - Menu_set_link_menu_user_data(popup_link_url); + Menu_set_link_menu_user_data(popup_link_url, popup_page_url); } else { pm[7].deactivate(); } |