Merge with git+ssh://pasky/srv/git/elinks.git
[elinks.git] / src / dialogs / menu.c
blob392869df63cd23f45225120db1bf907f6b43e2c0
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 void
104 really_exit_prog(struct session *ses)
106 register_bottom_half(destroy_terminal, ses->tab->term);
109 static inline void
110 dont_exit_prog(struct session *ses)
112 ses->exit_query = 0;
115 void
116 query_exit(struct session *ses)
118 ses->exit_query = 1;
119 msg_box(ses->tab->term, NULL, 0,
120 N_("Exit ELinks"), ALIGN_CENTER,
121 (ses->tab->term->next == ses->tab->term->prev && are_there_downloads())
122 ? N_("Do you really want to exit ELinks "
123 "(and terminate all downloads)?")
124 : N_("Do you really want to exit ELinks?"),
125 ses, 2,
126 N_("~Yes"), (void (*)(void *)) really_exit_prog, B_ENTER,
127 N_("~No"), (void (*)(void *)) dont_exit_prog, B_ESC);
130 void
131 exit_prog(struct session *ses, int query)
133 assert(ses);
135 /* An exit query is in progress. */
136 if (ses->exit_query)
137 return;
139 /* Force a query if the last terminal is exiting with downloads still in
140 * progress. */
141 if (query || (list_is_singleton(terminals) && are_there_downloads())) {
142 query_exit(ses);
143 return;
146 really_exit_prog(ses);
150 static void
151 go_historywards(struct terminal *term, void *target_, void *ses_)
153 struct location *target = target_;
154 struct session *ses = ses_;
156 go_history(ses, target);
159 static struct menu_item no_hist_menu[] = {
160 INIT_MENU_ITEM(N_("No history"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
161 NULL_MENU_ITEM
164 /* unhist == 0 => history
165 * unhist == 1 => unhistory */
166 static void
167 history_menu_common(struct terminal *term, struct session *ses, int unhist)
169 struct menu_item *mi = NULL;
171 if (have_location(ses)) {
172 struct location *loc;
174 for (loc = unhist ? cur_loc(ses)->next : cur_loc(ses)->prev;
175 loc != (struct location *) &ses->history.history;
176 loc = unhist ? loc->next : loc->prev) {
177 unsigned char *url;
179 if (!mi) {
180 mi = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
181 if (!mi) return;
184 url = get_uri_string(loc->vs.uri, URI_PUBLIC);
185 if (url) {
186 add_to_menu(&mi, url, NULL, ACT_MAIN_NONE,
187 go_historywards,
188 (void *) loc, 0);
193 if (!mi)
194 do_menu(term, no_hist_menu, ses, 0);
195 else
196 do_menu(term, mi, ses, 0);
199 static void
200 history_menu(struct terminal *term, void *xxx, void *ses_)
202 struct session *ses = ses_;
204 history_menu_common(term, ses, 0);
207 static void
208 unhistory_menu(struct terminal *term, void *xxx, void *ses_)
210 struct session *ses = ses_;
212 history_menu_common(term, ses, 1);
215 void
216 tab_menu(struct session *ses, int x, int y, int place_above_cursor)
218 struct menu_item *menu;
219 int tabs;
220 #ifdef CONFIG_BOOKMARKS
221 int anonymous = get_cmd_opt_bool("anonymous");
222 #endif
224 assert(ses && ses->tab);
225 if_assert_failed return;
227 tabs = number_of_tabs(ses->tab->term);
228 menu = new_menu(FREE_LIST);
229 if (!menu) return;
231 add_menu_action(&menu, N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK);
232 add_menu_action(&menu, N_("Go for~ward"), ACT_MAIN_HISTORY_MOVE_FORWARD);
234 if (have_location(ses)) {
235 add_menu_separator(&menu);
237 #ifdef CONFIG_BOOKMARKS
238 if (!anonymous) {
239 add_menu_action(&menu, N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK);
241 #endif
243 add_menu_action(&menu, N_("Toggle ~html/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN);
244 add_menu_action(&menu, N_("~Reload"), ACT_MAIN_RELOAD);
246 if (ses->doc_view && document_has_frames(ses->doc_view->document)) {
247 add_menu_action(&menu, N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE);
248 add_uri_command_to_menu(&menu, PASS_URI_FRAME);
252 /* Keep tab related operations below this separator */
253 add_menu_separator(&menu);
255 if (tabs > 1) {
256 add_menu_action(&menu, N_("Nex~t tab"), ACT_MAIN_TAB_NEXT);
257 add_menu_action(&menu, N_("Pre~v tab"), ACT_MAIN_TAB_PREV);
260 add_menu_action(&menu, N_("~Close tab"), ACT_MAIN_TAB_CLOSE);
262 if (tabs > 1) {
263 add_menu_action(&menu, N_("C~lose all tabs but the current"),
264 ACT_MAIN_TAB_CLOSE_ALL_BUT_CURRENT);
265 #ifdef CONFIG_BOOKMARKS
266 if (!anonymous) {
267 add_menu_action(&menu, N_("B~ookmark all tabs"),
268 ACT_MAIN_ADD_BOOKMARK_TABS);
270 #endif
273 if (have_location(ses))
274 add_uri_command_to_menu(&menu, PASS_URI_TAB);
276 /* Adjust the menu position taking the menu frame into account */
277 if (place_above_cursor) {
278 int i = 0;
280 while (menu[i].text) i++;
282 y = int_max(y - i - 1, 0);
285 set_window_ptr(ses->tab, x, y);
287 do_menu(ses->tab->term, menu, ses, 1);
290 static void
291 do_submenu(struct terminal *term, void *menu_, void *ses_)
293 struct menu_item *menu = menu_;
295 do_menu(term, menu, ses_, 1);
299 static struct menu_item file_menu11[] = {
300 INIT_MENU_ACTION(N_("Open new ~tab"), ACT_MAIN_OPEN_NEW_TAB),
301 INIT_MENU_ACTION(N_("Open new tab in backgroun~d"), ACT_MAIN_OPEN_NEW_TAB_IN_BACKGROUND),
302 INIT_MENU_ACTION(N_("~Go to URL"), ACT_MAIN_GOTO_URL),
303 INIT_MENU_ACTION(N_("Go ~back"), ACT_MAIN_HISTORY_MOVE_BACK),
304 INIT_MENU_ACTION(N_("Go ~forward"), ACT_MAIN_HISTORY_MOVE_FORWARD),
305 INIT_MENU_ITEM(N_("~History"), NULL, ACT_MAIN_NONE, history_menu, NULL, SUBMENU),
306 INIT_MENU_ITEM(N_("~Unhistory"), NULL, ACT_MAIN_NONE, unhistory_menu, NULL, SUBMENU),
309 static struct menu_item file_menu21[] = {
310 BAR_MENU_ITEM,
311 INIT_MENU_ACTION(N_("~Save as"), ACT_MAIN_SAVE_AS),
312 INIT_MENU_ACTION(N_("Save UR~L as"), ACT_MAIN_SAVE_URL_AS),
313 INIT_MENU_ACTION(N_("Sa~ve formatted document"), ACT_MAIN_SAVE_FORMATTED),
314 #ifdef CONFIG_BOOKMARKS
315 INIT_MENU_ACTION(N_("Bookm~ark document"), ACT_MAIN_ADD_BOOKMARK),
316 #endif
319 static struct menu_item file_menu22[] = {
320 BAR_MENU_ITEM,
321 INIT_MENU_ACTION(N_("~Kill background connections"), ACT_MAIN_KILL_BACKGROUNDED_CONNECTIONS),
322 INIT_MENU_ACTION(N_("Flush all ~caches"), ACT_MAIN_CACHE_MINIMIZE),
323 INIT_MENU_ACTION(N_("Resource ~info"), ACT_MAIN_RESOURCE_INFO),
324 BAR_MENU_ITEM,
327 static struct menu_item file_menu3[] = {
328 BAR_MENU_ITEM,
329 INIT_MENU_ACTION(N_("E~xit"), ACT_MAIN_QUIT),
330 NULL_MENU_ITEM,
333 static void
334 do_file_menu(struct terminal *term, void *xxx, void *ses_)
336 struct menu_item *file_menu, *e, *f;
337 int anonymous = get_cmd_opt_bool("anonymous");
338 int x, o;
340 file_menu = mem_alloc(sizeof(file_menu11) + sizeof(file_menu21)
341 + sizeof(file_menu22) + sizeof(file_menu3)
342 + 3 * sizeof(struct menu_item));
343 if (!file_menu) return;
345 e = file_menu;
347 if (!anonymous
348 && !get_cmd_opt_bool("no-connect")
349 && !get_cmd_opt_bool("no-home"))
350 o = can_open_in_new(term);
351 else
352 o = 0;
354 if (o) {
355 SET_MENU_ITEM(e, N_("Open ~new window"), NULL, ACT_MAIN_OPEN_NEW_WINDOW,
356 open_in_new_window, send_open_new_window,
357 (o - 1) ? SUBMENU : 0, 0, HKS_SHOW);
358 e++;
361 memcpy(e, file_menu11, sizeof(file_menu11));
362 e += sizeof_array(file_menu11);
364 if (!anonymous) {
365 memcpy(e, file_menu21, sizeof(file_menu21));
366 e += sizeof_array(file_menu21);
369 memcpy(e, file_menu22, sizeof(file_menu22));
370 e += sizeof_array(file_menu22);
372 x = 1;
373 if (!anonymous && can_open_os_shell(term->environment)) {
374 SET_MENU_ITEM(e, N_("~OS shell"), NULL, ACT_MAIN_OPEN_OS_SHELL,
375 NULL, NULL, 0, 0, HKS_SHOW);
376 e++;
377 x = 0;
380 if (can_resize_window(term->environment)) {
381 SET_MENU_ITEM(e, N_("Resize t~erminal"), NULL, ACT_MAIN_TERMINAL_RESIZE,
382 NULL, NULL, 0, 0, HKS_SHOW);
383 e++;
384 x = 0;
387 memcpy(e, file_menu3 + x, sizeof(file_menu3) - x * sizeof(struct menu_item));
388 e += sizeof_array(file_menu3);
390 for (f = file_menu; f < e; f++)
391 f->flags |= FREE_LIST;
393 do_menu(term, file_menu, ses_, 1);
396 static struct menu_item view_menu[] = {
397 INIT_MENU_ACTION(N_("~Search"), ACT_MAIN_SEARCH),
398 INIT_MENU_ACTION(N_("Search ~backward"), ACT_MAIN_SEARCH_BACK),
399 INIT_MENU_ACTION(N_("Find ~next"), ACT_MAIN_FIND_NEXT),
400 INIT_MENU_ACTION(N_("Find ~previous"), ACT_MAIN_FIND_NEXT_BACK),
401 INIT_MENU_ACTION(N_("T~ypeahead search"), ACT_MAIN_SEARCH_TYPEAHEAD),
402 BAR_MENU_ITEM,
403 INIT_MENU_ACTION(N_("Toggle ~html/plain"), ACT_MAIN_TOGGLE_HTML_PLAIN),
404 INIT_MENU_ACTION(N_("Toggle i~mages"), ACT_MAIN_TOGGLE_DISPLAY_IMAGES),
405 INIT_MENU_ACTION(N_("Toggle ~link numbering"), ACT_MAIN_TOGGLE_NUMBERED_LINKS),
406 INIT_MENU_ACTION(N_("Toggle ~document colors"), ACT_MAIN_TOGGLE_DOCUMENT_COLORS),
407 INIT_MENU_ACTION(N_("~Wrap text on/off"), ACT_MAIN_TOGGLE_WRAP_TEXT),
408 BAR_MENU_ITEM,
409 INIT_MENU_ACTION(N_("Document ~info"), ACT_MAIN_DOCUMENT_INFO),
410 INIT_MENU_ACTION(N_("H~eader info"), ACT_MAIN_HEADER_INFO),
411 INIT_MENU_ACTION(N_("Rel~oad document"), ACT_MAIN_RELOAD),
412 INIT_MENU_ACTION(N_("~Rerender document"), ACT_MAIN_RERENDER),
413 INIT_MENU_ACTION(N_("Frame at ~full-screen"), ACT_MAIN_FRAME_MAXIMIZE),
414 BAR_MENU_ITEM,
415 INIT_MENU_ACTION(N_("Nex~t tab"), ACT_MAIN_TAB_NEXT),
416 INIT_MENU_ACTION(N_("Pre~v tab"), ACT_MAIN_TAB_PREV),
417 INIT_MENU_ACTION(N_("~Close tab"), ACT_MAIN_TAB_CLOSE),
418 NULL_MENU_ITEM
422 static struct menu_item help_menu[] = {
423 INIT_MENU_ITEM(N_("~ELinks homepage"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_HOMEPAGE, 0),
424 INIT_MENU_ITEM(N_("~Documentation"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_DOC_URL, 0),
425 INIT_MENU_ITEM(N_("~Keys"), NULL, ACT_MAIN_NONE, menu_keys, NULL, 0),
426 #ifdef CONFIG_LEDS
427 INIT_MENU_ITEM(N_("LED ~indicators"), NULL, ACT_MAIN_NONE, menu_leds_info, NULL, 0),
428 #endif
429 BAR_MENU_ITEM,
430 INIT_MENU_ITEM(N_("~Bugs information"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_BUGS_URL, 0),
431 #ifdef CONFIG_DEBUG
432 INIT_MENU_ITEM(N_("ELinks ~GITWeb"), NULL, ACT_MAIN_NONE, menu_url_shortcut, ELINKS_GITWEB_URL, 0),
433 #endif
434 BAR_MENU_ITEM,
435 INIT_MENU_ITEM(N_("~Copying"), NULL, ACT_MAIN_NONE, menu_copying, NULL, 0),
436 INIT_MENU_ITEM(N_("~About"), NULL, ACT_MAIN_NONE, menu_about, NULL, 0),
437 NULL_MENU_ITEM
441 static struct menu_item ext_menu[] = {
442 INIT_MENU_ITEM(N_("~Add"), NULL, ACT_MAIN_NONE, menu_add_ext, NULL, 0),
443 INIT_MENU_ITEM(N_("~Modify"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_add_ext, SUBMENU),
444 INIT_MENU_ITEM(N_("~Delete"), NULL, ACT_MAIN_NONE, menu_list_ext, menu_del_ext, SUBMENU),
445 NULL_MENU_ITEM
448 static struct menu_item setup_menu[] = {
449 #ifdef CONFIG_NLS
450 INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
451 #endif
452 INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
453 INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
454 INIT_MENU_ITEM(N_("File ~extensions"), NULL, ACT_MAIN_NONE, do_submenu, ext_menu, SUBMENU),
455 BAR_MENU_ITEM,
456 INIT_MENU_ACTION(N_("~Options manager"), ACT_MAIN_OPTIONS_MANAGER),
457 INIT_MENU_ACTION(N_("~Keybinding manager"), ACT_MAIN_KEYBINDING_MANAGER),
458 INIT_MENU_ACTION(N_("~Save options"), ACT_MAIN_SAVE_OPTIONS),
459 NULL_MENU_ITEM
462 static struct menu_item setup_menu_anon[] = {
463 INIT_MENU_ITEM(N_("~Language"), NULL, ACT_MAIN_NONE, menu_language_list, NULL, SUBMENU),
464 INIT_MENU_ITEM(N_("C~haracter set"), NULL, ACT_MAIN_NONE, charset_list, NULL, SUBMENU),
465 INIT_MENU_ACTION(N_("~Terminal options"), ACT_MAIN_SHOW_TERM_OPTIONS),
466 NULL_MENU_ITEM
469 static struct menu_item tools_menu[] = {
470 #ifdef CONFIG_GLOBHIST
471 INIT_MENU_ACTION(N_("Global ~history"), ACT_MAIN_HISTORY_MANAGER),
472 #endif
473 #ifdef CONFIG_BOOKMARKS
474 INIT_MENU_ACTION(N_("~Bookmarks"), ACT_MAIN_BOOKMARK_MANAGER),
475 #endif
476 INIT_MENU_ACTION(N_("~Cache"), ACT_MAIN_CACHE_MANAGER),
477 INIT_MENU_ACTION(N_("~Downloads"), ACT_MAIN_DOWNLOAD_MANAGER),
478 #ifdef CONFIG_COOKIES
479 INIT_MENU_ACTION(N_("Coo~kies"), ACT_MAIN_COOKIE_MANAGER),
480 #endif
481 #ifdef CONFIG_FORMHIST
482 INIT_MENU_ACTION(N_("~Form history"), ACT_MAIN_FORMHIST_MANAGER),
483 #endif
484 INIT_MENU_ACTION(N_("~Authentication"), ACT_MAIN_AUTH_MANAGER),
485 NULL_MENU_ITEM
488 static void
489 do_setup_menu(struct terminal *term, void *xxx, void *ses_)
491 struct session *ses = ses_;
493 if (!get_cmd_opt_bool("anonymous"))
494 do_menu(term, setup_menu, ses, 1);
495 else
496 do_menu(term, setup_menu_anon, ses, 1);
499 static struct menu_item main_menu[] = {
500 INIT_MENU_ITEM(N_("~File"), NULL, ACT_MAIN_NONE, do_file_menu, NULL, FREE_LIST | SUBMENU),
501 INIT_MENU_ITEM(N_("~View"), NULL, ACT_MAIN_NONE, do_submenu, view_menu, FREE_LIST | SUBMENU),
502 INIT_MENU_ITEM(N_("~Link"), NULL, ACT_MAIN_NONE, link_menu, NULL, FREE_LIST | SUBMENU),
503 INIT_MENU_ITEM(N_("~Tools"), NULL, ACT_MAIN_NONE, do_submenu, tools_menu, FREE_LIST | SUBMENU),
504 INIT_MENU_ITEM(N_("~Setup"), NULL, ACT_MAIN_NONE, do_setup_menu, NULL, FREE_LIST | SUBMENU),
505 INIT_MENU_ITEM(N_("~Help"), NULL, ACT_MAIN_NONE, do_submenu, help_menu, FREE_LIST | SUBMENU),
506 NULL_MENU_ITEM
509 void
510 activate_bfu_technology(struct session *ses, int item)
512 do_mainmenu(ses->tab->term, main_menu, ses, item);
516 void
517 dialog_goto_url(struct session *ses, unsigned char *url)
519 input_dialog(ses->tab->term, NULL,
520 N_("Go to URL"), N_("Enter URL"),
521 ses, &goto_url_history,
522 MAX_STR_LEN, url, 0, 0, NULL,
523 (void (*)(void *, unsigned char *)) goto_url_with_hook,
524 NULL);
528 static INIT_INPUT_HISTORY(file_history);
530 void
531 query_file(struct session *ses, struct uri *uri, void *data,
532 void (*std)(void *, unsigned char *),
533 void (*cancel)(void *), int interactive)
535 struct string def;
537 assert(ses && uri);
538 if_assert_failed return;
540 /* FIXME: This ``sanity'' checking is mostly for the download code
541 * using this function. They pass ses->download_uri and we have to make
542 * sure that the connection code can download the URI. The reason we do
543 * it before is that then users won't waste time typing a filename and
544 * then discover that the URI can not be downloaded. However it might
545 * be better to introduce a set_session_download_uri() which will do
546 * the checking? --jonas */
548 if (uri->protocol == PROTOCOL_UNKNOWN) {
549 print_error_dialog(ses, S_UNKNOWN_PROTOCOL, uri, PRI_CANCEL);
550 return;
553 if (get_protocol_external_handler(ses->tab->term, uri)) {
554 print_error_dialog(ses, S_EXTERNAL_PROTOCOL, uri, PRI_CANCEL);
555 return;
558 if (!init_string(&def)) return;
560 add_to_string(&def, get_opt_str("document.download.directory"));
561 if (def.length && !dir_sep(def.source[def.length - 1]))
562 add_char_to_string(&def, '/');
564 add_mime_filename_to_string(&def, uri);
566 /* Remove the %-ugliness for display */
567 decode_uri_string_for_display(&def);
569 if (interactive) {
570 input_dialog(ses->tab->term, NULL,
571 N_("Download"), N_("Save to file"),
572 data, &file_history,
573 MAX_STR_LEN, def.source, 0, 0, check_nonempty,
574 (void (*)(void *, unsigned char *)) std,
575 (void (*)(void *)) cancel);
576 } else {
577 std(data, def.source);
580 done_string(&def);
583 void
584 free_history_lists(void)
586 free_list(file_history.entries);
587 #ifdef CONFIG_SCRIPTING
588 trigger_event_name("free-history");
589 #endif
593 static void
594 add_cmdline_bool_option(struct string *string, unsigned char *name)
596 if (!get_cmd_opt_bool(name)) return;
597 add_to_string(string, " -");
598 add_to_string(string, name);
601 void
602 open_uri_in_new_window(struct session *ses, struct uri *uri, struct uri *referrer,
603 enum term_env_type env, enum cache_mode cache_mode,
604 enum task_type task)
606 int ring = get_cmd_opt_int("session-ring");
607 struct string parameters;
608 int id;
610 assert(env && ses);
611 if_assert_failed return;
613 id = add_session_info(ses, uri, referrer, cache_mode, task);
614 if (id < 1) return;
616 if (!init_string(&parameters)) return;
618 add_format_to_string(&parameters, "-base-session %d", id);
619 if (ring) add_format_to_string(&parameters, " -session-ring %d", ring);
621 /* No URI means open new (clean) window possibly without connecting to
622 * the current master so add command line options to properly clone the
623 * current master */
624 if (!uri) {
625 /* Adding -touch-files will only lead to problems */
626 add_cmdline_bool_option(&parameters, "localhost");
627 add_cmdline_bool_option(&parameters, "no-home");
628 add_cmdline_bool_option(&parameters, "no-connect");
631 open_new_window(ses->tab->term, program.path, env, parameters.source);
632 done_string(&parameters);
635 /* Open a link in a new xterm. */
636 void
637 send_open_in_new_window(struct terminal *term, const struct open_in_new *open,
638 struct session *ses)
640 struct document_view *doc_view;
641 struct link *link;
642 struct uri *uri;
644 assert(term && open && ses);
645 if_assert_failed return;
646 doc_view = current_frame(ses);
647 assert(doc_view && doc_view->vs && doc_view->document);
648 if_assert_failed return;
650 link = get_current_link(doc_view);
651 if (!link) return;
653 uri = get_link_uri(ses, doc_view, link);
654 if (!uri) return;
656 open_uri_in_new_window(ses, uri, NULL, open->env,
657 CACHE_MODE_NORMAL, TASK_NONE);
658 done_uri(uri);
661 void
662 send_open_new_window(struct terminal *term, const struct open_in_new *open,
663 struct session *ses)
665 open_uri_in_new_window(ses, NULL, NULL, open->env,
666 CACHE_MODE_NORMAL, TASK_NONE);
670 void
671 open_in_new_window(struct terminal *term, void *func_, void *ses_)
673 menu_func_T func = func_;
674 struct session *ses = ses_;
675 struct menu_item *mi;
676 int posibilities;
678 assert(term && ses && func);
679 if_assert_failed return;
681 switch (can_open_in_new(term)) {
682 case 0:
683 return;
685 case 1:
686 mi = NULL;
687 break;
689 default:
690 mi = new_menu(FREE_LIST);
691 if (!mi) return;
694 foreach_open_in_new (posibilities, term->environment) {
695 const struct open_in_new *oi = &open_in_new[posibilities];
697 if (mi == NULL) {
698 func(term, (void *) oi, ses);
699 return;
701 add_to_menu(&mi, oi->text, NULL, ACT_MAIN_NONE, func, (void *) oi, 0);
704 do_menu(term, mi, ses, 1);
708 void
709 add_new_win_to_menu(struct menu_item **mi, unsigned char *text,
710 struct terminal *term)
712 int c = can_open_in_new(term);
714 if (!c) return;
716 /* The URI is saved as session info in the master and not sent to the
717 * instance in the new window so with -no-connect or -no-home enabled
718 * it is not possible to open links URIs. For -anonymous one window
719 * should be enough. */
720 if (get_cmd_opt_bool("no-connect")
721 || get_cmd_opt_bool("no-home")
722 || get_cmd_opt_bool("anonymous"))
723 return;
725 add_to_menu(mi, text, NULL, ACT_MAIN_OPEN_LINK_IN_NEW_WINDOW,
726 open_in_new_window,
727 send_open_in_new_window, c - 1 ? SUBMENU : 0);
731 static void
732 do_pass_uri_to_command(struct terminal *term, void *command_, void *xxx)
734 unsigned char *command = command_;
736 exec_on_terminal(term, command, "", 0);
737 mem_free(command);
740 /* TODO:
741 * - Support for passing MIME type
742 * - Merge this function with rewrite_uri(), subst_cmd(), subst_file()
743 * and subst_url(). */
744 static unsigned char *
745 format_command(unsigned char *format, struct uri *uri)
747 struct string string;
749 if (!init_string(&string)) return NULL;
751 while (*format) {
752 int pos = 0;
754 while (format[pos] && format[pos] != '%') pos++;
756 add_bytes_to_string(&string, format, pos);
757 format += pos;
759 if (*format != '%') continue;
761 format++;
762 switch (*format) {
763 case 'c':
765 unsigned char *str = struri(uri);
766 int length = get_real_uri_length(uri);
768 add_shell_quoted_to_string(&string,
769 str, length);
770 break;
772 case '%':
773 add_char_to_string(&string, '%');
774 break;
775 default:
776 add_bytes_to_string(&string, format - 1, 2);
777 break;
779 if (*format) format++;
782 return string.source;
785 enum frame_event_status
786 pass_uri_to_command(struct session *ses, struct document_view *doc_view,
787 int which_type)
789 struct list_head *tree = get_opt_tree("document.uri_passing");
790 enum pass_uri_type type = which_type;
791 struct menu_item *items;
792 struct option *option;
793 struct uri *uri;
794 int commands = 0;
796 switch (type) {
797 case PASS_URI_FRAME:
798 uri = get_uri_reference(doc_view->document->uri);
799 break;
801 case PASS_URI_LINK:
803 struct link *link = get_current_link(doc_view);
805 if (!link) return FRAME_EVENT_OK;
807 uri = get_link_uri(ses, doc_view, link);
808 if (!uri) return FRAME_EVENT_OK;
809 break;
811 default:
812 case PASS_URI_TAB:
813 uri = get_uri_reference(ses->doc_view->document->uri);
816 items = new_menu(FREE_LIST | FREE_TEXT | FREE_DATA | NO_INTL);
817 if (!items) {
818 done_uri(uri);
819 return FRAME_EVENT_OK;
822 foreach (option, *tree) {
823 unsigned char *text, *data;
825 if (!strcmp(option->name, "_template_"))
826 continue;
828 text = stracpy(option->name);
829 if (!text) continue;
831 data = format_command(option->value.string, uri);
832 if (!data) {
833 mem_free(text);
834 continue;
837 add_to_menu(&items, text, NULL, ACT_MAIN_NONE,
838 do_pass_uri_to_command, data, 0);
839 commands++;
842 done_uri(uri);
844 if (commands > 1) {
845 do_menu(ses->tab->term, items, ses, 1);
846 } else {
847 if (commands == 1)
848 do_pass_uri_to_command(ses->tab->term, items->data, ses);
849 else
850 mem_free(items->data);
851 mem_free(items->text);
852 mem_free(items);
855 return FRAME_EVENT_OK;
858 void
859 add_uri_command_to_menu(struct menu_item **mi, enum pass_uri_type type)
861 struct list_head *tree = get_opt_tree("document.uri_passing");
862 struct option *option;
863 int commands = 0;
864 enum menu_item_flags flags = NO_FLAG;
865 action_id_T action_id;
866 unsigned char *text;
868 switch (type) {
869 case PASS_URI_FRAME:
870 action_id = ACT_MAIN_FRAME_EXTERNAL_COMMAND;
871 text = N_("~Pass frame URI to external command");
872 break;
874 case PASS_URI_LINK:
875 action_id = ACT_MAIN_LINK_EXTERNAL_COMMAND;
876 text = N_("Pass link URI to e~xternal command");
877 break;
879 default:
880 case PASS_URI_TAB:
881 action_id = ACT_MAIN_TAB_EXTERNAL_COMMAND;
882 text = N_("Pass tab URI to e~xternal command");
885 foreach (option, *tree) {
886 if (!strcmp(option->name, "_template_"))
887 continue;
889 commands++;
890 if (commands > 1) {
891 flags = SUBMENU;
892 break;
896 if (commands == 0) return;
898 add_to_menu(mi, text, NULL, action_id, NULL, NULL, flags);
902 /* The file completion menu always has two non selectable menu item at the
903 * start. First is the 'Directory:' or 'Files:' text and then a separator. */
904 #define FILE_COMPLETION_MENU_OFFSET 2
906 static struct menu_item empty_directory_menu[] = {
907 INIT_MENU_ITEM(N_("Empty directory"), NULL, ACT_MAIN_NONE, NULL, NULL, NO_SELECT),
908 NULL_MENU_ITEM
911 /* Builds the file completion menu. If there is only one item it is selected
912 * else the menu is launched. */
913 static void
914 complete_file_menu(struct terminal *term, int no_elevator, void *data,
915 menu_func_T file_func, menu_func_T dir_func,
916 unsigned char *dirname, unsigned char *filename)
918 struct menu_item *menu = new_menu(FREE_LIST | NO_INTL);
919 struct directory_entry *entries, *entry;
920 int filenamelen = strlen(filename);
921 int direntries = 0, fileentries = 0;
923 if (!menu) return;
925 entries = get_directory_entries(dirname, 1);
926 if (!entries) {
927 mem_free(menu);
928 return;
931 for (entry = entries; entry->name; entry++) {
932 unsigned char *text;
933 int is_dir = (*entry->attrib == 'd');
934 int is_file = (*entry->attrib == '-');
936 mem_free(entry->attrib);
937 if ((!is_dir && !is_file) || !file_can_read(entry->name)) {
938 mem_free(entry->name);
939 continue;
942 text = get_filename_position(entry->name);
943 if (strncmp(filename, text, filenamelen)
944 || (no_elevator && !strcmp("..", text))) {
945 mem_free(entry->name);
946 continue;
949 if (is_dir) {
950 if (!direntries) {
951 add_to_menu(&menu, _("Directories:", term), NULL,
952 ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
953 add_menu_separator(&menu);
956 add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
957 dir_func, entry->name, FREE_DATA | SUBMENU);
959 direntries++;
961 } else {
962 if (!fileentries) {
963 if (direntries) add_menu_separator(&menu);
964 add_to_menu(&menu, _("Files:", term), NULL,
965 ACT_MAIN_NONE, NULL, NULL, NO_SELECT);
966 add_menu_separator(&menu);
969 add_to_menu(&menu, text, NULL, ACT_MAIN_NONE,
970 file_func, entry->name, FREE_DATA);
972 fileentries++;
977 mem_free(entries);
978 if (direntries == 0 && fileentries == 0) {
979 mem_free(menu);
980 return;
983 /* Only one entry */
984 if (direntries + fileentries == 1) {
985 unsigned char *text = menu[FILE_COMPLETION_MENU_OFFSET].data;
987 mem_free(menu);
989 if (fileentries) {
990 /* Complete what is already there */
991 file_func(term, text, data);
992 return;
995 /* For single directory entries open the lonely subdir if it is
996 * not the parent elevator. */
997 if (strcmp(&text[strlen(dirname)], "..")) {
998 dir_func(term, text, data);
999 } else {
1000 do_menu(term, empty_directory_menu, NULL, 0); \
1003 mem_free(text);
1005 } else {
1006 /* Start with the first directory or file entry selected */
1007 do_menu(term, menu, data, 0);
1011 /* Prepares the launching of the file completion menu by expanding the @path
1012 * and splitting it in directory and file name part. */
1013 void
1014 auto_complete_file(struct terminal *term, int no_elevator, unsigned char *path,
1015 menu_func_T file_func, menu_func_T dir_func, void *data)
1017 struct uri *uri;
1018 unsigned char *dirname;
1019 unsigned char *filename;
1021 assert(term && data && file_func && dir_func && data);
1023 if (get_cmd_opt_bool("anonymous"))
1024 return;
1026 if (!*path) path = "./";
1028 /* Use the URI translation to handle ./ and ../ and ~/ expansion */
1029 uri = get_translated_uri(path, term->cwd);
1030 if (!uri) return;
1032 if (uri->protocol != PROTOCOL_FILE) {
1033 path = NULL;
1034 } else {
1035 path = get_uri_string(uri, URI_PATH);
1038 done_uri(uri);
1039 if (!path) return;
1041 filename = get_filename_position(path);
1043 if (*filename && file_is_dir(path)) {
1044 filename = path + strlen(path);
1046 } else if (*filename && file_exists(path)) {
1047 /* Complete any tilde expansion */
1048 file_func(term, path, data);
1049 return;
1052 /* Split the path into @dirname and @filename */
1053 dirname = path;
1054 path = filename;
1055 filename = stracpy(path);
1056 *path = 0;
1058 /* Make sure the dirname has an ending slash */
1059 if (!dir_sep(path[-1])) {
1060 unsigned char separator = *dirname;
1061 int dirnamelen = path - dirname;
1063 insert_in_string(&dirname, dirnamelen, &separator, 1);
1066 complete_file_menu(term, no_elevator, data,
1067 file_func, dir_func, dirname, filename);
1069 mem_free(dirname);
1070 mem_free(filename);