bug 771: Remember whether navigation was requested by user
[elinks/kon.git] / src / dialogs / menu.c
blobb8233aeb92577fef00df70d3d4298252eda20182
1 /* Menu system */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdlib.h>
8 #include <string.h>
10 #include "elinks.h"
12 #include "bfu/dialog.h"
13 #include "bfu/leds.h"
14 #include "bfu/menu.h"
15 #include "config/options.h"
16 #include "config/urlhist.h"
17 #include "document/document.h"
18 #include "document/view.h"
19 #include "dialogs/exmode.h"
20 #include "dialogs/info.h"
21 #include "dialogs/menu.h"
22 #include "dialogs/options.h"
23 #include "intl/gettext/libintl.h"
24 #include "main/event.h"
25 #include "main/main.h"
26 #include "main/select.h"
27 #include "mime/dialogs.h"
28 #include "mime/mime.h"
29 #include "network/connection.h"
30 #include "osdep/osdep.h"
31 #include "osdep/newwin.h"
32 #include "protocol/protocol.h"
33 #include "protocol/uri.h"
34 #include "session/download.h"
35 #include "session/history.h"
36 #include "session/location.h"
37 #include "session/session.h"
38 #include "session/task.h"
39 #include "terminal/tab.h"
40 #include "terminal/terminal.h"
41 #include "util/conv.h"
42 #include "util/file.h"
43 #include "util/memlist.h"
44 #include "util/memory.h"
45 #include "util/string.h"
46 #include "viewer/action.h"
47 #include "viewer/text/link.h"
48 #include "viewer/text/view.h"
51 /* Helper for url items in help menu. */
52 static void
53 menu_url_shortcut(struct terminal *term, void *url_, void *ses_)
55 unsigned char *url = url_;
56 struct session *ses = ses_;
57 struct uri *uri = get_uri(url, 0);
59 if (!uri) return;
60 goto_uri(ses, uri);
61 done_uri(uri);
64 static void
65 save_url(struct session *ses, unsigned char *url)
67 struct document_view *doc_view;
68 struct uri *uri;
70 assert(ses && ses->tab && ses->tab->term && url);
71 if_assert_failed return;
73 if (!*url) return;
75 uri = get_translated_uri(url, ses->tab->term->cwd);
76 if (!uri) {
77 print_error_dialog(ses, connection_state(S_BAD_URL),
78 uri, PRI_CANCEL);
79 return;
82 if (ses->download_uri) done_uri(ses->download_uri);
83 ses->download_uri = uri;
85 doc_view = current_frame(ses);
86 assert(doc_view && doc_view->document && doc_view->document->uri);
87 if_assert_failed return;
89 set_session_referrer(ses, doc_view->document->uri);
90 query_file(ses, ses->download_uri, ses, start_download, NULL, 1);
93 void
94 save_url_as(struct session *ses)
96 input_dialog(ses->tab->term, NULL,
97 N_("Save URL"), N_("Enter URL"),
98 ses, &goto_url_history,
99 MAX_STR_LEN, "", 0, 0, NULL,
100 (void (*)(void *, unsigned char *)) save_url,
101 NULL);
104 static void
105 really_exit_prog(void *ses_)
107 struct session *ses = ses_;
109 register_bottom_half(destroy_terminal, ses->tab->term);
112 static inline void
113 dont_exit_prog(void *ses_)
115 struct session *ses = ses_;
117 ses->exit_query = 0;
120 void
121 query_exit(struct session *ses)
123 /* [gettext_accelerator_context(query_exit)] */
124 ses->exit_query = 1;
125 msg_box(ses->tab->term, NULL, 0,
126 N_("Exit ELinks"), ALIGN_CENTER,
127 (ses->tab->term->next == ses->tab->term->prev && are_there_downloads())
128 ? N_("Do you really want to exit ELinks "
129 "(and terminate all downloads)?")
130 : N_("Do you really want to exit ELinks?"),
131 ses, 2,
132 MSG_BOX_BUTTON(N_("~Yes"), really_exit_prog, B_ENTER),
133 MSG_BOX_BUTTON(N_("~No"), dont_exit_prog, B_ESC));
136 void
137 exit_prog(struct session *ses, int query)
139 assert(ses);
141 /* An exit query is in progress. */
142 if (ses->exit_query)
143 return;
145 /* Force a query if the last terminal is exiting with downloads still in
146 * progress. */
147 if (query || (list_is_singleton(terminals) && are_there_downloads())) {
148 query_exit(ses);
149 return;
152 really_exit_prog(ses);
156 static void
157 go_historywards(struct terminal *term, void *target_, void *ses_)
159 struct location *target = target_;
160 struct session *ses = ses_;
162 go_history(ses, target);
165 static struct menu_item no_hist_menu[] = {
166 INIT_MENU_ITEM(N_("No history"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
167 NULL_MENU_ITEM
170 /* unhist == 0 => history
171 * unhist == 1 => unhistory */
172 static void
173 history_menu_common(struct terminal *term, struct session *ses, int unhist)
175 struct menu_item *mi = NULL;
177 if (have_location(ses)) {
178 struct location *loc;
180 for (loc = unhist ? cur_loc(ses)->next : cur_loc(ses)->prev;
181 loc != (struct location *) &ses->history.history;
182 loc = unhist ? loc->next : loc->prev) {
183 unsigned char *url;
185 if (!mi) {
186 mi = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
187 if (!mi) return;
190 url = get_uri_string(loc->vs.uri, URI_PUBLIC);
191 if (url) {
192 add_to_menu(&mi, url, NULL, ACT_MAIN_NONE,
193 go_historywards,
194 (void *) loc, 0);
199 if (!mi)
200 do_menu(term, no_hist_menu, ses, 0);
201 else
202 do_menu(term, mi, ses, 0);
205 static void
206 history_menu(struct terminal *term, void *xxx, void *ses_)
208 struct session *ses = ses_;
210 history_menu_common(term, ses, 0);
213 static void
214 unhistory_menu(struct terminal *term, void *xxx, void *ses_)
216 struct session *ses = ses_;
218 history_menu_common(term, ses, 1);
221 void
222 tab_menu(struct session *ses, int x, int y, int place_above_cursor)
224 /* [gettext_accelerator_context(tab_menu)] */
225 struct menu_item *menu;
226 int tabs_count;
227 #ifdef CONFIG_BOOKMARKS
228 int anonymous = get_cmd_opt_bool("anonymous");
229 #endif
231 assert(ses && ses->tab);
232 if_assert_failed return;
234 tabs_count = number_of_tabs(ses->tab->term);
235 menu = new_menu(FREE_LIST);
236 if (!menu) return;
238 add_menu_action(&menu, N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK);
239 add_menu_action(&menu, N_("Go for~ward"), ACT_MAIN_HISTORY_MOVE_FORWARD);
241 if (have_location(ses)) {
242 add_menu_separator(&menu);
244 #ifdef CONFIG_BOOKMARKS
245 if (!anonymous) {
246 add_menu_action(&menu, N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK);
248 #endif
250 add_menu_action(&menu, N_("Toggle ~HTML/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN);
251 add_menu_action(&menu, N_("~Reload"), ACT_MAIN_RELOAD);
253 if (ses->doc_view && document_has_frames(ses->doc_view->document)) {
254 add_menu_action(&menu, N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE);
255 add_uri_command_to_menu(&menu, PASS_URI_FRAME,
256 N_("~Pass frame URI to external command"));
260 /* Keep tab related operations below this separator */
261 add_menu_separator(&menu);
263 if (tabs_count > 1) {
264 add_menu_action(&menu, N_("Nex~t tab"), ACT_MAIN_TAB_NEXT);
265 add_menu_action(&menu, N_("Pre~v tab"), ACT_MAIN_TAB_PREV);
268 add_menu_action(&menu, N_("~Close tab"), ACT_MAIN_TAB_CLOSE);
270 if (tabs_count > 1) {
271 add_menu_action(&menu, N_("C~lose all tabs but the current"),
272 ACT_MAIN_TAB_CLOSE_ALL_BUT_CURRENT);
273 #ifdef CONFIG_BOOKMARKS
274 if (!anonymous) {
275 add_menu_action(&menu, N_("B~ookmark all tabs"),
276 ACT_MAIN_ADD_BOOKMARK_TABS);
278 #endif
281 if (have_location(ses)) {
282 add_uri_command_to_menu(&menu, PASS_URI_TAB,
283 N_("Pass tab URI to e~xternal command"));
286 /* Adjust the menu position taking the menu frame into account */
287 if (place_above_cursor) {
288 int i = 0;
290 while (menu[i].text) i++;
292 y = int_max(y - i - 1, 0);
295 set_window_ptr(ses->tab, x, y);
297 do_menu(ses->tab->term, menu, ses, 1);
300 static void
301 do_submenu(struct terminal *term, void *menu_, void *ses_)
303 struct menu_item *menu = menu_;
305 do_menu(term, menu, ses_, 1);
309 static struct menu_item file_menu11[] = {
310 /* [gettext_accelerator_context(.file_menu)] */
311 INIT_MENU_ACTION(N_("Open new ~tab"), ACT_MAIN_OPEN_NEW_TAB),
312 INIT_MENU_ACTION(N_("Open new tab in backgroun~d"), ACT_MAIN_OPEN_NEW_TAB_IN_BACKGROUND),
313 INIT_MENU_ACTION(N_("~Go to URL"), ACT_MAIN_GOTO_URL),
314 INIT_MENU_ACTION(N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK),
315 INIT_MENU_ACTION(N_("Go ~forward"), ACT_MAIN_HISTORY_MOVE_FORWARD),
316 INIT_MENU_ITEM(N_("~History"), NULL, ACT_MAIN_NONE, history_menu, NULL, SUBMENU),
317 INIT_MENU_ITEM(N_("~Unhistory"), NULL, ACT_MAIN_NONE, unhistory_menu, NULL, SUBMENU),
320 static struct menu_item file_menu21[] = {
321 /* [gettext_accelerator_context(.file_menu)] */
322 BAR_MENU_ITEM,
323 INIT_MENU_ACTION(N_("~Save as"), ACT_MAIN_SAVE_AS),
324 INIT_MENU_ACTION(N_("Save UR~L as"), ACT_MAIN_SAVE_URL_AS),
325 INIT_MENU_ACTION(N_("Sa~ve formatted document"), ACT_MAIN_SAVE_FORMATTED),
326 #ifdef CONFIG_BOOKMARKS
327 INIT_MENU_ACTION(N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK),
328 #endif
331 static struct menu_item file_menu22[] = {
332 /* [gettext_accelerator_context(.file_menu)] */
333 BAR_MENU_ITEM,
334 INIT_MENU_ACTION(N_("~Kill background connections"), ACT_MAIN_KILL_BACKGROUNDED_CONNECTIONS),
335 INIT_MENU_ACTION(N_("Flush all ~caches"), ACT_MAIN_CACHE_MINIMIZE),
336 INIT_MENU_ACTION(N_("Resource ~info"), ACT_MAIN_RESOURCE_INFO),
337 BAR_MENU_ITEM,
340 static struct menu_item file_menu3[] = {
341 /* [gettext_accelerator_context(.file_menu)] */
342 BAR_MENU_ITEM,
343 INIT_MENU_ACTION(N_("E~xit"), ACT_MAIN_QUIT),
344 NULL_MENU_ITEM,
347 static void
348 do_file_menu(struct terminal *term, void *xxx, void *ses_)
350 /* [gettext_accelerator_context(.file_menu)] */
351 struct menu_item *file_menu, *e, *f;
352 int anonymous = get_cmd_opt_bool("anonymous");
353 int x, o;
355 file_menu = mem_alloc(sizeof(file_menu11) + sizeof(file_menu21)
356 + sizeof(file_menu22) + sizeof(file_menu3)
357 + 3 * sizeof(struct menu_item));
358 if (!file_menu) return;
360 e = file_menu;
362 if (!anonymous
363 && !get_cmd_opt_bool("no-connect")
364 && !get_cmd_opt_bool("no-home"))
365 o = can_open_in_new(term);
366 else
367 o = 0;
369 if (o) {
370 SET_MENU_ITEM(e, N_("Open ~new window"), NULL, ACT_MAIN_OPEN_NEW_WINDOW,
371 open_in_new_window, send_open_new_window,
372 (o - 1) ? SUBMENU : 0, HKS_SHOW, 0);
373 e++;
376 memcpy(e, file_menu11, sizeof(file_menu11));
377 e += sizeof_array(file_menu11);
379 if (!anonymous) {
380 memcpy(e, file_menu21, sizeof(file_menu21));
381 e += sizeof_array(file_menu21);
384 memcpy(e, file_menu22, sizeof(file_menu22));
385 e += sizeof_array(file_menu22);
387 x = 1;
388 if (!anonymous && can_open_os_shell(term->environment)) {
389 SET_MENU_ITEM(e, N_("~OS shell"), NULL, ACT_MAIN_OPEN_OS_SHELL,
390 NULL, NULL, 0, HKS_SHOW, 0);
391 e++;
392 x = 0;
395 if (can_resize_window(term->environment)) {
396 SET_MENU_ITEM(e, N_("Resize t~erminal"), NULL, ACT_MAIN_TERMINAL_RESIZE,
397 NULL, NULL, 0, HKS_SHOW, 0);
398 e++;
399 x = 0;
402 memcpy(e, file_menu3 + x, sizeof(file_menu3) - x * sizeof(struct menu_item));
403 e += sizeof_array(file_menu3);
405 for (f = file_menu; f < e; f++)
406 f->flags |= FREE_LIST;
408 do_menu(term, file_menu, ses_, 1);
411 static struct menu_item view_menu[] = {
412 /* [gettext_accelerator_context(.view_menu)] */
413 INIT_MENU_ACTION(N_("~Search"), ACT_MAIN_SEARCH),
414 INIT_MENU_ACTION(N_("Search ~backward"), ACT_MAIN_SEARCH_BACK),
415 INIT_MENU_ACTION(N_("Find ~next"), ACT_MAIN_FIND_NEXT),
416 INIT_MENU_ACTION(N_("Find ~previous"), ACT_MAIN_FIND_NEXT_BACK),
417 INIT_MENU_ACTION(N_("T~ypeahead search"), ACT_MAIN_SEARCH_TYPEAHEAD),
418 BAR_MENU_ITEM,
419 INIT_MENU_ACTION(N_("Toggle ~HTML/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN),
420 INIT_MENU_ACTION(N_("Toggle i~mages"), ACT_MAIN_TOGGLE_DISPLAY_IMAGES),
421 INIT_MENU_ACTION(N_("Toggle ~link numbering"), ACT_MAIN_TOGGLE_NUMBERED_LINKS),
422 INIT_MENU_ACTION(N_("Toggle ~document colors"), ACT_MAIN_TOGGLE_DOCUMENT_COLORS),
423 INIT_MENU_ACTION(N_("~Wrap text on/off"), ACT_MAIN_TOGGLE_WRAP_TEXT),
424 BAR_MENU_ITEM,
425 INIT_MENU_ACTION(N_("Document ~info"), ACT_MAIN_DOCUMENT_INFO),
426 INIT_MENU_ACTION(N_("H~eader info"), ACT_MAIN_HEADER_INFO),
427 INIT_MENU_ACTION(N_("Rel~oad document"), ACT_MAIN_RELOAD),
428 INIT_MENU_ACTION(N_("~Rerender document"), ACT_MAIN_RERENDER),
429 INIT_MENU_ACTION(N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE),
430 BAR_MENU_ITEM,
431 INIT_MENU_ACTION(N_("Nex~t tab"), ACT_MAIN_TAB_NEXT),
432 INIT_MENU_ACTION(N_("Pre~v tab"), ACT_MAIN_TAB_PREV),
433 INIT_MENU_ACTION(N_("~Close tab"), ACT_MAIN_TAB_CLOSE),
434 NULL_MENU_ITEM
438 static struct menu_item help_menu[] = {
439 /* [gettext_accelerator_context(.help_menu)] */
440 INIT_MENU_ITEM(N_("~ELinks homepage"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_WEBSITE_URL, 0),
441 INIT_MENU_ITEM(N_("~Documentation"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_DOC_URL, 0),
442 INIT_MENU_ITEM(N_("~Keys"), NULL, ACT_MAIN_NONE, menu_keys, NULL, 0),
443 #ifdef CONFIG_LEDS
444 INIT_MENU_ITEM(N_("LED ~indicators"), NULL, ACT_MAIN_NONE, menu_leds_info, NULL, 0),
445 #endif
446 BAR_MENU_ITEM,
447 INIT_MENU_ITEM(N_("~Bugs information"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_BUGS_URL, 0),
448 #ifdef CONFIG_DEBUG
449 INIT_MENU_ITEM(N_("ELinks ~GITWeb"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_GITWEB_URL, 0),
450 #endif
451 BAR_MENU_ITEM,
452 INIT_MENU_ITEM(N_("~Copying"), NULL, ACT_MAIN_NONE, menu_copying, NULL, 0),
453 INIT_MENU_ITEM(N_("Autho~rs"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_AUTHORS_URL, 0),
454 INIT_MENU_ITEM(N_("~About"), NULL, ACT_MAIN_NONE, menu_about, NULL, 0),
455 NULL_MENU_ITEM
459 static struct menu_item ext_menu[] = {
460 /* [gettext_accelerator_context(.ext_menu)] */
461 INIT_MENU_ITEM(N_("~Add"), NULL, ACT_MAIN_NONE, menu_add_ext, NULL, 0),
462 INIT_MENU_ITEM(N_("~Modify"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_add_ext, SUBMENU),
463 INIT_MENU_ITEM(N_("~Delete"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_del_ext, SUBMENU),
464 NULL_MENU_ITEM
467 static struct menu_item setup_menu[] = {
468 /* [gettext_accelerator_context(.setup_menu)] */
469 #ifdef CONFIG_NLS
470 INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
471 #endif
472 INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
473 INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
474 INIT_MENU_ITEM(N_("File ~extensions"), NULL, ACT_MAIN_NONE, do_submenu, ext_menu, SUBMENU),
475 BAR_MENU_ITEM,
476 INIT_MENU_ACTION(N_("~Options manager"), ACT_MAIN_OPTIONS_MANAGER),
477 INIT_MENU_ACTION(N_("~Keybinding manager"), ACT_MAIN_KEYBINDING_MANAGER),
478 INIT_MENU_ACTION(N_("~Save options"), ACT_MAIN_SAVE_OPTIONS),
479 NULL_MENU_ITEM
482 static struct menu_item setup_menu_anon[] = {
483 /* [gettext_accelerator_context(.setup_menu)] */
484 INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
485 INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
486 INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
487 NULL_MENU_ITEM
490 static struct menu_item tools_menu[] = {
491 /* [gettext_accelerator_context(.tools_menu)] */
492 #ifdef CONFIG_GLOBHIST
493 INIT_MENU_ACTION(N_("Global ~history"), ACT_MAIN_HISTORY_MANAGER),
494 #endif
495 #ifdef CONFIG_BOOKMARKS
496 INIT_MENU_ACTION(N_("~Bookmarks"), ACT_MAIN_BOOKMARK_MANAGER),
497 #endif
498 INIT_MENU_ACTION(N_("~Cache"), ACT_MAIN_CACHE_MANAGER),
499 INIT_MENU_ACTION(N_("~Downloads"), ACT_MAIN_DOWNLOAD_MANAGER),
500 #ifdef CONFIG_COOKIES
501 INIT_MENU_ACTION(N_("Coo~kies"), ACT_MAIN_COOKIE_MANAGER),
502 #endif
503 #ifdef CONFIG_FORMHIST
504 INIT_MENU_ACTION(N_("~Form history"), ACT_MAIN_FORMHIST_MANAGER),
505 #endif
506 INIT_MENU_ACTION(N_("~Authentication"), ACT_MAIN_AUTH_MANAGER),
507 NULL_MENU_ITEM
510 static void
511 do_setup_menu(struct terminal *term, void *xxx, void *ses_)
513 struct session *ses = ses_;
515 if (!get_cmd_opt_bool("anonymous"))
516 do_menu(term, setup_menu, ses, 1);
517 else
518 do_menu(term, setup_menu_anon, ses, 1);
521 static struct menu_item main_menu[] = {
522 /* [gettext_accelerator_context(.main_menu)] */
523 INIT_MENU_ITEM(N_("~File"), NULL, ACT_MAIN_NONE, do_file_menu, NULL, FREE_LIST | SUBMENU),
524 INIT_MENU_ITEM(N_("~View"), NULL, ACT_MAIN_NONE, do_submenu, view_menu, FREE_LIST | SUBMENU),
525 INIT_MENU_ITEM(N_("~Link"), NULL, ACT_MAIN_NONE, link_menu, NULL, FREE_LIST | SUBMENU),
526 INIT_MENU_ITEM(N_("~Tools"), NULL, ACT_MAIN_NONE, do_submenu, tools_menu, FREE_LIST | SUBMENU),
527 INIT_MENU_ITEM(N_("~Setup"), NULL, ACT_MAIN_NONE, do_setup_menu, NULL, FREE_LIST | SUBMENU),
528 INIT_MENU_ITEM(N_("~Help"), NULL, ACT_MAIN_NONE, do_submenu, help_menu, FREE_LIST | SUBMENU),
529 NULL_MENU_ITEM
532 void
533 activate_bfu_technology(struct session *ses, int item)
535 do_mainmenu(ses->tab->term, main_menu, ses, item);
539 void
540 dialog_goto_url(struct session *ses, unsigned char *url)
542 input_dialog(ses->tab->term, NULL,
543 N_("Go to URL"), N_("Enter URL"),
544 ses, &goto_url_history,
545 MAX_STR_LEN, url, 0, 0, NULL,
546 (void (*)(void *, unsigned char *)) goto_url_with_hook,
547 NULL);
551 static INIT_INPUT_HISTORY(file_history);
553 void
554 query_file(struct session *ses, struct uri *uri, void *data,
555 void (*std)(void *, unsigned char *),
556 void (*cancel)(void *), int interactive)
558 struct string def;
560 assert(ses && uri);
561 if_assert_failed return;
563 /* FIXME: This ``sanity'' checking is mostly for the download code
564 * using this function. They pass ses->download_uri and we have to make
565 * sure that the connection code can download the URI. The reason we do
566 * it before is that then users won't waste time typing a filename and
567 * then discover that the URI can not be downloaded. However it might
568 * be better to introduce a set_session_download_uri() which will do
569 * the checking? --jonas */
571 if (uri->protocol == PROTOCOL_UNKNOWN) {
572 print_error_dialog(ses, connection_state(S_UNKNOWN_PROTOCOL),
573 uri, PRI_CANCEL);
574 return;
577 if (get_protocol_external_handler(ses->tab->term, uri)) {
578 print_error_dialog(ses, connection_state(S_EXTERNAL_PROTOCOL),
579 uri, PRI_CANCEL);
580 return;
583 if (!init_string(&def)) return;
585 add_to_string(&def, get_opt_str("document.download.directory"));
586 if (def.length && !dir_sep(def.source[def.length - 1]))
587 add_char_to_string(&def, '/');
589 add_mime_filename_to_string(&def, uri);
591 /* Remove the %-ugliness for display */
592 #ifdef CONFIG_UTF8
593 if (ses->tab->term->utf8_cp)
594 decode_uri_string(&def);
595 else
596 #endif /* CONFIG_UTF8 */
597 decode_uri_string_for_display(&def);
599 if (interactive) {
600 input_dialog(ses->tab->term, NULL,
601 N_("Download"), N_("Save to file"),
602 data, &file_history,
603 MAX_STR_LEN, def.source, 0, 0, check_nonempty,
604 (void (*)(void *, unsigned char *)) std,
605 (void (*)(void *)) cancel);
606 } else {
607 std(data, def.source);
610 done_string(&def);
613 void
614 free_history_lists(void)
616 free_list(file_history.entries);
617 #ifdef CONFIG_SCRIPTING
618 trigger_event_name("free-history");
619 #endif
623 static void
624 add_cmdline_bool_option(struct string *string, unsigned char *name)
626 if (!get_cmd_opt_bool(name)) return;
627 add_to_string(string, " -");
628 add_to_string(string, name);
631 void
632 open_uri_in_new_window(struct session *ses, struct uri *uri, struct uri *referrer,
633 enum term_env_type env, enum cache_mode cache_mode,
634 enum task_type task)
636 int ring = get_cmd_opt_int("session-ring");
637 struct string parameters;
638 int id;
640 assert(env && ses);
641 if_assert_failed return;
643 id = add_session_info(ses, uri, referrer, cache_mode, task);
644 if (id < 1) return;
646 if (!init_string(&parameters)) return;
648 add_format_to_string(&parameters, "-base-session %d", id);
649 if (ring) add_format_to_string(&parameters, " -session-ring %d", ring);
651 /* No URI means open new (clean) window possibly without connecting to
652 * the current master so add command line options to properly clone the
653 * current master */
654 if (!uri) {
655 /* Adding -touch-files will only lead to problems */
656 add_cmdline_bool_option(&parameters, "localhost");
657 add_cmdline_bool_option(&parameters, "no-home");
658 add_cmdline_bool_option(&parameters, "no-connect");
661 open_new_window(ses->tab->term, program.path, env, parameters.source);
662 done_string(&parameters);
665 /* Open a link in a new xterm. */
666 void
667 send_open_in_new_window(struct terminal *term, const struct open_in_new *open,
668 struct session *ses)
670 struct document_view *doc_view;
671 struct link *link;
672 struct uri *uri;
674 assert(term && open && ses);
675 if_assert_failed return;
676 doc_view = current_frame(ses);
677 assert(doc_view && doc_view->vs && doc_view->document);
678 if_assert_failed return;
680 link = get_current_link(doc_view);
681 if (!link) return;
683 uri = get_link_uri(ses, doc_view, link);
684 if (!uri) return;
686 open_uri_in_new_window(ses, uri, NULL, open->env,
687 CACHE_MODE_NORMAL, TASK_NONE);
688 done_uri(uri);
691 void
692 send_open_new_window(struct terminal *term, const struct open_in_new *open,
693 struct session *ses)
695 open_uri_in_new_window(ses, NULL, NULL, open->env,
696 CACHE_MODE_NORMAL, TASK_NONE);
700 void
701 open_in_new_window(struct terminal *term, void *func_, void *ses_)
703 menu_func_T func = func_;
704 struct session *ses = ses_;
705 struct menu_item *mi;
706 int posibilities;
708 assert(term && ses && func);
709 if_assert_failed return;
711 switch (can_open_in_new(term)) {
712 case 0:
713 return;
715 case 1:
716 mi = NULL;
717 break;
719 default:
720 mi = new_menu(FREE_LIST);
721 if (!mi) return;
724 foreach_open_in_new (posibilities, term->environment) {
725 const struct open_in_new *oi = &open_in_new[posibilities];
727 if (mi == NULL) {
728 func(term, (void *) oi, ses);
729 return;
731 add_to_menu(&mi, oi->text, NULL, ACT_MAIN_NONE, func, (void *) oi, 0);
734 do_menu(term, mi, ses, 1);
738 void
739 add_new_win_to_menu(struct menu_item **mi, unsigned char *text,
740 struct terminal *term)
742 int c = can_open_in_new(term);
744 if (!c) return;
746 /* The URI is saved as session info in the master and not sent to the
747 * instance in the new window so with -no-connect or -no-home enabled
748 * it is not possible to open links URIs. For -anonymous one window
749 * should be enough. */
750 if (get_cmd_opt_bool("no-connect")
751 || get_cmd_opt_bool("no-home")
752 || get_cmd_opt_bool("anonymous"))
753 return;
755 add_to_menu(mi, text, NULL, ACT_MAIN_OPEN_LINK_IN_NEW_WINDOW,
756 open_in_new_window,
757 send_open_in_new_window, c - 1 ? SUBMENU : 0);
761 static void
762 do_pass_uri_to_command(struct terminal *term, void *command_, void *xxx)
764 unsigned char *command = command_;
766 exec_on_terminal(term, command, "", TERM_EXEC_BG);
767 mem_free(command);
770 /* TODO:
771 * - Support for passing MIME type
772 * - Merge this function with rewrite_uri(), subst_cmd(), subst_file()
773 * and subst_url(). */
774 static unsigned char *
775 format_command(unsigned char *format, struct uri *uri)
777 struct string string;
779 if (!init_string(&string)) return NULL;
781 while (*format) {
782 int pos = 0;
784 while (format[pos] && format[pos] != '%') pos++;
786 add_bytes_to_string(&string, format, pos);
787 format += pos;
789 if (*format != '%') continue;
791 format++;
792 switch (*format) {
793 case 'c':
795 unsigned char *str = struri(uri);
796 int length = get_real_uri_length(uri);
798 add_shell_quoted_to_string(&string,
799 str, length);
800 break;
802 case '%':
803 add_char_to_string(&string, '%');
804 break;
805 default:
806 add_bytes_to_string(&string, format - 1, 2);
807 break;
809 if (*format) format++;
812 return string.source;
815 enum frame_event_status
816 pass_uri_to_command(struct session *ses, struct document_view *doc_view,
817 int which_type)
819 LIST_OF(struct option) *tree = get_opt_tree("document.uri_passing");
820 enum pass_uri_type type = which_type;
821 struct menu_item *items;
822 struct option *option;
823 struct uri *uri;
824 int commands = 0;
826 switch (type) {
827 case PASS_URI_FRAME:
828 uri = get_uri_reference(doc_view->document->uri);
829 break;
831 case PASS_URI_LINK:
833 struct link *link = get_current_link(doc_view);
835 if (!link) return FRAME_EVENT_OK;
837 uri = get_link_uri(ses, doc_view, link);
838 if (!uri) return FRAME_EVENT_OK;
839 break;
841 default:
842 case PASS_URI_TAB:
843 uri = get_uri_reference(ses->doc_view->document->uri);
846 items = new_menu(FREE_LIST | FREE_TEXT | FREE_DATA | NO_INTL);
847 if (!items) {
848 done_uri(uri);
849 return FRAME_EVENT_OK;
852 foreach (option, *tree) {
853 unsigned char *text, *data;
855 if (!strcmp(option->name, "_template_"))
856 continue;
858 text = stracpy(option->name);
859 if (!text) continue;
861 data = format_command(option->value.string, uri);
862 if (!data) {
863 mem_free(text);
864 continue;
867 add_to_menu(&items, text, NULL, ACT_MAIN_NONE,
868 do_pass_uri_to_command, data, 0);
869 commands++;
872 done_uri(uri);
874 if (commands > 1) {
875 do_menu(ses->tab->term, items, ses, 1);
876 } else {
877 if (commands == 1)
878 do_pass_uri_to_command(ses->tab->term, items->data, ses);
879 else
880 mem_free(items->data);
881 mem_free(items->text);
882 mem_free(items);
885 return FRAME_EVENT_OK;
888 /* The caller provides the text of the menu item, so that it can
889 * choose an available accelerator key. */
890 void
891 add_uri_command_to_menu(struct menu_item **mi, enum pass_uri_type type,
892 unsigned char *text)
894 LIST_OF(struct option) *tree = get_opt_tree("document.uri_passing");
895 struct option *option;
896 int commands = 0;
897 enum menu_item_flags flags = NO_FLAG;
898 action_id_T action_id;
900 switch (type) {
901 case PASS_URI_FRAME:
902 action_id = ACT_MAIN_FRAME_EXTERNAL_COMMAND;
903 break;
905 case PASS_URI_LINK:
906 action_id = ACT_MAIN_LINK_EXTERNAL_COMMAND;
907 break;
909 default:
910 case PASS_URI_TAB:
911 action_id = ACT_MAIN_TAB_EXTERNAL_COMMAND;
914 foreach (option, *tree) {
915 if (!strcmp(option->name, "_template_"))
916 continue;
918 commands++;
919 if (commands > 1) {
920 flags = SUBMENU;
921 break;
925 if (commands == 0) return;
927 add_to_menu(mi, text, NULL, action_id, NULL, NULL, flags);
931 /* The file completion menu always has two non selectable menu item at the
932 * start. First is the 'Directory:' or 'Files:' text and then a separator. */
933 #define FILE_COMPLETION_MENU_OFFSET 2
935 static struct menu_item empty_directory_menu[] = {
936 INIT_MENU_ITEM(N_("Empty directory"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
937 NULL_MENU_ITEM
940 /* Builds the file completion menu. If there is only one item it is selected
941 * else the menu is launched. */
942 static void
943 complete_file_menu(struct terminal *term, int no_elevator, void *data,
944 menu_func_T file_func, menu_func_T dir_func,
945 unsigned char *dirname, unsigned char *filename)
947 struct menu_item *menu = new_menu(FREE_LIST | NO_INTL);
948 struct directory_entry *entries, *entry;
949 int filenamelen = strlen(filename);
950 int direntries = 0, fileentries = 0;
952 if (!menu) return;
954 entries = get_directory_entries(dirname, 1);
955 if (!entries) {
956 mem_free(menu);
957 return;
960 for (entry = entries; entry->name; entry++) {
961 unsigned char *text;
962 int is_dir = (*entry->attrib == 'd');
963 int is_file = (*entry->attrib == '-');
965 mem_free(entry->attrib);
966 if ((!is_dir && !is_file) || !file_can_read(entry->name)) {
967 mem_free(entry->name);
968 continue;
971 text = get_filename_position(entry->name);
972 if (strncmp(filename, text, filenamelen)
973 || (no_elevator && !strcmp("..", text))) {
974 mem_free(entry->name);
975 continue;
978 if (is_dir) {
979 if (!direntries) {
980 add_to_menu(&menu, _("Directories:", term), NULL,
981 ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
982 add_menu_separator(&menu);
985 add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
986 dir_func, entry->name, FREE_DATA | SUBMENU);
988 direntries++;
990 } else {
991 if (!fileentries) {
992 if (direntries) add_menu_separator(&menu);
993 add_to_menu(&menu, _("Files:", term), NULL,
994 ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
995 add_menu_separator(&menu);
998 add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
999 file_func, entry->name, FREE_DATA);
1001 fileentries++;
1006 mem_free(entries);
1007 if (direntries == 0 && fileentries == 0) {
1008 mem_free(menu);
1009 return;
1012 /* Only one entry */
1013 if (direntries + fileentries == 1) {
1014 unsigned char *text = menu[FILE_COMPLETION_MENU_OFFSET].data;
1016 mem_free(menu);
1018 if (fileentries) {
1019 /* Complete what is already there */
1020 file_func(term, text, data);
1021 return;
1024 /* For single directory entries open the lonely subdir if it is
1025 * not the parent elevator. */
1026 if (strcmp(&text[strlen(dirname)], "..")) {
1027 dir_func(term, text, data);
1028 } else {
1029 do_menu(term, empty_directory_menu, NULL, 0); \
1032 mem_free(text);
1034 } else {
1035 /* Start with the first directory or file entry selected */
1036 do_menu(term, menu, data, 0);
1040 /* Prepares the launching of the file completion menu by expanding the @path
1041 * and splitting it in directory and file name part. */
1042 void
1043 auto_complete_file(struct terminal *term, int no_elevator, unsigned char *path,
1044 menu_func_T file_func, menu_func_T dir_func, void *data)
1046 struct uri *uri;
1047 unsigned char *dirname;
1048 unsigned char *filename;
1050 assert(term && data && file_func && dir_func && data);
1052 if (get_cmd_opt_bool("anonymous"))
1053 return;
1055 if (!*path) path = "./";
1057 /* Use the URI translation to handle ./ and ../ and ~/ expansion */
1058 uri = get_translated_uri(path, term->cwd);
1059 if (!uri) return;
1061 if (uri->protocol != PROTOCOL_FILE) {
1062 path = NULL;
1063 } else {
1064 path = get_uri_string(uri, URI_PATH);
1067 done_uri(uri);
1068 if (!path) return;
1070 filename = get_filename_position(path);
1072 if (*filename && file_is_dir(path)) {
1073 filename = path + strlen(path);
1075 } else if (*filename && file_exists(path)) {
1076 /* Complete any tilde expansion */
1077 file_func(term, path, data);
1078 return;
1081 /* Split the path into @dirname and @filename */
1082 dirname = path;
1083 path = filename;
1084 filename = stracpy(path);
1085 *path = 0;
1087 /* Make sure the dirname has an ending slash */
1088 if (!dir_sep(path[-1])) {
1089 unsigned char separator = *dirname;
1090 int dirnamelen = path - dirname;
1092 insert_in_string(&dirname, dirnamelen, &separator, 1);
1095 complete_file_menu(term, no_elevator, data,
1096 file_func, dir_func, dirname, filename);
1098 mem_free(dirname);
1099 mem_free(filename);