Added test for uploading big files.
[elinks.git] / src / dialogs / menu.c
blob63ed81aa00990dc25b533c419884f81571f902bf
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, S_BAD_URL, uri, PRI_CANCEL);
78 return;
81 if (ses->download_uri) done_uri(ses->download_uri);
82 ses->download_uri = uri;
84 doc_view = current_frame(ses);
85 assert(doc_view && doc_view->document && doc_view->document->uri);
86 if_assert_failed return;
88 set_session_referrer(ses, doc_view->document->uri);
89 query_file(ses, ses->download_uri, ses, start_download, NULL, 1);
92 void
93 save_url_as(struct session *ses)
95 input_dialog(ses->tab->term, NULL,
96 N_("Save URL"), N_("Enter URL"),
97 ses, &goto_url_history,
98 MAX_STR_LEN, "", 0, 0, NULL,
99 (void (*)(void *, unsigned char *)) save_url,
100 NULL);
103 static void
104 really_exit_prog(void *ses_)
106 struct session *ses = ses_;
108 register_bottom_half(destroy_terminal, ses->tab->term);
111 static inline void
112 dont_exit_prog(void *ses_)
114 struct session *ses = ses_;
116 ses->exit_query = 0;
119 void
120 query_exit(struct session *ses)
122 /* [gettext_accelerator_context(query_exit)] */
123 ses->exit_query = 1;
124 msg_box(ses->tab->term, NULL, 0,
125 N_("Exit ELinks"), ALIGN_CENTER,
126 (ses->tab->term->next == ses->tab->term->prev && are_there_downloads())
127 ? N_("Do you really want to exit ELinks "
128 "(and terminate all downloads)?")
129 : N_("Do you really want to exit ELinks?"),
130 ses, 2,
131 MSG_BOX_BUTTON(N_("~Yes"), really_exit_prog, B_ENTER),
132 MSG_BOX_BUTTON(N_("~No"), dont_exit_prog, B_ESC));
135 void
136 exit_prog(struct session *ses, int query)
138 assert(ses);
140 /* An exit query is in progress. */
141 if (ses->exit_query)
142 return;
144 /* Force a query if the last terminal is exiting with downloads still in
145 * progress. */
146 if (query || (list_is_singleton(terminals) && are_there_downloads())) {
147 query_exit(ses);
148 return;
151 really_exit_prog(ses);
155 static void
156 go_historywards(struct terminal *term, void *target_, void *ses_)
158 struct location *target = target_;
159 struct session *ses = ses_;
161 go_history(ses, target);
164 static struct menu_item no_hist_menu[] = {
165 INIT_MENU_ITEM(N_("No history"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
166 NULL_MENU_ITEM
169 /* unhist == 0 => history
170 * unhist == 1 => unhistory */
171 static void
172 history_menu_common(struct terminal *term, struct session *ses, int unhist)
174 struct menu_item *mi = NULL;
176 if (have_location(ses)) {
177 struct location *loc;
179 for (loc = unhist ? cur_loc(ses)->next : cur_loc(ses)->prev;
180 loc != (struct location *) &ses->history.history;
181 loc = unhist ? loc->next : loc->prev) {
182 unsigned char *url;
184 if (!mi) {
185 mi = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
186 if (!mi) return;
189 url = get_uri_string(loc->vs.uri, URI_PUBLIC);
190 if (url) {
191 add_to_menu(&mi, url, NULL, ACT_MAIN_NONE,
192 go_historywards,
193 (void *) loc, 0);
198 if (!mi)
199 do_menu(term, no_hist_menu, ses, 0);
200 else
201 do_menu(term, mi, ses, 0);
204 static void
205 history_menu(struct terminal *term, void *xxx, void *ses_)
207 struct session *ses = ses_;
209 history_menu_common(term, ses, 0);
212 static void
213 unhistory_menu(struct terminal *term, void *xxx, void *ses_)
215 struct session *ses = ses_;
217 history_menu_common(term, ses, 1);
220 void
221 tab_menu(struct session *ses, int x, int y, int place_above_cursor)
223 /* [gettext_accelerator_context(tab_menu)] */
224 struct menu_item *menu;
225 int tabs_count;
226 #ifdef CONFIG_BOOKMARKS
227 int anonymous = get_cmd_opt_bool("anonymous");
228 #endif
230 assert(ses && ses->tab);
231 if_assert_failed return;
233 tabs_count = number_of_tabs(ses->tab->term);
234 menu = new_menu(FREE_LIST);
235 if (!menu) return;
237 add_menu_action(&menu, N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK);
238 add_menu_action(&menu, N_("Go for~ward"), ACT_MAIN_HISTORY_MOVE_FORWARD);
240 if (have_location(ses)) {
241 add_menu_separator(&menu);
243 #ifdef CONFIG_BOOKMARKS
244 if (!anonymous) {
245 add_menu_action(&menu, N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK);
247 #endif
249 add_menu_action(&menu, N_("Toggle ~HTML/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN);
250 add_menu_action(&menu, N_("~Reload"), ACT_MAIN_RELOAD);
252 if (ses->doc_view && document_has_frames(ses->doc_view->document)) {
253 add_menu_action(&menu, N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE);
254 add_uri_command_to_menu(&menu, PASS_URI_FRAME,
255 N_("~Pass frame URI to external command"));
259 /* Keep tab related operations below this separator */
260 add_menu_separator(&menu);
262 if (tabs_count > 1) {
263 add_menu_action(&menu, N_("Nex~t tab"), ACT_MAIN_TAB_NEXT);
264 add_menu_action(&menu, N_("Pre~v tab"), ACT_MAIN_TAB_PREV);
267 add_menu_action(&menu, N_("~Close tab"), ACT_MAIN_TAB_CLOSE);
269 if (tabs_count > 1) {
270 add_menu_action(&menu, N_("C~lose all tabs but the current"),
271 ACT_MAIN_TAB_CLOSE_ALL_BUT_CURRENT);
272 #ifdef CONFIG_BOOKMARKS
273 if (!anonymous) {
274 add_menu_action(&menu, N_("B~ookmark all tabs"),
275 ACT_MAIN_ADD_BOOKMARK_TABS);
277 #endif
280 if (have_location(ses)) {
281 add_uri_command_to_menu(&menu, PASS_URI_TAB,
282 N_("Pass tab URI to e~xternal command"));
285 /* Adjust the menu position taking the menu frame into account */
286 if (place_above_cursor) {
287 int i = 0;
289 while (menu[i].text) i++;
291 y = int_max(y - i - 1, 0);
294 set_window_ptr(ses->tab, x, y);
296 do_menu(ses->tab->term, menu, ses, 1);
299 static void
300 do_submenu(struct terminal *term, void *menu_, void *ses_)
302 struct menu_item *menu = menu_;
304 do_menu(term, menu, ses_, 1);
308 static struct menu_item file_menu11[] = {
309 /* [gettext_accelerator_context(.file_menu)] */
310 INIT_MENU_ACTION(N_("Open new ~tab"), ACT_MAIN_OPEN_NEW_TAB),
311 INIT_MENU_ACTION(N_("Open new tab in backgroun~d"), ACT_MAIN_OPEN_NEW_TAB_IN_BACKGROUND),
312 INIT_MENU_ACTION(N_("~Go to URL"), ACT_MAIN_GOTO_URL),
313 INIT_MENU_ACTION(N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK),
314 INIT_MENU_ACTION(N_("Go ~forward"), ACT_MAIN_HISTORY_MOVE_FORWARD),
315 INIT_MENU_ITEM(N_("~History"), NULL, ACT_MAIN_NONE, history_menu, NULL, SUBMENU),
316 INIT_MENU_ITEM(N_("~Unhistory"), NULL, ACT_MAIN_NONE, unhistory_menu, NULL, SUBMENU),
319 static struct menu_item file_menu21[] = {
320 /* [gettext_accelerator_context(.file_menu)] */
321 BAR_MENU_ITEM,
322 INIT_MENU_ACTION(N_("~Save as"), ACT_MAIN_SAVE_AS),
323 INIT_MENU_ACTION(N_("Save UR~L as"), ACT_MAIN_SAVE_URL_AS),
324 INIT_MENU_ACTION(N_("Sa~ve formatted document"), ACT_MAIN_SAVE_FORMATTED),
325 #ifdef CONFIG_BOOKMARKS
326 INIT_MENU_ACTION(N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK),
327 #endif
330 static struct menu_item file_menu22[] = {
331 /* [gettext_accelerator_context(.file_menu)] */
332 BAR_MENU_ITEM,
333 INIT_MENU_ACTION(N_("~Kill background connections"), ACT_MAIN_KILL_BACKGROUNDED_CONNECTIONS),
334 INIT_MENU_ACTION(N_("Flush all ~caches"), ACT_MAIN_CACHE_MINIMIZE),
335 INIT_MENU_ACTION(N_("Resource ~info"), ACT_MAIN_RESOURCE_INFO),
336 BAR_MENU_ITEM,
339 static struct menu_item file_menu3[] = {
340 /* [gettext_accelerator_context(.file_menu)] */
341 BAR_MENU_ITEM,
342 INIT_MENU_ACTION(N_("E~xit"), ACT_MAIN_QUIT),
343 NULL_MENU_ITEM,
346 static void
347 do_file_menu(struct terminal *term, void *xxx, void *ses_)
349 /* [gettext_accelerator_context(.file_menu)] */
350 struct menu_item *file_menu, *e, *f;
351 int anonymous = get_cmd_opt_bool("anonymous");
352 int x, o;
354 file_menu = mem_alloc(sizeof(file_menu11) + sizeof(file_menu21)
355 + sizeof(file_menu22) + sizeof(file_menu3)
356 + 3 * sizeof(struct menu_item));
357 if (!file_menu) return;
359 e = file_menu;
361 if (!anonymous
362 && !get_cmd_opt_bool("no-connect")
363 && !get_cmd_opt_bool("no-home"))
364 o = can_open_in_new(term);
365 else
366 o = 0;
368 if (o) {
369 SET_MENU_ITEM(e, N_("Open ~new window"), NULL, ACT_MAIN_OPEN_NEW_WINDOW,
370 open_in_new_window, send_open_new_window,
371 (o - 1) ? SUBMENU : 0, HKS_SHOW, 0);
372 e++;
375 memcpy(e, file_menu11, sizeof(file_menu11));
376 e += sizeof_array(file_menu11);
378 if (!anonymous) {
379 memcpy(e, file_menu21, sizeof(file_menu21));
380 e += sizeof_array(file_menu21);
383 memcpy(e, file_menu22, sizeof(file_menu22));
384 e += sizeof_array(file_menu22);
386 x = 1;
387 if (!anonymous && can_open_os_shell(term->environment)) {
388 SET_MENU_ITEM(e, N_("~OS shell"), NULL, ACT_MAIN_OPEN_OS_SHELL,
389 NULL, NULL, 0, HKS_SHOW, 0);
390 e++;
391 x = 0;
394 if (can_resize_window(term->environment)) {
395 SET_MENU_ITEM(e, N_("Resize t~erminal"), NULL, ACT_MAIN_TERMINAL_RESIZE,
396 NULL, NULL, 0, HKS_SHOW, 0);
397 e++;
398 x = 0;
401 memcpy(e, file_menu3 + x, sizeof(file_menu3) - x * sizeof(struct menu_item));
402 e += sizeof_array(file_menu3);
404 for (f = file_menu; f < e; f++)
405 f->flags |= FREE_LIST;
407 do_menu(term, file_menu, ses_, 1);
410 static struct menu_item view_menu[] = {
411 /* [gettext_accelerator_context(.view_menu)] */
412 INIT_MENU_ACTION(N_("~Search"), ACT_MAIN_SEARCH),
413 INIT_MENU_ACTION(N_("Search ~backward"), ACT_MAIN_SEARCH_BACK),
414 INIT_MENU_ACTION(N_("Find ~next"), ACT_MAIN_FIND_NEXT),
415 INIT_MENU_ACTION(N_("Find ~previous"), ACT_MAIN_FIND_NEXT_BACK),
416 INIT_MENU_ACTION(N_("T~ypeahead search"), ACT_MAIN_SEARCH_TYPEAHEAD),
417 BAR_MENU_ITEM,
418 INIT_MENU_ACTION(N_("Toggle ~HTML/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN),
419 INIT_MENU_ACTION(N_("Toggle i~mages"), ACT_MAIN_TOGGLE_DISPLAY_IMAGES),
420 INIT_MENU_ACTION(N_("Toggle ~link numbering"), ACT_MAIN_TOGGLE_NUMBERED_LINKS),
421 INIT_MENU_ACTION(N_("Toggle ~document colors"), ACT_MAIN_TOGGLE_DOCUMENT_COLORS),
422 INIT_MENU_ACTION(N_("~Wrap text on/off"), ACT_MAIN_TOGGLE_WRAP_TEXT),
423 BAR_MENU_ITEM,
424 INIT_MENU_ACTION(N_("Document ~info"), ACT_MAIN_DOCUMENT_INFO),
425 INIT_MENU_ACTION(N_("H~eader info"), ACT_MAIN_HEADER_INFO),
426 INIT_MENU_ACTION(N_("Rel~oad document"), ACT_MAIN_RELOAD),
427 INIT_MENU_ACTION(N_("~Rerender document"), ACT_MAIN_RERENDER),
428 INIT_MENU_ACTION(N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE),
429 BAR_MENU_ITEM,
430 INIT_MENU_ACTION(N_("Nex~t tab"), ACT_MAIN_TAB_NEXT),
431 INIT_MENU_ACTION(N_("Pre~v tab"), ACT_MAIN_TAB_PREV),
432 INIT_MENU_ACTION(N_("~Close tab"), ACT_MAIN_TAB_CLOSE),
433 NULL_MENU_ITEM
437 static struct menu_item help_menu[] = {
438 /* [gettext_accelerator_context(.help_menu)] */
439 INIT_MENU_ITEM(N_("~ELinks homepage"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_WEBSITE_URL, 0),
440 INIT_MENU_ITEM(N_("~Documentation"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_DOC_URL, 0),
441 INIT_MENU_ITEM(N_("~Keys"), NULL, ACT_MAIN_NONE, menu_keys, NULL, 0),
442 #ifdef CONFIG_LEDS
443 INIT_MENU_ITEM(N_("LED ~indicators"), NULL, ACT_MAIN_NONE, menu_leds_info, NULL, 0),
444 #endif
445 BAR_MENU_ITEM,
446 INIT_MENU_ITEM(N_("~Bugs information"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_BUGS_URL, 0),
447 #ifdef CONFIG_DEBUG
448 INIT_MENU_ITEM(N_("ELinks ~GITWeb"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_GITWEB_URL, 0),
449 #endif
450 BAR_MENU_ITEM,
451 INIT_MENU_ITEM(N_("~Copying"), NULL, ACT_MAIN_NONE, menu_copying, NULL, 0),
452 INIT_MENU_ITEM(N_("Autho~rs"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_AUTHORS_URL, 0),
453 INIT_MENU_ITEM(N_("~About"), NULL, ACT_MAIN_NONE, menu_about, NULL, 0),
454 NULL_MENU_ITEM
458 static struct menu_item ext_menu[] = {
459 /* [gettext_accelerator_context(.ext_menu)] */
460 INIT_MENU_ITEM(N_("~Add"), NULL, ACT_MAIN_NONE, menu_add_ext, NULL, 0),
461 INIT_MENU_ITEM(N_("~Modify"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_add_ext, SUBMENU),
462 INIT_MENU_ITEM(N_("~Delete"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_del_ext, SUBMENU),
463 NULL_MENU_ITEM
466 static struct menu_item setup_menu[] = {
467 /* [gettext_accelerator_context(.setup_menu)] */
468 #ifdef CONFIG_NLS
469 INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
470 #endif
471 INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
472 INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
473 INIT_MENU_ITEM(N_("File ~extensions"), NULL, ACT_MAIN_NONE, do_submenu, ext_menu, SUBMENU),
474 BAR_MENU_ITEM,
475 INIT_MENU_ACTION(N_("~Options manager"), ACT_MAIN_OPTIONS_MANAGER),
476 INIT_MENU_ACTION(N_("~Keybinding manager"), ACT_MAIN_KEYBINDING_MANAGER),
477 INIT_MENU_ACTION(N_("~Save options"), ACT_MAIN_SAVE_OPTIONS),
478 NULL_MENU_ITEM
481 static struct menu_item setup_menu_anon[] = {
482 /* [gettext_accelerator_context(.setup_menu)] */
483 INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
484 INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
485 INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
486 NULL_MENU_ITEM
489 static struct menu_item tools_menu[] = {
490 /* [gettext_accelerator_context(.tools_menu)] */
491 #ifdef CONFIG_GLOBHIST
492 INIT_MENU_ACTION(N_("Global ~history"), ACT_MAIN_HISTORY_MANAGER),
493 #endif
494 #ifdef CONFIG_BOOKMARKS
495 INIT_MENU_ACTION(N_("~Bookmarks"), ACT_MAIN_BOOKMARK_MANAGER),
496 #endif
497 INIT_MENU_ACTION(N_("~Cache"), ACT_MAIN_CACHE_MANAGER),
498 INIT_MENU_ACTION(N_("~Downloads"), ACT_MAIN_DOWNLOAD_MANAGER),
499 #ifdef CONFIG_COOKIES
500 INIT_MENU_ACTION(N_("Coo~kies"), ACT_MAIN_COOKIE_MANAGER),
501 #endif
502 #ifdef CONFIG_FORMHIST
503 INIT_MENU_ACTION(N_("~Form history"), ACT_MAIN_FORMHIST_MANAGER),
504 #endif
505 INIT_MENU_ACTION(N_("~Authentication"), ACT_MAIN_AUTH_MANAGER),
506 NULL_MENU_ITEM
509 static void
510 do_setup_menu(struct terminal *term, void *xxx, void *ses_)
512 struct session *ses = ses_;
514 if (!get_cmd_opt_bool("anonymous"))
515 do_menu(term, setup_menu, ses, 1);
516 else
517 do_menu(term, setup_menu_anon, ses, 1);
520 static struct menu_item main_menu[] = {
521 /* [gettext_accelerator_context(.main_menu)] */
522 INIT_MENU_ITEM(N_("~File"), NULL, ACT_MAIN_NONE, do_file_menu, NULL, FREE_LIST | SUBMENU),
523 INIT_MENU_ITEM(N_("~View"), NULL, ACT_MAIN_NONE, do_submenu, view_menu, FREE_LIST | SUBMENU),
524 INIT_MENU_ITEM(N_("~Link"), NULL, ACT_MAIN_NONE, link_menu, NULL, FREE_LIST | SUBMENU),
525 INIT_MENU_ITEM(N_("~Tools"), NULL, ACT_MAIN_NONE, do_submenu, tools_menu, FREE_LIST | SUBMENU),
526 INIT_MENU_ITEM(N_("~Setup"), NULL, ACT_MAIN_NONE, do_setup_menu, NULL, FREE_LIST | SUBMENU),
527 INIT_MENU_ITEM(N_("~Help"), NULL, ACT_MAIN_NONE, do_submenu, help_menu, FREE_LIST | SUBMENU),
528 NULL_MENU_ITEM
531 void
532 activate_bfu_technology(struct session *ses, int item)
534 do_mainmenu(ses->tab->term, main_menu, ses, item);
538 void
539 dialog_goto_url(struct session *ses, unsigned char *url)
541 input_dialog(ses->tab->term, NULL,
542 N_("Go to URL"), N_("Enter URL"),
543 ses, &goto_url_history,
544 MAX_STR_LEN, url, 0, 0, NULL,
545 (void (*)(void *, unsigned char *)) goto_url_with_hook,
546 NULL);
550 static INIT_INPUT_HISTORY(file_history);
552 void
553 query_file(struct session *ses, struct uri *uri, void *data,
554 void (*std)(void *, unsigned char *),
555 void (*cancel)(void *), int interactive)
557 struct string def;
559 assert(ses && uri);
560 if_assert_failed return;
562 /* FIXME: This ``sanity'' checking is mostly for the download code
563 * using this function. They pass ses->download_uri and we have to make
564 * sure that the connection code can download the URI. The reason we do
565 * it before is that then users won't waste time typing a filename and
566 * then discover that the URI can not be downloaded. However it might
567 * be better to introduce a set_session_download_uri() which will do
568 * the checking? --jonas */
570 if (uri->protocol == PROTOCOL_UNKNOWN) {
571 print_error_dialog(ses, S_UNKNOWN_PROTOCOL, uri, PRI_CANCEL);
572 return;
575 if (get_protocol_external_handler(ses->tab->term, uri)) {
576 print_error_dialog(ses, S_EXTERNAL_PROTOCOL, uri, PRI_CANCEL);
577 return;
580 if (!init_string(&def)) return;
582 add_to_string(&def, get_opt_str("document.download.directory", NULL));
583 if (def.length && !dir_sep(def.source[def.length - 1]))
584 add_char_to_string(&def, '/');
586 add_mime_filename_to_string(&def, uri);
588 /* Remove the %-ugliness for display */
589 #ifdef CONFIG_UTF8
590 if (ses->tab->term->utf8_cp)
591 decode_uri_string(&def);
592 else
593 #endif /* CONFIG_UTF8 */
594 decode_uri_string_for_display(&def);
596 if (interactive) {
597 input_dialog(ses->tab->term, NULL,
598 N_("Download"), N_("Save to file"),
599 data, &file_history,
600 MAX_STR_LEN, def.source, 0, 0, check_nonempty,
601 (void (*)(void *, unsigned char *)) std,
602 (void (*)(void *)) cancel);
603 } else {
604 std(data, def.source);
607 done_string(&def);
610 void
611 free_history_lists(void)
613 free_list(file_history.entries);
614 #ifdef CONFIG_SCRIPTING
615 trigger_event_name("free-history");
616 #endif
620 static void
621 add_cmdline_bool_option(struct string *string, unsigned char *name)
623 if (!get_cmd_opt_bool(name)) return;
624 add_to_string(string, " -");
625 add_to_string(string, name);
628 void
629 open_uri_in_new_window(struct session *ses, struct uri *uri, struct uri *referrer,
630 enum term_env_type env, enum cache_mode cache_mode,
631 enum task_type task)
633 int ring = get_cmd_opt_int("session-ring");
634 struct string parameters;
635 int id;
637 assert(env && ses);
638 if_assert_failed return;
640 id = add_session_info(ses, uri, referrer, cache_mode, task);
641 if (id < 1) return;
643 if (!init_string(&parameters)) return;
645 add_format_to_string(&parameters, "-base-session %d", id);
646 if (ring) add_format_to_string(&parameters, " -session-ring %d", ring);
648 /* No URI means open new (clean) window possibly without connecting to
649 * the current master so add command line options to properly clone the
650 * current master */
651 if (!uri) {
652 /* Adding -touch-files will only lead to problems */
653 add_cmdline_bool_option(&parameters, "localhost");
654 add_cmdline_bool_option(&parameters, "no-home");
655 add_cmdline_bool_option(&parameters, "no-connect");
658 open_new_window(ses->tab->term, program.path, env, parameters.source);
659 done_string(&parameters);
662 /* Open a link in a new xterm. */
663 void
664 send_open_in_new_window(struct terminal *term, const struct open_in_new *open,
665 struct session *ses)
667 struct document_view *doc_view;
668 struct link *link;
669 struct uri *uri;
671 assert(term && open && ses);
672 if_assert_failed return;
673 doc_view = current_frame(ses);
674 assert(doc_view && doc_view->vs && doc_view->document);
675 if_assert_failed return;
677 link = get_current_link(doc_view);
678 if (!link) return;
680 uri = get_link_uri(ses, doc_view, link);
681 if (!uri) return;
683 open_uri_in_new_window(ses, uri, NULL, open->env,
684 CACHE_MODE_NORMAL, TASK_NONE);
685 done_uri(uri);
688 void
689 send_open_new_window(struct terminal *term, const struct open_in_new *open,
690 struct session *ses)
692 open_uri_in_new_window(ses, NULL, NULL, open->env,
693 CACHE_MODE_NORMAL, TASK_NONE);
697 void
698 open_in_new_window(struct terminal *term, void *func_, void *ses_)
700 menu_func_T func = func_;
701 struct session *ses = ses_;
702 struct menu_item *mi;
703 int posibilities;
705 assert(term && ses && func);
706 if_assert_failed return;
708 switch (can_open_in_new(term)) {
709 case 0:
710 return;
712 case 1:
713 mi = NULL;
714 break;
716 default:
717 mi = new_menu(FREE_LIST);
718 if (!mi) return;
721 foreach_open_in_new (posibilities, term->environment) {
722 const struct open_in_new *oi = &open_in_new[posibilities];
724 if (mi == NULL) {
725 func(term, (void *) oi, ses);
726 return;
728 add_to_menu(&mi, oi->text, NULL, ACT_MAIN_NONE, func, (void *) oi, 0);
731 do_menu(term, mi, ses, 1);
735 void
736 add_new_win_to_menu(struct menu_item **mi, unsigned char *text,
737 struct terminal *term)
739 int c = can_open_in_new(term);
741 if (!c) return;
743 /* The URI is saved as session info in the master and not sent to the
744 * instance in the new window so with -no-connect or -no-home enabled
745 * it is not possible to open links URIs. For -anonymous one window
746 * should be enough. */
747 if (get_cmd_opt_bool("no-connect")
748 || get_cmd_opt_bool("no-home")
749 || get_cmd_opt_bool("anonymous"))
750 return;
752 add_to_menu(mi, text, NULL, ACT_MAIN_OPEN_LINK_IN_NEW_WINDOW,
753 open_in_new_window,
754 send_open_in_new_window, c - 1 ? SUBMENU : 0);
758 static void
759 do_pass_uri_to_command(struct terminal *term, void *command_, void *xxx)
761 unsigned char *command = command_;
763 exec_on_terminal(term, command, "", TERM_EXEC_BG);
764 mem_free(command);
767 /* TODO:
768 * - Support for passing MIME type
769 * - Merge this function with rewrite_uri(), subst_cmd(), subst_file()
770 * and subst_url(). */
771 static unsigned char *
772 format_command(unsigned char *format, struct uri *uri)
774 struct string string;
776 if (!init_string(&string)) return NULL;
778 while (*format) {
779 int pos = 0;
781 while (format[pos] && format[pos] != '%') pos++;
783 add_bytes_to_string(&string, format, pos);
784 format += pos;
786 if (*format != '%') continue;
788 format++;
789 switch (*format) {
790 case 'c':
792 unsigned char *str = struri(uri);
793 int length = get_real_uri_length(uri);
795 add_shell_quoted_to_string(&string,
796 str, length);
797 break;
799 case '%':
800 add_char_to_string(&string, '%');
801 break;
802 default:
803 add_bytes_to_string(&string, format - 1, 2);
804 break;
806 if (*format) format++;
809 return string.source;
812 enum frame_event_status
813 pass_uri_to_command(struct session *ses, struct document_view *doc_view,
814 int which_type)
816 LIST_OF(struct option) *tree = get_opt_tree("document.uri_passing",
817 NULL);
818 enum pass_uri_type type = which_type;
819 struct menu_item *items;
820 struct option *option;
821 struct uri *uri;
822 int commands = 0;
824 switch (type) {
825 case PASS_URI_FRAME:
826 uri = get_uri_reference(doc_view->document->uri);
827 break;
829 case PASS_URI_LINK:
831 struct link *link = get_current_link(doc_view);
833 if (!link) return FRAME_EVENT_OK;
835 uri = get_link_uri(ses, doc_view, link);
836 if (!uri) return FRAME_EVENT_OK;
837 break;
839 default:
840 case PASS_URI_TAB:
841 uri = get_uri_reference(ses->doc_view->document->uri);
844 items = new_menu(FREE_LIST | FREE_TEXT | FREE_DATA | NO_INTL);
845 if (!items) {
846 done_uri(uri);
847 return FRAME_EVENT_OK;
850 foreach (option, *tree) {
851 unsigned char *text, *data;
853 if (!strcmp(option->name, "_template_"))
854 continue;
856 text = stracpy(option->name);
857 if (!text) continue;
859 data = format_command(option->value.string, uri);
860 if (!data) {
861 mem_free(text);
862 continue;
865 add_to_menu(&items, text, NULL, ACT_MAIN_NONE,
866 do_pass_uri_to_command, data, 0);
867 commands++;
870 done_uri(uri);
872 if (commands > 1) {
873 do_menu(ses->tab->term, items, ses, 1);
874 } else {
875 if (commands == 1)
876 do_pass_uri_to_command(ses->tab->term, items->data, ses);
877 else
878 mem_free(items->data);
879 mem_free(items->text);
880 mem_free(items);
883 return FRAME_EVENT_OK;
886 /* The caller provides the text of the menu item, so that it can
887 * choose an available accelerator key. */
888 void
889 add_uri_command_to_menu(struct menu_item **mi, enum pass_uri_type type,
890 unsigned char *text)
892 LIST_OF(struct option) *tree = get_opt_tree("document.uri_passing",
893 NULL);
894 struct option *option;
895 int commands = 0;
896 enum menu_item_flags flags = NO_FLAG;
897 action_id_T action_id;
899 switch (type) {
900 case PASS_URI_FRAME:
901 action_id = ACT_MAIN_FRAME_EXTERNAL_COMMAND;
902 break;
904 case PASS_URI_LINK:
905 action_id = ACT_MAIN_LINK_EXTERNAL_COMMAND;
906 break;
908 default:
909 case PASS_URI_TAB:
910 action_id = ACT_MAIN_TAB_EXTERNAL_COMMAND;
913 foreach (option, *tree) {
914 if (!strcmp(option->name, "_template_"))
915 continue;
917 commands++;
918 if (commands > 1) {
919 flags = SUBMENU;
920 break;
924 if (commands == 0) return;
926 add_to_menu(mi, text, NULL, action_id, NULL, NULL, flags);
930 /* The file completion menu always has two non selectable menu item at the
931 * start. First is the 'Directory:' or 'Files:' text and then a separator. */
932 #define FILE_COMPLETION_MENU_OFFSET 2
934 static struct menu_item empty_directory_menu[] = {
935 INIT_MENU_ITEM(N_("Empty directory"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
936 NULL_MENU_ITEM
939 /* Builds the file completion menu. If there is only one item it is selected
940 * else the menu is launched. */
941 static void
942 complete_file_menu(struct terminal *term, int no_elevator, void *data,
943 menu_func_T file_func, menu_func_T dir_func,
944 unsigned char *dirname, unsigned char *filename)
946 struct menu_item *menu = new_menu(FREE_LIST | NO_INTL);
947 struct directory_entry *entries, *entry;
948 int filenamelen = strlen(filename);
949 int direntries = 0, fileentries = 0;
951 if (!menu) return;
953 entries = get_directory_entries(dirname, 1);
954 if (!entries) {
955 mem_free(menu);
956 return;
959 for (entry = entries; entry->name; entry++) {
960 unsigned char *text;
961 int is_dir = (*entry->attrib == 'd');
962 int is_file = (*entry->attrib == '-');
964 mem_free(entry->attrib);
965 if ((!is_dir && !is_file) || !file_can_read(entry->name)) {
966 mem_free(entry->name);
967 continue;
970 text = get_filename_position(entry->name);
971 if (strncmp(filename, text, filenamelen)
972 || (no_elevator && !strcmp("..", text))) {
973 mem_free(entry->name);
974 continue;
977 if (is_dir) {
978 if (!direntries) {
979 add_to_menu(&menu, _("Directories:", term), NULL,
980 ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
981 add_menu_separator(&menu);
984 add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
985 dir_func, entry->name, FREE_DATA | SUBMENU);
987 direntries++;
989 } else {
990 if (!fileentries) {
991 if (direntries) add_menu_separator(&menu);
992 add_to_menu(&menu, _("Files:", term), NULL,
993 ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
994 add_menu_separator(&menu);
997 add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
998 file_func, entry->name, FREE_DATA);
1000 fileentries++;
1005 mem_free(entries);
1006 if (direntries == 0 && fileentries == 0) {
1007 mem_free(menu);
1008 return;
1011 /* Only one entry */
1012 if (direntries + fileentries == 1) {
1013 unsigned char *text = menu[FILE_COMPLETION_MENU_OFFSET].data;
1015 mem_free(menu);
1017 if (fileentries) {
1018 /* Complete what is already there */
1019 file_func(term, text, data);
1020 return;
1023 /* For single directory entries open the lonely subdir if it is
1024 * not the parent elevator. */
1025 if (strcmp(&text[strlen(dirname)], "..")) {
1026 dir_func(term, text, data);
1027 } else {
1028 do_menu(term, empty_directory_menu, NULL, 0); \
1031 mem_free(text);
1033 } else {
1034 /* Start with the first directory or file entry selected */
1035 do_menu(term, menu, data, 0);
1039 /* Prepares the launching of the file completion menu by expanding the @path
1040 * and splitting it in directory and file name part. */
1041 void
1042 auto_complete_file(struct terminal *term, int no_elevator, unsigned char *path,
1043 menu_func_T file_func, menu_func_T dir_func, void *data)
1045 struct uri *uri;
1046 unsigned char *dirname;
1047 unsigned char *filename;
1049 assert(term && data && file_func && dir_func && data);
1051 if (get_cmd_opt_bool("anonymous"))
1052 return;
1054 if (!*path) path = "./";
1056 /* Use the URI translation to handle ./ and ../ and ~/ expansion */
1057 uri = get_translated_uri(path, term->cwd);
1058 if (!uri) return;
1060 if (uri->protocol != PROTOCOL_FILE) {
1061 path = NULL;
1062 } else {
1063 path = get_uri_string(uri, URI_PATH);
1066 done_uri(uri);
1067 if (!path) return;
1069 filename = get_filename_position(path);
1071 if (*filename && file_is_dir(path)) {
1072 filename = path + strlen(path);
1074 } else if (*filename && file_exists(path)) {
1075 /* Complete any tilde expansion */
1076 file_func(term, path, data);
1077 return;
1080 /* Split the path into @dirname and @filename */
1081 dirname = path;
1082 path = filename;
1083 filename = stracpy(path);
1084 *path = 0;
1086 /* Make sure the dirname has an ending slash */
1087 if (!dir_sep(path[-1])) {
1088 unsigned char separator = *dirname;
1089 int dirnamelen = path - dirname;
1091 insert_in_string(&dirname, dirnamelen, &separator, 1);
1094 complete_file_menu(term, no_elevator, data,
1095 file_func, dir_func, dirname, filename);
1097 mem_free(dirname);
1098 mem_free(filename);