mirror of
https://github.com/rkd77/elinks.git
synced 2024-12-04 14:46:47 -05:00
1127 lines
29 KiB
C
1127 lines
29 KiB
C
/* Menu system */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "elinks.h"
|
|
|
|
#include "bfu/dialog.h"
|
|
#include "bfu/leds.h"
|
|
#include "bfu/menu.h"
|
|
#include "config/options.h"
|
|
#include "config/urlhist.h"
|
|
#include "document/document.h"
|
|
#include "document/view.h"
|
|
#include "dialogs/exmode.h"
|
|
#include "dialogs/info.h"
|
|
#include "dialogs/menu.h"
|
|
#include "dialogs/options.h"
|
|
#include "intl/libintl.h"
|
|
#include "main/event.h"
|
|
#include "main/main.h"
|
|
#include "main/select.h"
|
|
#include "mime/dialogs.h"
|
|
#include "mime/mime.h"
|
|
#include "network/connection.h"
|
|
#include "osdep/osdep.h"
|
|
#include "osdep/newwin.h"
|
|
#include "protocol/protocol.h"
|
|
#include "protocol/uri.h"
|
|
#include "session/download.h"
|
|
#include "session/history.h"
|
|
#include "session/location.h"
|
|
#include "session/session.h"
|
|
#include "session/task.h"
|
|
#include "terminal/tab.h"
|
|
#include "terminal/terminal.h"
|
|
#include "util/conv.h"
|
|
#include "util/file.h"
|
|
#include "util/memlist.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
#include "viewer/action.h"
|
|
#include "viewer/text/link.h"
|
|
#include "viewer/text/view.h"
|
|
|
|
|
|
/* Helper for url items in help menu. */
|
|
static void
|
|
menu_url_shortcut(struct terminal *term, void *url_, void *ses_)
|
|
{
|
|
char *url = (char *)url_;
|
|
struct session *ses = (struct session *)ses_;
|
|
struct uri *uri = get_uri(url, URI_NONE);
|
|
|
|
if (!uri) return;
|
|
goto_uri(ses, uri);
|
|
done_uri(uri);
|
|
}
|
|
|
|
static void
|
|
save_url(struct session *ses, char *url)
|
|
{
|
|
struct document_view *doc_view;
|
|
struct uri *uri;
|
|
|
|
assert(ses && ses->tab && ses->tab->term && url);
|
|
if_assert_failed return;
|
|
|
|
if (!*url) return;
|
|
|
|
uri = get_translated_uri(url, ses->tab->term->cwd);
|
|
if (!uri) {
|
|
print_error_dialog(ses, connection_state(S_BAD_URL),
|
|
uri, PRI_CANCEL);
|
|
return;
|
|
}
|
|
|
|
if (ses->download_uri) done_uri(ses->download_uri);
|
|
ses->download_uri = uri;
|
|
|
|
doc_view = current_frame(ses);
|
|
assert(doc_view && doc_view->document && doc_view->document->uri);
|
|
if_assert_failed return;
|
|
|
|
set_session_referrer(ses, doc_view->document->uri);
|
|
query_file(ses, ses->download_uri, ses, start_download, NULL, 1);
|
|
}
|
|
|
|
void
|
|
save_url_as(struct session *ses)
|
|
{
|
|
input_dialog(ses->tab->term, NULL,
|
|
N_("Save URL"), N_("Enter URL"),
|
|
ses, &goto_url_history,
|
|
MAX_STR_LEN, "", 0, 0, NULL,
|
|
(void (*)(void *, char *)) save_url,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
really_exit_prog(void *ses_)
|
|
{
|
|
struct session *ses = (struct session *)ses_;
|
|
|
|
register_bottom_half(destroy_terminal, ses->tab->term);
|
|
}
|
|
|
|
static inline void
|
|
dont_exit_prog(void *ses_)
|
|
{
|
|
struct session *ses = (struct session *)ses_;
|
|
|
|
ses->exit_query = 0;
|
|
}
|
|
|
|
void
|
|
query_exit(struct session *ses)
|
|
{
|
|
/* [gettext_accelerator_context(query_exit)] */
|
|
ses->exit_query = 1;
|
|
msg_box(ses->tab->term, NULL, 0,
|
|
N_("Exit ELinks"), ALIGN_CENTER,
|
|
(ses->tab->term->next == ses->tab->term->prev && are_there_downloads())
|
|
? N_("Do you really want to exit ELinks "
|
|
"(and terminate all downloads)?")
|
|
: N_("Do you really want to exit ELinks?"),
|
|
ses, 2,
|
|
MSG_BOX_BUTTON(N_("~Yes"), really_exit_prog, B_ENTER),
|
|
MSG_BOX_BUTTON(N_("~No"), dont_exit_prog, B_ESC));
|
|
}
|
|
|
|
void
|
|
exit_prog(struct session *ses, int query)
|
|
{
|
|
assert(ses);
|
|
|
|
/* An exit query is in progress. */
|
|
if (ses->exit_query)
|
|
return;
|
|
|
|
/* Force a query if the last terminal is exiting with downloads still in
|
|
* progress. */
|
|
if (query || (list_is_singleton(terminals) && are_there_downloads())) {
|
|
query_exit(ses);
|
|
return;
|
|
}
|
|
|
|
really_exit_prog(ses);
|
|
}
|
|
|
|
|
|
static void
|
|
go_historywards(struct terminal *term, void *target_, void *ses_)
|
|
{
|
|
struct location *target = (struct location *)target_;
|
|
struct session *ses = (struct session *)ses_;
|
|
|
|
go_history(ses, target);
|
|
}
|
|
|
|
static struct menu_item no_hist_menu[] = {
|
|
INIT_MENU_ITEM(N_("No history"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
/* unhist == 0 => history
|
|
* unhist == 1 => unhistory */
|
|
static void
|
|
history_menu_common(struct terminal *term, struct session *ses, int unhist)
|
|
{
|
|
struct menu_item *mi = NULL;
|
|
|
|
if (have_location(ses)) {
|
|
struct location *loc;
|
|
|
|
for (loc = unhist ? cur_loc(ses)->next : cur_loc(ses)->prev;
|
|
loc != (struct location *) &ses->history.history;
|
|
loc = unhist ? loc->next : loc->prev) {
|
|
char *url;
|
|
|
|
if (!mi) {
|
|
mi = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
|
|
if (!mi) return;
|
|
}
|
|
|
|
url = get_uri_string(loc->vs.uri, URI_PUBLIC);
|
|
if (url) {
|
|
add_to_menu(&mi, url, NULL, ACT_MAIN_NONE,
|
|
go_historywards,
|
|
(void *) loc, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!mi)
|
|
do_menu(term, no_hist_menu, ses, 0);
|
|
else
|
|
do_menu(term, mi, ses, 0);
|
|
}
|
|
|
|
static void
|
|
history_menu(struct terminal *term, void *xxx, void *ses_)
|
|
{
|
|
struct session *ses = (struct session *)ses_;
|
|
|
|
history_menu_common(term, ses, 0);
|
|
}
|
|
|
|
static void
|
|
unhistory_menu(struct terminal *term, void *xxx, void *ses_)
|
|
{
|
|
struct session *ses = (struct session *)ses_;
|
|
|
|
history_menu_common(term, ses, 1);
|
|
}
|
|
|
|
void
|
|
tab_menu(struct session *ses, int x, int y, int place_above_cursor)
|
|
{
|
|
/* [gettext_accelerator_context(tab_menu)] */
|
|
struct menu_item *menu;
|
|
int tabs_count;
|
|
#ifdef CONFIG_BOOKMARKS
|
|
int anonymous = get_cmd_opt_bool("anonymous");
|
|
#endif
|
|
|
|
assert(ses && ses->tab);
|
|
if_assert_failed return;
|
|
|
|
tabs_count = number_of_tabs(ses->tab->term);
|
|
menu = new_menu(FREE_LIST);
|
|
if (!menu) return;
|
|
|
|
add_menu_action(&menu, N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK);
|
|
add_menu_action(&menu, N_("Go for~ward"), ACT_MAIN_HISTORY_MOVE_FORWARD);
|
|
|
|
if (have_location(ses)) {
|
|
add_menu_separator(&menu);
|
|
|
|
#ifdef CONFIG_BOOKMARKS
|
|
if (!anonymous) {
|
|
add_menu_action(&menu, N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK);
|
|
}
|
|
#endif
|
|
|
|
add_menu_action(&menu, N_("Toggle ~HTML/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN);
|
|
add_menu_action(&menu, N_("~Reload"), ACT_MAIN_RELOAD);
|
|
|
|
if (ses->doc_view && document_has_frames(ses->doc_view->document)) {
|
|
add_menu_action(&menu, N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE);
|
|
add_uri_command_to_menu(&menu, PASS_URI_FRAME,
|
|
N_("~Pass frame URI to external command"));
|
|
}
|
|
}
|
|
|
|
/* Keep tab related operations below this separator */
|
|
add_menu_separator(&menu);
|
|
|
|
if (tabs_count > 1) {
|
|
add_menu_action(&menu, N_("Nex~t tab"), ACT_MAIN_TAB_NEXT);
|
|
add_menu_action(&menu, N_("Pre~v tab"), ACT_MAIN_TAB_PREV);
|
|
}
|
|
|
|
add_menu_action(&menu, N_("~Close tab"), ACT_MAIN_TAB_CLOSE);
|
|
|
|
if (tabs_count > 1) {
|
|
add_menu_action(&menu, N_("C~lose all tabs but the current"),
|
|
ACT_MAIN_TAB_CLOSE_ALL_BUT_CURRENT);
|
|
#ifdef CONFIG_BOOKMARKS
|
|
if (!anonymous) {
|
|
add_menu_action(&menu, N_("B~ookmark all tabs"),
|
|
ACT_MAIN_ADD_BOOKMARK_TABS);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (have_location(ses)) {
|
|
add_uri_command_to_menu(&menu, PASS_URI_TAB,
|
|
N_("Pass tab URI to e~xternal command"));
|
|
}
|
|
|
|
/* Adjust the menu position taking the menu frame into account */
|
|
if (place_above_cursor) {
|
|
int i = 0;
|
|
|
|
while (menu[i].text) i++;
|
|
|
|
y = int_max(y - i - 1, 0);
|
|
}
|
|
|
|
set_window_ptr(ses->tab, x, y);
|
|
|
|
do_menu(ses->tab->term, menu, ses, 1);
|
|
}
|
|
|
|
static void
|
|
do_submenu(struct terminal *term, void *menu_, void *ses_)
|
|
{
|
|
struct menu_item *menu = (struct menu_item *)menu_;
|
|
|
|
do_menu(term, menu, ses_, 1);
|
|
}
|
|
|
|
|
|
static struct menu_item file_menu11[] = {
|
|
/* [gettext_accelerator_context(.file_menu)] */
|
|
INIT_MENU_ACTION(N_("Open new ~tab"), ACT_MAIN_OPEN_NEW_TAB),
|
|
INIT_MENU_ACTION(N_("Open new tab in backgroun~d"), ACT_MAIN_OPEN_NEW_TAB_IN_BACKGROUND),
|
|
INIT_MENU_ACTION(N_("~Go to URL"), ACT_MAIN_GOTO_URL),
|
|
INIT_MENU_ACTION(N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK),
|
|
INIT_MENU_ACTION(N_("Go ~forward"), ACT_MAIN_HISTORY_MOVE_FORWARD),
|
|
INIT_MENU_ITEM(N_("~History"), NULL, ACT_MAIN_NONE, history_menu, NULL, SUBMENU),
|
|
INIT_MENU_ITEM(N_("~Unhistory"), NULL, ACT_MAIN_NONE, unhistory_menu, NULL, SUBMENU),
|
|
};
|
|
|
|
static struct menu_item file_menu21[] = {
|
|
/* [gettext_accelerator_context(.file_menu)] */
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ACTION(N_("~Save as"), ACT_MAIN_SAVE_AS),
|
|
INIT_MENU_ACTION(N_("Save UR~L as"), ACT_MAIN_SAVE_URL_AS),
|
|
INIT_MENU_ACTION(N_("Sa~ve formatted document"), ACT_MAIN_SAVE_FORMATTED),
|
|
#ifdef CONFIG_BOOKMARKS
|
|
INIT_MENU_ACTION(N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK),
|
|
#endif
|
|
};
|
|
|
|
static struct menu_item file_menu22[] = {
|
|
/* [gettext_accelerator_context(.file_menu)] */
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ACTION(N_("~Kill background connections"), ACT_MAIN_KILL_BACKGROUNDED_CONNECTIONS),
|
|
INIT_MENU_ACTION(N_("Flush all ~caches"), ACT_MAIN_CACHE_MINIMIZE),
|
|
INIT_MENU_ACTION(N_("Resource ~info"), ACT_MAIN_RESOURCE_INFO),
|
|
BAR_MENU_ITEM,
|
|
};
|
|
|
|
static struct menu_item file_menu3[] = {
|
|
/* [gettext_accelerator_context(.file_menu)] */
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ACTION(N_("E~xit"), ACT_MAIN_QUIT),
|
|
NULL_MENU_ITEM,
|
|
};
|
|
|
|
static void
|
|
do_file_menu(struct terminal *term, void *xxx, void *ses_)
|
|
{
|
|
/* [gettext_accelerator_context(.file_menu)] */
|
|
struct menu_item *file_menu, *e, *f;
|
|
int anonymous = get_cmd_opt_bool("anonymous");
|
|
int x, o;
|
|
|
|
file_menu = (struct menu_item *)mem_alloc(sizeof(file_menu11) + sizeof(file_menu21)
|
|
+ sizeof(file_menu22) + sizeof(file_menu3)
|
|
+ 3 * sizeof(struct menu_item));
|
|
if (!file_menu) return;
|
|
|
|
e = file_menu;
|
|
|
|
if (!anonymous
|
|
&& !get_cmd_opt_bool("no-connect")
|
|
&& !get_cmd_opt_bool("no-home"))
|
|
o = can_open_in_new(term);
|
|
else
|
|
o = 0;
|
|
|
|
if (o) {
|
|
SET_MENU_ITEM(e, N_("Open ~new window"), NULL, ACT_MAIN_OPEN_NEW_WINDOW,
|
|
open_in_new_window, send_open_new_window,
|
|
(o - 1) ? SUBMENU : 0, HKS_SHOW, 0);
|
|
e++;
|
|
}
|
|
|
|
memcpy(e, file_menu11, sizeof(file_menu11));
|
|
e += sizeof_array(file_menu11);
|
|
|
|
if (!anonymous) {
|
|
memcpy(e, file_menu21, sizeof(file_menu21));
|
|
e += sizeof_array(file_menu21);
|
|
}
|
|
|
|
memcpy(e, file_menu22, sizeof(file_menu22));
|
|
e += sizeof_array(file_menu22);
|
|
|
|
x = 1;
|
|
if (!anonymous && can_open_os_shell(term->environment)) {
|
|
SET_MENU_ITEM(e, N_("~OS shell"), NULL, ACT_MAIN_OPEN_OS_SHELL,
|
|
NULL, NULL, 0, HKS_SHOW, 0);
|
|
e++;
|
|
x = 0;
|
|
}
|
|
|
|
if (can_resize_window(term->environment)) {
|
|
SET_MENU_ITEM(e, N_("Resize t~erminal"), NULL, ACT_MAIN_TERMINAL_RESIZE,
|
|
NULL, NULL, 0, HKS_SHOW, 0);
|
|
e++;
|
|
x = 0;
|
|
}
|
|
|
|
memcpy(e, file_menu3 + x, sizeof(file_menu3) - x * sizeof(struct menu_item));
|
|
e += sizeof_array(file_menu3);
|
|
|
|
for (f = file_menu; f < e; f++)
|
|
f->flags |= FREE_LIST;
|
|
|
|
do_menu(term, file_menu, ses_, 1);
|
|
}
|
|
|
|
static struct menu_item view_menu[] = {
|
|
/* [gettext_accelerator_context(.view_menu)] */
|
|
INIT_MENU_ACTION(N_("~Search"), ACT_MAIN_SEARCH),
|
|
INIT_MENU_ACTION(N_("Search ~backward"), ACT_MAIN_SEARCH_BACK),
|
|
INIT_MENU_ACTION(N_("Find ~next"), ACT_MAIN_FIND_NEXT),
|
|
INIT_MENU_ACTION(N_("Find ~previous"), ACT_MAIN_FIND_NEXT_BACK),
|
|
INIT_MENU_ACTION(N_("T~ypeahead search"), ACT_MAIN_SEARCH_TYPEAHEAD),
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ACTION(N_("Toggle ~HTML/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN),
|
|
INIT_MENU_ACTION(N_("Toggle i~mages"), ACT_MAIN_TOGGLE_DISPLAY_IMAGES),
|
|
INIT_MENU_ACTION(N_("Toggle ~link numbering"), ACT_MAIN_TOGGLE_NUMBERED_LINKS),
|
|
INIT_MENU_ACTION(N_("Toggle ~document colors"), ACT_MAIN_TOGGLE_DOCUMENT_COLORS),
|
|
INIT_MENU_ACTION(N_("~Wrap text on/off"), ACT_MAIN_TOGGLE_WRAP_TEXT),
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ACTION(N_("Document ~info"), ACT_MAIN_DOCUMENT_INFO),
|
|
INIT_MENU_ACTION(N_("H~eader info"), ACT_MAIN_HEADER_INFO),
|
|
INIT_MENU_ACTION(N_("Rel~oad document"), ACT_MAIN_RELOAD),
|
|
INIT_MENU_ACTION(N_("~Rerender document"), ACT_MAIN_RERENDER),
|
|
INIT_MENU_ACTION(N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE),
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ACTION(N_("Nex~t tab"), ACT_MAIN_TAB_NEXT),
|
|
INIT_MENU_ACTION(N_("Pre~v tab"), ACT_MAIN_TAB_PREV),
|
|
INIT_MENU_ACTION(N_("~Close tab"), ACT_MAIN_TAB_CLOSE),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
|
|
static struct menu_item help_menu[] = {
|
|
/* [gettext_accelerator_context(.help_menu)] */
|
|
INIT_MENU_ITEM(N_("~ELinks homepage"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_WEBSITE_URL, 0),
|
|
INIT_MENU_ITEM(N_("~Documentation"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_DOC_URL, 0),
|
|
INIT_MENU_ITEM(N_("~Keys"), NULL, ACT_MAIN_NONE, menu_keys, NULL, 0),
|
|
#ifdef CONFIG_LEDS
|
|
INIT_MENU_ITEM(N_("LED ~indicators"), NULL, ACT_MAIN_NONE, menu_leds_info, NULL, 0),
|
|
#endif
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ITEM(N_("~Bugs information"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_BUGS_URL, 0),
|
|
#ifdef CONFIG_DEBUG
|
|
INIT_MENU_ITEM(N_("ELinks ~GITWeb"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_GITWEB_URL, 0),
|
|
#endif
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ITEM(N_("~Copying"), NULL, ACT_MAIN_NONE, menu_copying, NULL, 0),
|
|
INIT_MENU_ITEM(N_("Autho~rs"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_AUTHORS_URL, 0),
|
|
INIT_MENU_ITEM(N_("~About"), NULL, ACT_MAIN_NONE, menu_about, NULL, 0),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
|
|
static struct menu_item ext_menu[] = {
|
|
/* [gettext_accelerator_context(.ext_menu)] */
|
|
INIT_MENU_ITEM(N_("~Add"), NULL, ACT_MAIN_NONE, menu_add_ext, NULL, 0),
|
|
INIT_MENU_ITEM(N_("~Modify"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_add_ext, SUBMENU),
|
|
INIT_MENU_ITEM(N_("~Delete"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_del_ext, SUBMENU),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
static struct menu_item setup_menu[] = {
|
|
/* [gettext_accelerator_context(.setup_menu)] */
|
|
#ifdef CONFIG_NLS
|
|
INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
|
|
#endif
|
|
INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
|
|
INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
|
|
INIT_MENU_ITEM(N_("File ~extensions"), NULL, ACT_MAIN_NONE, do_submenu, ext_menu, SUBMENU),
|
|
BAR_MENU_ITEM,
|
|
INIT_MENU_ACTION(N_("~Options manager"), ACT_MAIN_OPTIONS_MANAGER),
|
|
INIT_MENU_ACTION(N_("~Keybinding manager"), ACT_MAIN_KEYBINDING_MANAGER),
|
|
INIT_MENU_ACTION(N_("~Save options"), ACT_MAIN_SAVE_OPTIONS),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
static struct menu_item setup_menu_anon[] = {
|
|
/* [gettext_accelerator_context(.setup_menu)] */
|
|
INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
|
|
INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
|
|
INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
static struct menu_item tools_menu[] = {
|
|
/* [gettext_accelerator_context(.tools_menu)] */
|
|
#ifdef CONFIG_GLOBHIST
|
|
INIT_MENU_ACTION(N_("Global ~history"), ACT_MAIN_HISTORY_MANAGER),
|
|
#endif
|
|
#ifdef CONFIG_BOOKMARKS
|
|
INIT_MENU_ACTION(N_("~Bookmarks"), ACT_MAIN_BOOKMARK_MANAGER),
|
|
#endif
|
|
INIT_MENU_ACTION(N_("~Cache"), ACT_MAIN_CACHE_MANAGER),
|
|
INIT_MENU_ACTION(N_("~Downloads"), ACT_MAIN_DOWNLOAD_MANAGER),
|
|
#ifdef CONFIG_COOKIES
|
|
INIT_MENU_ACTION(N_("Coo~kies"), ACT_MAIN_COOKIE_MANAGER),
|
|
#endif
|
|
#ifdef CONFIG_FORMHIST
|
|
INIT_MENU_ACTION(N_("~Form history"), ACT_MAIN_FORMHIST_MANAGER),
|
|
#endif
|
|
INIT_MENU_ACTION(N_("~Authentication"), ACT_MAIN_AUTH_MANAGER),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
static void
|
|
do_setup_menu(struct terminal *term, void *xxx, void *ses_)
|
|
{
|
|
struct session *ses = (struct session *)ses_;
|
|
|
|
if (!get_cmd_opt_bool("anonymous"))
|
|
do_menu(term, setup_menu, ses, 1);
|
|
else
|
|
do_menu(term, setup_menu_anon, ses, 1);
|
|
}
|
|
|
|
static struct menu_item main_menu[] = {
|
|
/* [gettext_accelerator_context(.main_menu)] */
|
|
INIT_MENU_ITEM(N_("~File"), NULL, ACT_MAIN_NONE, do_file_menu, NULL, FREE_LIST | SUBMENU),
|
|
INIT_MENU_ITEM(N_("~View"), NULL, ACT_MAIN_NONE, do_submenu, view_menu, FREE_LIST | SUBMENU),
|
|
INIT_MENU_ITEM(N_("~Link"), NULL, ACT_MAIN_NONE, link_menu, NULL, FREE_LIST | SUBMENU),
|
|
INIT_MENU_ITEM(N_("~Tools"), NULL, ACT_MAIN_NONE, do_submenu, tools_menu, FREE_LIST | SUBMENU),
|
|
INIT_MENU_ITEM(N_("~Setup"), NULL, ACT_MAIN_NONE, do_setup_menu, NULL, FREE_LIST | SUBMENU),
|
|
INIT_MENU_ITEM(N_("~Help"), NULL, ACT_MAIN_NONE, do_submenu, help_menu, FREE_LIST | SUBMENU),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
void
|
|
activate_bfu_technology(struct session *ses, int item)
|
|
{
|
|
do_mainmenu(ses->tab->term, main_menu, ses, item);
|
|
}
|
|
|
|
|
|
void
|
|
dialog_goto_url(struct session *ses, char *url)
|
|
{
|
|
input_dialog(ses->tab->term, NULL,
|
|
N_("Go to URL"), N_("Enter URL"),
|
|
ses, &goto_url_history,
|
|
MAX_STR_LEN, url, 0, 0, NULL,
|
|
(void (*)(void *, char *)) goto_url_with_hook,
|
|
NULL);
|
|
}
|
|
|
|
|
|
static INIT_INPUT_HISTORY(file_history);
|
|
|
|
void
|
|
query_file(struct session *ses, struct uri *uri, void *data,
|
|
void (*std)(void *, char *),
|
|
void (*cancel)(void *), int interactive)
|
|
{
|
|
struct string def;
|
|
|
|
assert(ses && uri);
|
|
if_assert_failed return;
|
|
|
|
/* FIXME: This ``sanity'' checking is mostly for the download code
|
|
* using this function. They pass ses->download_uri and we have to make
|
|
* sure that the connection code can download the URI. The reason we do
|
|
* it before is that then users won't waste time typing a filename and
|
|
* then discover that the URI can not be downloaded. However it might
|
|
* be better to introduce a set_session_download_uri() which will do
|
|
* the checking? --jonas */
|
|
|
|
if (uri->protocol == PROTOCOL_UNKNOWN) {
|
|
print_error_dialog(ses, connection_state(S_UNKNOWN_PROTOCOL),
|
|
uri, PRI_CANCEL);
|
|
return;
|
|
}
|
|
|
|
if (get_protocol_external_handler(ses->tab->term, uri)) {
|
|
print_error_dialog(ses, connection_state(S_EXTERNAL_PROTOCOL),
|
|
uri, PRI_CANCEL);
|
|
return;
|
|
}
|
|
|
|
if (!init_string(&def)) return;
|
|
|
|
add_to_string(&def, get_opt_str("document.download.directory", NULL));
|
|
if (def.length && !dir_sep(def.source[def.length - 1]))
|
|
add_char_to_string(&def, '/');
|
|
|
|
add_mime_filename_to_string(&def, uri);
|
|
|
|
/* Remove the %-ugliness for display */
|
|
#ifdef CONFIG_UTF8
|
|
if (ses->tab->term->utf8_cp)
|
|
decode_uri_string(&def);
|
|
else
|
|
#endif /* CONFIG_UTF8 */
|
|
decode_uri_string_for_display(&def);
|
|
|
|
if (interactive) {
|
|
input_dialog(ses->tab->term, NULL,
|
|
N_("Download"), N_("Save to file"),
|
|
data, &file_history,
|
|
MAX_STR_LEN, def.source, 0, 0, check_nonempty,
|
|
(void (*)(void *, char *)) std,
|
|
(void (*)(void *)) cancel);
|
|
} else {
|
|
std(data, def.source);
|
|
}
|
|
|
|
done_string(&def);
|
|
}
|
|
|
|
void
|
|
free_history_lists(void)
|
|
{
|
|
free_list(file_history.entries);
|
|
#ifdef CONFIG_SCRIPTING
|
|
trigger_event_name("free-history");
|
|
#endif
|
|
}
|
|
|
|
|
|
static void
|
|
add_cmdline_bool_option(struct string *string, const char *name)
|
|
{
|
|
if (!get_cmd_opt_bool(name)) return;
|
|
add_to_string(string, " -");
|
|
add_to_string(string, name);
|
|
}
|
|
|
|
void
|
|
open_uri_in_new_window(struct session *ses, struct uri *uri, struct uri *referrer,
|
|
term_env_type_T env, cache_mode_T cache_mode,
|
|
enum task_type task)
|
|
{
|
|
int ring = get_cmd_opt_int("session-ring");
|
|
struct string parameters;
|
|
int id;
|
|
|
|
assert(env && ses);
|
|
if_assert_failed return;
|
|
|
|
id = add_session_info(ses, uri, referrer, cache_mode, task);
|
|
if (id < 1) return;
|
|
|
|
if (!init_string(¶meters)) return;
|
|
|
|
add_format_to_string(¶meters, "-base-session %d", id);
|
|
if (ring) add_format_to_string(¶meters, " -session-ring %d", ring);
|
|
|
|
/* No URI means open new (clean) window possibly without connecting to
|
|
* the current master so add command line options to properly clone the
|
|
* current master */
|
|
if (!uri) {
|
|
/* Adding -touch-files will only lead to problems */
|
|
add_cmdline_bool_option(¶meters, "localhost");
|
|
add_cmdline_bool_option(¶meters, "no-home");
|
|
add_cmdline_bool_option(¶meters, "no-connect");
|
|
}
|
|
|
|
open_new_window(ses->tab->term, program.path, env, parameters.source);
|
|
done_string(¶meters);
|
|
}
|
|
|
|
/* Open a link in a new xterm. */
|
|
void
|
|
send_open_in_new_window(struct terminal *term, const struct open_in_new *open,
|
|
struct session *ses)
|
|
{
|
|
struct document_view *doc_view;
|
|
struct link *link;
|
|
struct uri *uri;
|
|
|
|
assert(term && open && ses);
|
|
if_assert_failed return;
|
|
doc_view = current_frame(ses);
|
|
assert(doc_view && doc_view->vs && doc_view->document);
|
|
if_assert_failed return;
|
|
|
|
link = get_current_link(doc_view);
|
|
if (!link) return;
|
|
|
|
uri = get_link_uri(ses, doc_view, link);
|
|
if (!uri) return;
|
|
|
|
open_uri_in_new_window(ses, uri, NULL, open->env,
|
|
CACHE_MODE_NORMAL, TASK_NONE);
|
|
done_uri(uri);
|
|
}
|
|
|
|
void
|
|
send_open_new_window(struct terminal *term, const struct open_in_new *open,
|
|
struct session *ses)
|
|
{
|
|
open_uri_in_new_window(ses, NULL, NULL, open->env,
|
|
CACHE_MODE_NORMAL, TASK_NONE);
|
|
}
|
|
|
|
|
|
void
|
|
open_in_new_window(struct terminal *term, void *func_, void *ses_)
|
|
{
|
|
menu_func_T func = (menu_func_T)func_;
|
|
struct session *ses = (struct session *)ses_;
|
|
struct menu_item *mi;
|
|
int posibilities;
|
|
|
|
assert(term && ses && func);
|
|
if_assert_failed return;
|
|
|
|
switch (can_open_in_new(term)) {
|
|
case 0:
|
|
return;
|
|
|
|
case 1:
|
|
mi = NULL;
|
|
break;
|
|
|
|
default:
|
|
mi = new_menu(FREE_LIST);
|
|
if (!mi) return;
|
|
}
|
|
|
|
foreach_open_in_new (posibilities, term->environment) {
|
|
const struct open_in_new *oi = &open_in_new[posibilities];
|
|
|
|
if (mi == NULL) {
|
|
func(term, (void *) oi, ses);
|
|
return;
|
|
}
|
|
add_to_menu(&mi, oi->text, NULL, ACT_MAIN_NONE, func, (void *) oi, 0);
|
|
}
|
|
|
|
do_menu(term, mi, ses, 1);
|
|
}
|
|
|
|
|
|
void
|
|
add_new_win_to_menu(struct menu_item **mi, char *text,
|
|
struct terminal *term)
|
|
{
|
|
int c = can_open_in_new(term);
|
|
|
|
if (!c) return;
|
|
|
|
/* The URI is saved as session info in the master and not sent to the
|
|
* instance in the new window so with -no-connect or -no-home enabled
|
|
* it is not possible to open links URIs. For -anonymous one window
|
|
* should be enough. */
|
|
if (get_cmd_opt_bool("no-connect")
|
|
|| get_cmd_opt_bool("no-home")
|
|
|| get_cmd_opt_bool("anonymous"))
|
|
return;
|
|
|
|
add_to_menu(mi, text, NULL, ACT_MAIN_OPEN_LINK_IN_NEW_WINDOW,
|
|
open_in_new_window,
|
|
(void *)send_open_in_new_window, c - 1 ? SUBMENU : 0);
|
|
}
|
|
|
|
|
|
static void
|
|
do_pass_uri_to_command(struct terminal *term, void *command_, void *xxx)
|
|
{
|
|
char *command = (char *)command_;
|
|
int block = command[0] == 'b' ? TERM_EXEC_BG : TERM_EXEC_FG;
|
|
|
|
exec_on_terminal(term, command + 1, "", block);
|
|
mem_free(command);
|
|
}
|
|
|
|
/* TODO:
|
|
* - Support for passing MIME type
|
|
* - Merge this function with rewrite_uri(), subst_cmd(), subst_file()
|
|
* and subst_url(). */
|
|
static char *
|
|
format_command(char *format, struct uri *uri)
|
|
{
|
|
struct string string;
|
|
|
|
if (!init_string(&string)) return NULL;
|
|
|
|
while (*format) {
|
|
int pos = 0;
|
|
|
|
while (format[pos] && format[pos] != '%') pos++;
|
|
|
|
add_bytes_to_string(&string, format, pos);
|
|
format += pos;
|
|
|
|
if (*format != '%') continue;
|
|
|
|
format++;
|
|
switch (*format) {
|
|
case 'c':
|
|
{
|
|
char *str = struri(uri);
|
|
int length = get_real_uri_length(uri);
|
|
|
|
add_shell_quoted_to_string(&string,
|
|
str, length);
|
|
break;
|
|
}
|
|
case '%':
|
|
add_char_to_string(&string, '%');
|
|
break;
|
|
default:
|
|
add_bytes_to_string(&string, format - 1, 2);
|
|
break;
|
|
}
|
|
if (*format) format++;
|
|
}
|
|
|
|
return string.source;
|
|
}
|
|
|
|
enum frame_event_status
|
|
pass_uri_to_command(struct session *ses, struct document_view *doc_view,
|
|
int which_type)
|
|
{
|
|
LIST_OF(struct option) *tree = get_opt_tree("document.uri_passing",
|
|
NULL);
|
|
pass_uri_type_T type = which_type;
|
|
struct menu_item *items;
|
|
struct option *option, *sub;
|
|
struct uri *uri;
|
|
int commands = 0;
|
|
|
|
switch (type) {
|
|
case PASS_URI_FRAME:
|
|
uri = get_uri_reference((doc_view->vs && doc_view->vs->uri)
|
|
? doc_view->vs->uri : doc_view->document->uri);
|
|
break;
|
|
|
|
case PASS_URI_LINK:
|
|
{
|
|
struct link *link = get_current_link(doc_view);
|
|
|
|
if (!link) return FRAME_EVENT_OK;
|
|
|
|
uri = get_link_uri(ses, doc_view, link);
|
|
if (!uri) return FRAME_EVENT_OK;
|
|
break;
|
|
}
|
|
default:
|
|
case PASS_URI_TAB:
|
|
uri = have_location(ses) ? cur_loc(ses)->vs.uri : ses->loading_uri;
|
|
if (!uri) {
|
|
return FRAME_EVENT_OK;
|
|
}
|
|
uri = get_uri_reference(uri);
|
|
};
|
|
|
|
items = new_menu(FREE_LIST | FREE_TEXT | FREE_DATA | NO_INTL);
|
|
if (!items) {
|
|
done_uri(uri);
|
|
return FRAME_EVENT_OK;
|
|
}
|
|
|
|
foreach (option, *tree) {
|
|
char *text, *command, *data;
|
|
int block;
|
|
|
|
if (!strcmp(option->name, "_template_"))
|
|
continue;
|
|
|
|
text = stracpy(option->name);
|
|
if (!text) continue;
|
|
|
|
command = NULL;
|
|
block = 0;
|
|
|
|
foreach (sub, *option->value.tree) {
|
|
if (!strcmp(sub->name, "command"))
|
|
command = sub->value.string;
|
|
if (!strcmp(sub->name, "foreground"))
|
|
block = sub->value.number;
|
|
}
|
|
|
|
if (!command) {
|
|
mem_free(text);
|
|
continue;
|
|
}
|
|
|
|
data = format_command(command, uri);
|
|
if (!data) {
|
|
mem_free(text);
|
|
continue;
|
|
}
|
|
insert_in_string(&data, 0, " ", 1);
|
|
data[0] = block ? 'f' : 'b';
|
|
|
|
add_to_menu(&items, text, NULL, ACT_MAIN_NONE,
|
|
do_pass_uri_to_command, data, 0);
|
|
commands++;
|
|
}
|
|
|
|
done_uri(uri);
|
|
|
|
if (commands > 1) {
|
|
do_menu(ses->tab->term, items, ses, 1);
|
|
} else {
|
|
if (commands == 1)
|
|
do_pass_uri_to_command(ses->tab->term, items->data, ses);
|
|
else
|
|
mem_free(items->data);
|
|
mem_free(items->text);
|
|
mem_free(items);
|
|
}
|
|
|
|
return FRAME_EVENT_OK;
|
|
}
|
|
|
|
/* The caller provides the text of the menu item, so that it can
|
|
* choose an available accelerator key. */
|
|
void
|
|
add_uri_command_to_menu(struct menu_item **mi, pass_uri_type_T type,
|
|
char *text)
|
|
{
|
|
LIST_OF(struct option) *tree = get_opt_tree("document.uri_passing",
|
|
NULL);
|
|
struct option *option;
|
|
int commands = 0;
|
|
menu_item_flags_T flags = NO_FLAG;
|
|
action_id_T action_id;
|
|
|
|
switch (type) {
|
|
case PASS_URI_FRAME:
|
|
action_id = ACT_MAIN_FRAME_EXTERNAL_COMMAND;
|
|
break;
|
|
|
|
case PASS_URI_LINK:
|
|
action_id = ACT_MAIN_LINK_EXTERNAL_COMMAND;
|
|
break;
|
|
|
|
default:
|
|
case PASS_URI_TAB:
|
|
action_id = ACT_MAIN_TAB_EXTERNAL_COMMAND;
|
|
};
|
|
|
|
foreach (option, *tree) {
|
|
if (!strcmp(option->name, "_template_"))
|
|
continue;
|
|
|
|
commands++;
|
|
if (commands > 1) {
|
|
flags = SUBMENU;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (commands == 0) return;
|
|
|
|
add_to_menu(mi, text, NULL, action_id, NULL, NULL, flags);
|
|
}
|
|
|
|
|
|
/* The file completion menu always has two non selectable menu item at the
|
|
* start. First is the 'Directory:' or 'Files:' text and then a separator. */
|
|
#define FILE_COMPLETION_MENU_OFFSET 2
|
|
|
|
static struct menu_item empty_directory_menu[] = {
|
|
INIT_MENU_ITEM(N_("Empty directory"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
|
|
NULL_MENU_ITEM
|
|
};
|
|
|
|
/* Builds the file completion menu. If there is only one item it is selected
|
|
* else the menu is launched. */
|
|
static void
|
|
complete_file_menu(struct terminal *term, int no_elevator, void *data,
|
|
menu_func_T file_func, menu_func_T dir_func,
|
|
char *dirname, char *filename)
|
|
{
|
|
struct menu_item *menu = new_menu(FREE_LIST | NO_INTL);
|
|
struct directory_entry *entries, *entry;
|
|
int filenamelen = strlen(filename);
|
|
int direntries = 0, fileentries = 0;
|
|
|
|
if (!menu) return;
|
|
|
|
entries = get_directory_entries(dirname, 1);
|
|
if (!entries) {
|
|
mem_free(menu);
|
|
return;
|
|
}
|
|
|
|
for (entry = entries; entry->name; entry++) {
|
|
char *text;
|
|
int is_dir = (*entry->attrib == 'd');
|
|
int is_file = (*entry->attrib == '-');
|
|
|
|
mem_free(entry->attrib);
|
|
if ((!is_dir && !is_file) || !file_can_read(entry->name)) {
|
|
mem_free(entry->name);
|
|
continue;
|
|
}
|
|
|
|
text = get_filename_position(entry->name);
|
|
if (strncmp(filename, text, filenamelen)
|
|
|| (no_elevator && !strcmp("..", text))) {
|
|
mem_free(entry->name);
|
|
continue;
|
|
}
|
|
|
|
if (is_dir) {
|
|
if (!direntries) {
|
|
add_to_menu(&menu, _("Directories:", term), NULL,
|
|
ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
|
|
add_menu_separator(&menu);
|
|
}
|
|
|
|
add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
|
|
dir_func, entry->name, FREE_DATA | SUBMENU);
|
|
|
|
direntries++;
|
|
|
|
} else {
|
|
if (!fileentries) {
|
|
if (direntries) add_menu_separator(&menu);
|
|
add_to_menu(&menu, _("Files:", term), NULL,
|
|
ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
|
|
add_menu_separator(&menu);
|
|
}
|
|
|
|
add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
|
|
file_func, entry->name, FREE_DATA);
|
|
|
|
fileentries++;
|
|
|
|
}
|
|
}
|
|
|
|
mem_free(entries);
|
|
if (direntries == 0 && fileentries == 0) {
|
|
mem_free(menu);
|
|
return;
|
|
}
|
|
|
|
/* Only one entry */
|
|
if (direntries + fileentries == 1) {
|
|
char *text = (char *)menu[FILE_COMPLETION_MENU_OFFSET].data;
|
|
|
|
mem_free(menu);
|
|
|
|
if (fileentries) {
|
|
/* Complete what is already there */
|
|
file_func(term, text, data);
|
|
return;
|
|
}
|
|
|
|
/* For single directory entries open the lonely subdir if it is
|
|
* not the parent elevator. */
|
|
if (strcmp(&text[strlen(dirname)], "..")) {
|
|
dir_func(term, text, data);
|
|
} else {
|
|
do_menu(term, empty_directory_menu, NULL, 0); \
|
|
}
|
|
|
|
mem_free(text);
|
|
|
|
} else {
|
|
/* Start with the first directory or file entry selected */
|
|
do_menu(term, menu, data, 0);
|
|
}
|
|
}
|
|
|
|
/* Prepares the launching of the file completion menu by expanding the @path
|
|
* and splitting it in directory and file name part. */
|
|
void
|
|
auto_complete_file(struct terminal *term, int no_elevator, char *path,
|
|
menu_func_T file_func, menu_func_T dir_func, void *data)
|
|
{
|
|
struct uri *uri;
|
|
char *dirname;
|
|
char *filename;
|
|
|
|
assert(term && data && file_func && dir_func && data);
|
|
|
|
if (get_cmd_opt_bool("anonymous"))
|
|
return;
|
|
|
|
if (!*path) path = (char *)"./";
|
|
|
|
/* Use the URI translation to handle ./ and ../ and ~/ expansion */
|
|
uri = get_translated_uri(path, term->cwd);
|
|
if (!uri) return;
|
|
|
|
if (uri->protocol != PROTOCOL_FILE) {
|
|
path = NULL;
|
|
} else {
|
|
path = get_uri_string(uri, URI_PATH);
|
|
}
|
|
|
|
done_uri(uri);
|
|
if (!path) return;
|
|
|
|
filename = get_filename_position(path);
|
|
|
|
if (*filename && file_is_dir(path)) {
|
|
filename = path + strlen(path);
|
|
|
|
} else if (*filename && file_exists(path)) {
|
|
/* Complete any tilde expansion */
|
|
file_func(term, path, data);
|
|
return;
|
|
}
|
|
|
|
/* Split the path into @dirname and @filename */
|
|
dirname = path;
|
|
path = filename;
|
|
filename = stracpy(path);
|
|
*path = 0;
|
|
|
|
/* Make sure the dirname has an ending slash */
|
|
if (!dir_sep(path[-1])) {
|
|
char separator = *dirname;
|
|
int dirnamelen = path - dirname;
|
|
|
|
insert_in_string(&dirname, dirnamelen, &separator, 1);
|
|
}
|
|
|
|
complete_file_menu(term, no_elevator, data,
|
|
file_func, dir_func, dirname, filename);
|
|
|
|
mem_free(dirname);
|
|
mem_free(filename);
|
|
}
|