download: Add DOWNLOAD_EXTERNAL flag
[elinks.git] / src / viewer / text / view.c
blob13a61f50e9d8816bff4a32bfa8068dd13827319a
1 /** HTML viewer (and much more)
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <stdlib.h>
9 #include <string.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
14 #include "elinks.h"
16 #include "bfu/leds.h"
17 #include "bfu/menu.h"
18 #include "bfu/dialog.h"
19 #include "config/kbdbind.h"
20 #include "config/options.h"
21 #include "dialogs/document.h"
22 #include "dialogs/menu.h"
23 #include "dialogs/options.h"
24 #include "dialogs/status.h"
25 #include "document/document.h"
26 #include "document/html/frames.h"
27 #include "document/options.h"
28 #include "document/renderer.h"
29 #include "document/view.h"
30 #include "intl/charsets.h"
31 #include "intl/gettext/libintl.h"
32 #include "main/event.h"
33 #include "osdep/osdep.h"
34 #include "protocol/uri.h"
35 #include "session/download.h"
36 #include "session/location.h"
37 #include "session/session.h"
38 #include "session/task.h"
39 #include "terminal/draw.h"
40 #include "terminal/event.h"
41 #include "terminal/kbd.h"
42 #include "terminal/mouse.h"
43 #include "terminal/tab.h"
44 #include "terminal/terminal.h"
45 #include "terminal/window.h"
46 #include "util/color.h"
47 #include "util/conv.h"
48 #include "util/error.h"
49 #include "util/memory.h"
50 #include "util/snprintf.h"
51 #include "util/string.h"
52 #include "viewer/action.h"
53 #include "viewer/dump/dump.h"
54 #include "viewer/text/draw.h"
55 #include "viewer/text/form.h"
56 #include "viewer/text/link.h"
57 #include "viewer/text/marks.h"
58 #include "viewer/text/search.h"
59 #include "viewer/text/textarea.h"
60 #include "viewer/text/view.h"
61 #include "viewer/text/vs.h"
63 void
64 detach_formatted(struct document_view *doc_view)
66 assert(doc_view);
67 if_assert_failed return;
69 #ifdef CONFIG_ECMASCRIPT
70 if (doc_view->session) {
71 mem_free_set(&doc_view->session->status.window_status, NULL);
73 #endif
74 if (doc_view->document) {
75 release_document(doc_view->document);
76 doc_view->document = NULL;
78 if (doc_view->vs) {
79 doc_view->vs->doc_view = NULL;
80 doc_view->vs = NULL;
82 mem_free_set(&doc_view->name, NULL);
85 /*! @a type == 0 -> PAGE_DOWN;
86 * @a type == 1 -> DOWN */
87 static void
88 move_down(struct session *ses, struct document_view *doc_view, int type)
90 int newpos;
92 assert(ses && doc_view && doc_view->vs);
93 if_assert_failed return;
95 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
97 newpos = doc_view->vs->y + doc_view->box.height;
98 if (newpos < doc_view->document->height)
99 doc_view->vs->y = newpos;
101 if (current_link_is_visible(doc_view))
102 return;
104 if (type)
105 find_link_down(doc_view);
106 else
107 find_link_page_down(doc_view);
109 return;
112 enum frame_event_status
113 move_page_down(struct session *ses, struct document_view *doc_view)
115 int oldy = doc_view->vs->y;
116 int count = eat_kbd_repeat_count(ses);
118 ses->navigate_mode = NAVIGATE_LINKWISE;
120 do move_down(ses, doc_view, 0); while (--count > 0);
122 return doc_view->vs->y == oldy ? FRAME_EVENT_OK : FRAME_EVENT_REFRESH;
125 /*! @a type == 0 -> PAGE_UP;
126 * @a type == 1 -> UP */
127 static void
128 move_up(struct session *ses, struct document_view *doc_view, int type)
130 assert(ses && doc_view && doc_view->vs);
131 if_assert_failed return;
133 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
135 if (doc_view->vs->y == 0) return;
137 doc_view->vs->y -= doc_view->box.height;
138 int_lower_bound(&doc_view->vs->y, 0);
140 if (current_link_is_visible(doc_view))
141 return;
143 if (type)
144 find_link_up(doc_view);
145 else
146 find_link_page_up(doc_view);
148 return;
151 enum frame_event_status
152 move_page_up(struct session *ses, struct document_view *doc_view)
154 int oldy = doc_view->vs->y;
155 int count = eat_kbd_repeat_count(ses);
157 ses->navigate_mode = NAVIGATE_LINKWISE;
159 do move_up(ses, doc_view, 0); while (--count > 0);
161 return doc_view->vs->y == oldy ? FRAME_EVENT_OK : FRAME_EVENT_REFRESH;
165 enum frame_event_status
166 move_link(struct session *ses, struct document_view *doc_view, int direction,
167 int wraparound_bound, int wraparound_link)
169 int wraparound = 0;
170 int count;
172 assert(ses && doc_view && doc_view->vs && doc_view->document);
173 if_assert_failed return FRAME_EVENT_OK;
175 ses->navigate_mode = NAVIGATE_LINKWISE;
177 if (doc_view->document->nlinks < 2) {
178 /* Wraparound only makes sense with more than one link. */
179 wraparound_bound = -1;
180 } else {
181 /* We only bother this option if there's some links
182 * in document. */
183 wraparound = get_opt_bool("document.browse.links.wraparound");
186 count = eat_kbd_repeat_count(ses);
188 do {
189 int current_link = doc_view->vs->current_link;
191 if (current_link == wraparound_bound) {
192 if (wraparound) {
193 jump_to_link_number(ses, doc_view, wraparound_link);
194 /* FIXME: This needs further work, we should call
195 * page_down() and set_textarea() under some conditions
196 * as well. --pasky */
197 continue;
200 } else {
201 if (next_link_in_view_y(doc_view, current_link + direction,
202 direction))
203 continue;
206 /* This is a work around for the case where the index of
207 * @wraparound_bound is not necessarily the index of the first
208 * or the last link in the view. It means that the link moving
209 * could end up calling next_link_in_view() in the condition
210 * above. This is bad because next_link_in_view() will then
211 * 'reset' doc_view->vs->current_link to -1 and the effect will
212 * be that the current link will 'wrap around'. By restoring
213 * the index of the @current_link nothing will be wrapped
214 * around and move_{up,down} will take care of finding the next
215 * link. */
216 doc_view->vs->current_link = current_link;
218 if (direction > 0) {
219 move_down(ses, doc_view, 1);
220 } else {
221 move_up(ses, doc_view, 1);
224 if (current_link != wraparound_bound
225 && current_link != doc_view->vs->current_link) {
226 set_textarea(doc_view, -direction);
228 } while (--count > 0);
230 return FRAME_EVENT_REFRESH;
233 enum frame_event_status
234 move_link_dir(struct session *ses, struct document_view *doc_view, int dir_x, int dir_y)
236 int count;
238 assert(ses && doc_view && doc_view->vs && doc_view->document);
239 if_assert_failed return FRAME_EVENT_OK;
241 ses->navigate_mode = NAVIGATE_LINKWISE;
243 count = eat_kbd_repeat_count(ses);
245 do {
246 int current_link = doc_view->vs->current_link;
248 if (next_link_in_dir(doc_view, dir_x, dir_y))
249 continue;
251 /* FIXME: This won't preserve the column! */
252 if (dir_y > 0)
253 move_down(ses, doc_view, 1);
254 else if (dir_y < 0)
255 move_up(ses, doc_view, 1);
257 if (dir_y && current_link != doc_view->vs->current_link) {
258 set_textarea(doc_view, -dir_y);
260 } while (--count > 0);
262 return FRAME_EVENT_REFRESH;
265 /*! @a steps > 0 -> down */
266 static enum frame_event_status
267 vertical_scroll(struct session *ses, struct document_view *doc_view, int steps)
269 int y;
271 assert(ses && doc_view && doc_view->vs && doc_view->document);
272 if_assert_failed return FRAME_EVENT_OK;
274 y = doc_view->vs->y + steps;
275 if (steps > 0) {
276 /* DOWN */
277 int max_height = doc_view->document->height - doc_view->box.height;
279 if (doc_view->vs->y >= max_height) return FRAME_EVENT_OK;
280 int_upper_bound(&y, max_height);
283 int_lower_bound(&y, 0);
285 if (doc_view->vs->y == y) return FRAME_EVENT_OK;
287 doc_view->vs->y = y;
289 if (current_link_is_visible(doc_view))
290 return FRAME_EVENT_REFRESH;
292 if (steps > 0)
293 find_link_page_down(doc_view);
294 else
295 find_link_page_up(doc_view);
297 return FRAME_EVENT_REFRESH;
300 /*! @a steps > 0 -> right */
301 static enum frame_event_status
302 horizontal_scroll(struct session *ses, struct document_view *doc_view, int steps)
304 int x, max;
306 assert(ses && doc_view && doc_view->vs && doc_view->document);
307 if_assert_failed return FRAME_EVENT_OK;
309 x = doc_view->vs->x + steps;
311 if (get_opt_bool("document.browse.scrolling.horizontal_extended")) {
312 max = doc_view->document->width - 1;
313 } else {
314 max = int_max(doc_view->vs->x,
315 doc_view->document->width - doc_view->box.width);
318 int_bounds(&x, 0, max);
319 if (doc_view->vs->x == x) return FRAME_EVENT_OK;
321 doc_view->vs->x = x;
323 if (current_link_is_visible(doc_view))
324 return FRAME_EVENT_REFRESH;
326 find_link_page_down(doc_view);
328 return FRAME_EVENT_REFRESH;
331 enum frame_event_status
332 scroll_up(struct session *ses, struct document_view *doc_view)
334 int steps = eat_kbd_repeat_count(ses);
336 if (!steps)
337 steps = get_opt_int("document.browse.scrolling.vertical_step");
339 return vertical_scroll(ses, doc_view, -steps);
342 enum frame_event_status
343 scroll_down(struct session *ses, struct document_view *doc_view)
345 int steps = eat_kbd_repeat_count(ses);
347 if (!steps)
348 steps = get_opt_int("document.browse.scrolling.vertical_step");
350 return vertical_scroll(ses, doc_view, steps);
353 enum frame_event_status
354 scroll_left(struct session *ses, struct document_view *doc_view)
356 int steps = eat_kbd_repeat_count(ses);
358 if (!steps)
359 steps = get_opt_int("document.browse.scrolling.horizontal_step");
361 return horizontal_scroll(ses, doc_view, -steps);
364 enum frame_event_status
365 scroll_right(struct session *ses, struct document_view *doc_view)
367 int steps = eat_kbd_repeat_count(ses);
369 if (!steps)
370 steps = get_opt_int("document.browse.scrolling.horizontal_step");
372 return horizontal_scroll(ses, doc_view, steps);
375 #ifdef CONFIG_MOUSE
376 static enum frame_event_status
377 scroll_mouse_up(struct session *ses, struct document_view *doc_view)
379 int steps = get_opt_int("document.browse.scrolling.vertical_step");
381 return vertical_scroll(ses, doc_view, -steps);
384 static enum frame_event_status
385 scroll_mouse_down(struct session *ses, struct document_view *doc_view)
387 int steps = get_opt_int("document.browse.scrolling.vertical_step");
389 return vertical_scroll(ses, doc_view, steps);
392 static enum frame_event_status
393 scroll_mouse_left(struct session *ses, struct document_view *doc_view)
395 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
397 return horizontal_scroll(ses, doc_view, -steps);
400 static enum frame_event_status
401 scroll_mouse_right(struct session *ses, struct document_view *doc_view)
403 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
405 return horizontal_scroll(ses, doc_view, steps);
407 #endif /* CONFIG_MOUSE */
409 enum frame_event_status
410 move_document_start(struct session *ses, struct document_view *doc_view)
412 assert(ses && doc_view && doc_view->vs);
413 if_assert_failed return FRAME_EVENT_OK;
415 doc_view->vs->y = doc_view->vs->x = 0;
417 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
418 /* Move to the first line and the first column. */
419 move_cursor(ses, doc_view, doc_view->box.x, doc_view->box.y);
420 } else {
421 find_link_page_down(doc_view);
424 return FRAME_EVENT_REFRESH;
427 enum frame_event_status
428 move_document_end(struct session *ses, struct document_view *doc_view)
430 int max_height;
432 assert(ses && doc_view && doc_view->vs && doc_view->document);
433 if_assert_failed return FRAME_EVENT_OK;
435 max_height = doc_view->document->height - doc_view->box.height;
436 doc_view->vs->x = 0;
437 int_lower_bound(&doc_view->vs->y, int_max(0, max_height));
439 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
440 /* Move to the last line of the document,
441 * but preserve the column. This is done to avoid
442 * moving the cursor backwards if it is already
443 * on the last line but is not on the first column. */
444 move_cursor(ses, doc_view, ses->tab->x,
445 doc_view->document->height - doc_view->vs->y);
446 } else {
447 find_link_page_up(doc_view);
450 return FRAME_EVENT_REFRESH;
453 enum frame_event_status
454 set_frame(struct session *ses, struct document_view *doc_view, int xxxx)
456 assert(ses && ses->doc_view && doc_view && doc_view->vs);
457 if_assert_failed return FRAME_EVENT_OK;
459 if (doc_view == ses->doc_view) return FRAME_EVENT_OK;
460 goto_uri(ses, doc_view->vs->uri);
461 ses->navigate_mode = NAVIGATE_LINKWISE;
463 return FRAME_EVENT_OK;
467 void
468 toggle_plain_html(struct session *ses, struct document_view *doc_view, int xxxx)
470 assert(ses && doc_view && ses->tab && ses->tab->term);
471 if_assert_failed return;
473 if (!doc_view->vs) {
474 nowhere_box(ses->tab->term, NULL);
475 return;
478 doc_view->vs->plain = !doc_view->vs->plain;
479 draw_formatted(ses, 1);
482 void
483 toggle_wrap_text(struct session *ses, struct document_view *doc_view, int xxxx)
485 assert(ses && doc_view && ses->tab && ses->tab->term);
486 if_assert_failed return;
488 if (!doc_view->vs) {
489 nowhere_box(ses->tab->term, NULL);
490 return;
493 doc_view->vs->wrap = !doc_view->vs->wrap;
494 draw_formatted(ses, 1);
497 /** Move the cursor to the document view co-ordinates provided
498 * as @a x and @a y, scroll the document if necessary, put us in
499 * cursor-routing navigation mode if that is not the current mode,
500 * and select any link under the cursor. */
501 enum frame_event_status
502 move_cursor(struct session *ses, struct document_view *doc_view, int x, int y)
504 enum frame_event_status status = FRAME_EVENT_REFRESH;
505 struct terminal *term = ses->tab->term;
506 struct box *box = &doc_view->box;
507 struct link *link;
509 /* If cursor was moved outside the document view scroll it, but only
510 * within the document canvas */
511 if (!is_in_box(box, x, y)) {
512 int max_height = doc_view->document->height - doc_view->vs->y;
513 int max_width = doc_view->document->width - doc_view->vs->x;
515 if (y < box->y) {
516 status = vertical_scroll(ses, doc_view, y - box->y);
518 } else if (y >= box->y + box->height && y <= max_height) {
519 status = vertical_scroll(ses, doc_view,
520 y - (box->y + box->height - 1));
522 } else if (x < box->x) {
523 status = horizontal_scroll(ses, doc_view, x - box->x);
525 } else if (x >= box->x + box->width && x <= max_width) {
526 status = horizontal_scroll(ses, doc_view,
527 x - (box->x + box->width - 1));
530 /* If the view was not scrolled there's nothing more to do */
531 if (status != FRAME_EVENT_REFRESH)
532 return status;
534 /* Restrict the cursor position within the current view */
535 int_bounds(&x, box->x, box->x + box->width - 1);
536 int_bounds(&y, box->y, box->y + box->height - 1);
539 /* Scrolling could have changed the navigation mode */
540 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
542 link = get_link_at_coordinates(doc_view, x - box->x, y - box->y);
543 if (link) {
544 doc_view->vs->current_link = link - doc_view->document->links;
545 } else {
546 doc_view->vs->current_link = -1;
549 /* Set the unblockable cursor position and update the window pointer so
550 * stuff like the link menu will be drawn relative to the cursor. */
551 set_cursor(term, x, y, 0);
552 set_window_ptr(ses->tab, x, y);
554 return status;
557 static enum frame_event_status
558 move_cursor_rel_count(struct session *ses, struct document_view *view,
559 int rx, int ry, int count)
561 int x, y;
563 x = ses->tab->x + rx*count;
564 y = ses->tab->y + ry*count;
565 return move_cursor(ses, view, x, y);
568 static enum frame_event_status
569 move_cursor_rel(struct session *ses, struct document_view *view,
570 int rx, int ry)
572 int count = eat_kbd_repeat_count(ses);
574 int_lower_bound(&count, 1);
576 return move_cursor_rel_count(ses, view, rx, ry, count);
579 enum frame_event_status
580 move_cursor_left(struct session *ses, struct document_view *view)
582 return move_cursor_rel(ses, view, -1, 0);
585 enum frame_event_status
586 move_cursor_right(struct session *ses, struct document_view *view)
588 return move_cursor_rel(ses, view, 1, 0);
591 enum frame_event_status
592 move_cursor_up(struct session *ses, struct document_view *view)
594 return move_cursor_rel(ses, view, 0, -1);
597 enum frame_event_status
598 move_cursor_down(struct session *ses, struct document_view *view)
600 return move_cursor_rel(ses, view, 0, 1);
603 enum frame_event_status
604 move_link_up_line(struct session *ses, struct document_view *doc_view)
606 struct document *document;
607 struct view_state *vs;
608 struct box *box;
609 int min_y, y, y1;
611 assert(ses && doc_view && doc_view->vs && doc_view->document);
612 if_assert_failed return FRAME_EVENT_OK;
613 vs = doc_view->vs;
614 document = doc_view->document;
615 box = &doc_view->box;
616 if (!document->lines1) {
617 if (vs->y) {
618 vs->y -= box->height;
619 int_lower_bound(&vs->y, 0);
620 return FRAME_EVENT_REFRESH;
622 return FRAME_EVENT_OK;
624 min_y = vs->y - box->height;
625 int_lower_bound(&min_y, 0);
626 y1 = y = vs->y + ses->tab->y - box->y;
627 int_upper_bound(&y, document->height - 1);
628 for (y--; y >= min_y; y--) {
629 struct link *link = document->lines1[y];
631 if (!link) continue;
632 for (; link <= document->lines2[y]; link++) {
633 enum frame_event_status status;
635 if (link->points[0].y != y) continue;
636 if (y < vs->y) {
637 /* The line is above the visible part
638 * of the document. Scroll it by one
639 * page, but not at all past the
640 * beginning of the document. */
641 int mini = int_min(box->height, vs->y);
643 /* Before this update, y is the line
644 * number in the document, and y - y1
645 * is the number of lines the cursor
646 * must move in the document.
647 * Afterwards, y does not make sense,
648 * but y - y1 is the number of lines
649 * the cursor must move on the screen. */
650 vs->y -= mini;
651 y += mini;
653 status = move_cursor_rel_count(ses, doc_view, 0, y - y1, 1);
654 if (link == get_current_link(doc_view))
655 ses->navigate_mode = NAVIGATE_LINKWISE;
656 return status;
659 if (vs->y) {
660 vs->y -= box->height;
661 int_lower_bound(&vs->y, 0);
662 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
663 return FRAME_EVENT_REFRESH;
665 return FRAME_EVENT_OK;
668 enum frame_event_status
669 move_link_down_line(struct session *ses, struct document_view *doc_view)
671 struct document *document;
672 struct view_state *vs;
673 struct box *box;
674 int max_y, y, y1;
676 assert(ses && doc_view && doc_view->vs && doc_view->document);
677 if_assert_failed return FRAME_EVENT_OK;
678 vs = doc_view->vs;
679 document = doc_view->document;
680 box = &doc_view->box;
681 if (!document->lines1) {
682 if (vs->y + box->height < document->height) {
683 vs->y += box->height;
684 return FRAME_EVENT_REFRESH;
686 return FRAME_EVENT_OK;
688 max_y = vs->y + box->height * 2 - 1;
689 int_upper_bound(&max_y, document->height - 1);
690 y1 = y = vs->y + ses->tab->y - box->y;
691 for (y++; y <= max_y; y++) {
692 struct link *link = document->lines1[y];
694 if (!link) continue;
695 for (; link <= document->lines2[y]; link++) {
696 enum frame_event_status status;
698 if (link->points[0].y != y) continue;
699 if (y >= vs->y + box->height) {
700 /* The line is below the visible part
701 * of the document. Scroll it by one
702 * page, but keep at least one line of
703 * the document on the screen. */
704 int mini = int_min(box->height, document->height - vs->y - 1);
706 /* Before this update, y is the line
707 * number in the document, and y - y1
708 * is the number of lines the cursor
709 * must move in the document.
710 * Afterwards, y does not make sense,
711 * but y - y1 is the number of lines
712 * the cursor must move on the screen. */
713 vs->y += mini;
714 y -= mini;
716 status = move_cursor_rel_count(ses, doc_view, 0, y - y1, 1);
717 if (link == get_current_link(doc_view))
718 ses->navigate_mode = NAVIGATE_LINKWISE;
719 return status;
722 if (vs->y + box->height < document->height) {
723 vs->y += box->height;
724 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
725 return FRAME_EVENT_REFRESH;
727 return FRAME_EVENT_OK;
730 enum frame_event_status
731 move_link_prev_line(struct session *ses, struct document_view *doc_view)
733 struct view_state *vs;
734 struct document *document;
735 struct box *box;
736 struct link *link, *last = NULL;
737 int y1, y, min_y, min_x, max_x, x1;
739 assert(ses && doc_view && doc_view->vs && doc_view->document);
740 if_assert_failed return FRAME_EVENT_OK;
742 vs = doc_view->vs;
743 document = doc_view->document;
744 box = &doc_view->box;
745 if (!document->lines1) {
746 if (vs->y) {
747 vs->y -= box->height;
748 int_lower_bound(&vs->y, 0);
749 return FRAME_EVENT_REFRESH;
751 return FRAME_EVENT_OK;
753 y = y1 = vs->y + ses->tab->y - box->y;
754 x1 = vs->x + ses->tab->x - box->x;
756 link = get_current_link(doc_view);
757 if (link) {
758 get_link_x_bounds(link, y1, &min_x, &max_x);
759 } else {
760 min_x = max_x = x1;
762 int_upper_bound(&y, document->height - 1);
763 min_y = int_max(0, vs->y - box->height);
765 for (; y >= min_y; y--, min_x = INT_MAX) {
766 link = document->lines1[y];
767 if (!link) continue;
768 for (; link <= document->lines2[y]; link++) {
769 if (link->points[0].y != y) continue;
770 if (link->points[0].x >= min_x) continue;
771 if (!last) last = link;
772 else if (link->points[0].x > last->points[0].x) last = link;
774 if (last) {
775 enum frame_event_status status;
777 y = last->points[0].y;
778 if (y < vs->y) {
779 /* The line is above the visible part
780 * of the document. Scroll it by one
781 * page, but not at all past the
782 * beginning of the document. */
783 int mini = int_min(box->height, vs->y);
785 /* Before this update, y is the line
786 * number in the document, and y - y1
787 * is the number of lines the cursor
788 * must move in the document.
789 * Afterwards, y does not make sense,
790 * but y - y1 is the number of lines
791 * the cursor must move on the screen. */
792 vs->y -= mini;
793 y += mini;
795 status = move_cursor_rel_count(ses, doc_view, last->points[0].x - x1, y - y1, 1);
796 if (last == get_current_link(doc_view))
797 ses->navigate_mode = NAVIGATE_LINKWISE;
798 return status;
801 if (vs->y) {
802 vs->y -= box->height;
803 int_lower_bound(&vs->y, 0);
804 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
805 return FRAME_EVENT_REFRESH;
807 return FRAME_EVENT_OK;
810 enum frame_event_status
811 move_link_next_line(struct session *ses, struct document_view *doc_view)
813 struct view_state *vs;
814 struct document *document;
815 struct box *box;
816 struct link *link, *last = NULL;
817 int y1, y, max_y, min_x, max_x, x1;
819 assert(ses && doc_view && doc_view->vs && doc_view->document);
820 if_assert_failed return FRAME_EVENT_OK;
822 vs = doc_view->vs;
823 document = doc_view->document;
824 box = &doc_view->box;
825 if (!document->lines1) {
826 if (vs->y + box->height < document->height) {
827 vs->y += box->height;
828 return FRAME_EVENT_REFRESH;
830 return FRAME_EVENT_OK;
832 y = y1 = vs->y + ses->tab->y - box->y;
833 x1 = vs->x + ses->tab->x - box->x;
835 link = get_current_link(doc_view);
836 if (link) {
837 get_link_x_bounds(link, y1, &min_x, &max_x);
838 } else {
839 min_x = max_x = x1;
841 int_upper_bound(&y, document->height - 1);
842 max_y = int_min(vs->y + 2 * box->height - 1, document->height - 1);
844 for (; y <= max_y; y++, min_x = -1) {
845 link = document->lines1[y];
846 if (!link) continue;
847 for (; link <= document->lines2[y]; link++) {
848 if (link->points[0].y != y) continue;
849 if (link->points[0].x <= min_x) continue;
850 if (!last) last = link;
851 else if (link->points[0].x < last->points[0].x) last = link;
853 if (last) {
854 enum frame_event_status status;
856 y = last->points[0].y;
857 if (y >= vs->y + box->height) {
858 /* The line is below the visible part
859 * of the document. Scroll it by one
860 * page, but keep at least one line of
861 * the document on the screen. */
862 int mini = int_min(box->height, document->height - vs->y - 1);
864 /* Before this update, y is the line
865 * number in the document, and y - y1
866 * is the number of lines the cursor
867 * must move in the document.
868 * Afterwards, y does not make sense,
869 * but y - y1 is the number of lines
870 * the cursor must move on the screen. */
871 vs->y += mini;
872 y -= mini;
874 status = move_cursor_rel_count(ses, doc_view, last->points[0].x - x1, y - y1, 1);
875 if (last == get_current_link(doc_view))
876 ses->navigate_mode = NAVIGATE_LINKWISE;
877 return status;
880 if (vs->y + box->height < document->height) {
881 vs->y += box->height;
882 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
883 return FRAME_EVENT_REFRESH;
885 return FRAME_EVENT_OK;
888 enum frame_event_status
889 move_cursor_line_start(struct session *ses, struct document_view *doc_view)
891 struct view_state *vs;
892 struct box *box;
893 int x;
895 assert(ses && doc_view && doc_view->vs);
896 if_assert_failed return FRAME_EVENT_OK;
898 vs = doc_view->vs;
899 box = &doc_view->box;
900 x = vs->x + ses->tab->x - box->x;
901 return move_cursor_rel(ses, doc_view, -x, 0);
904 enum frame_event_status
905 copy_current_link_to_clipboard(struct session *ses,
906 struct document_view *doc_view,
907 int xxx)
909 struct link *link;
910 struct uri *uri;
911 unsigned char *uristring;
913 link = get_current_link(doc_view);
914 if (!link) return FRAME_EVENT_OK;
916 uri = get_link_uri(ses, doc_view, link);
917 if (!uri) return FRAME_EVENT_OK;
919 uristring = get_uri_string(uri, URI_ORIGINAL);
920 done_uri(uri);
922 if (uristring) {
923 set_clipboard_text(uristring);
924 mem_free(uristring);
927 return FRAME_EVENT_OK;
932 try_jump_to_link_number(struct session *ses, struct document_view *doc_view)
934 int link_number = eat_kbd_repeat_count(ses) - 1;
936 if (link_number < 0) return 1;
938 if (!doc_view) return 0;
940 if (link_number >= doc_view->document->nlinks)
941 return 0;
943 jump_to_link_number(ses, doc_view, link_number);
944 refresh_view(ses, doc_view, 0);
946 return 1;
949 #ifdef CONFIG_MARKS
950 enum frame_event_status
951 try_mark_key(struct session *ses, struct document_view *doc_view,
952 struct term_event *ev)
954 term_event_key_T key = get_kbd_key(ev);
955 unsigned char mark;
957 /* set_mark and goto_mark allow only a subset of the ASCII
958 * character repertoire as mark characters. If get_kbd_key(ev)
959 * is something else (i.e. a special key or a non-ASCII
960 * character), map it to an ASCII character that the functions
961 * will not accept, so the results are consistent.
962 * When CONFIG_UTF8 is not defined, this assumes that codes
963 * 0 to 0x7F in all codepages match ASCII. */
964 if (key >= 0 && key <= 0x7F)
965 mark = (unsigned char) key;
966 else
967 mark = 0;
969 switch (ses->kbdprefix.mark) {
970 case KP_MARK_NOTHING:
971 return FRAME_EVENT_IGNORED;
973 case KP_MARK_SET:
974 /* It is intentional to set the mark
975 * to NULL if !doc_view->vs. */
976 set_mark(mark, doc_view->vs);
977 break;
979 case KP_MARK_GOTO:
980 goto_mark(mark, doc_view->vs);
981 break;
984 ses->kbdprefix.repeat_count = 0;
985 ses->kbdprefix.mark = KP_MARK_NOTHING;
987 return FRAME_EVENT_REFRESH;
989 #endif
991 static enum frame_event_status
992 try_prefix_key(struct session *ses, struct document_view *doc_view,
993 struct term_event *ev)
995 struct document *document = doc_view->document;
996 struct document_options *doc_opts = &document->options;
997 int digit = get_kbd_key(ev) - '0';
999 if (digit < 0 || digit > 9)
1000 return FRAME_EVENT_IGNORED;
1002 if (get_kbd_modifier(ev)
1003 || ses->kbdprefix.repeat_count /* The user has already begun
1004 * entering a prefix. */
1005 || !doc_opts->num_links_key
1006 || (doc_opts->num_links_key == 1 && !doc_opts->links_numbering)) {
1007 /* Repeat count.
1008 * ses->kbdprefix.repeat_count is initialized to zero
1009 * the first time by init_session() calloc() call.
1010 * When used, it has to be reset to zero. */
1012 /* Clear the highlighting for the previous partial prefix. */
1013 if (ses->kbdprefix.repeat_count) draw_formatted(ses, 0);
1015 ses->kbdprefix.repeat_count *= 10;
1016 ses->kbdprefix.repeat_count += digit;
1018 /* If too big, just restart from zero, so pressing
1019 * '0' six times or more will reset the count. */
1020 if (ses->kbdprefix.repeat_count > 99999)
1021 ses->kbdprefix.repeat_count = 0;
1022 else if (ses->kbdprefix.repeat_count)
1023 highlight_links_with_prefixes_that_start_with_n(
1024 ses->tab->term, doc_view,
1025 ses->kbdprefix.repeat_count);
1027 return FRAME_EVENT_OK;
1030 if (digit >= 1 && !get_kbd_modifier(ev)) {
1031 int nlinks = document->nlinks, length;
1032 unsigned char d[2] = { get_kbd_key(ev), 0 };
1034 ses->kbdprefix.repeat_count = 0;
1036 if (!nlinks) return FRAME_EVENT_OK;
1038 for (length = 1; nlinks; nlinks /= 10)
1039 length++;
1041 input_dialog(ses->tab->term, NULL,
1042 N_("Go to link"), N_("Enter link number"),
1043 ses, NULL,
1044 length, d, 1, document->nlinks, check_number,
1045 (void (*)(void *, unsigned char *)) goto_link_number, NULL);
1047 return FRAME_EVENT_OK;
1050 return FRAME_EVENT_IGNORED;
1053 static enum frame_event_status
1054 try_form_insert_mode(struct session *ses, struct document_view *doc_view,
1055 struct link *link, struct term_event *ev)
1057 enum frame_event_status status = FRAME_EVENT_IGNORED;
1058 enum edit_action action_id;
1060 if (!link_is_textinput(link))
1061 return FRAME_EVENT_IGNORED;
1063 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
1065 if (ses->insert_mode == INSERT_MODE_OFF) {
1066 if (action_id == ACT_EDIT_ENTER) {
1067 ses->insert_mode = INSERT_MODE_ON;
1068 status = FRAME_EVENT_REFRESH;
1072 return status;
1075 static enum frame_event_status
1076 try_form_action(struct session *ses, struct document_view *doc_view,
1077 struct link *link, struct term_event *ev)
1079 enum frame_event_status status;
1081 assert(link);
1083 if (!link_is_textinput(link))
1084 return FRAME_EVENT_IGNORED;
1086 status = field_op(ses, doc_view, link, ev);
1088 if (status != FRAME_EVENT_IGNORED
1089 && ses->insert_mode == INSERT_MODE_ON) {
1090 assert(link == get_current_link(doc_view));
1093 return status;
1096 static enum frame_event_status
1097 frame_ev_kbd(struct session *ses, struct document_view *doc_view, struct term_event *ev)
1099 enum frame_event_status status = FRAME_EVENT_IGNORED;
1100 int accesskey_priority;
1101 struct link *link = get_current_link(doc_view);
1103 if (link) {
1104 status = try_form_insert_mode(ses, doc_view, link, ev);
1105 if (status != FRAME_EVENT_IGNORED)
1106 return status;
1108 status = try_form_action(ses, doc_view, link, ev);
1109 if (status != FRAME_EVENT_IGNORED)
1110 return status;
1113 #ifdef CONFIG_MARKS
1114 status = try_mark_key(ses, doc_view, ev);
1115 if (status != FRAME_EVENT_IGNORED)
1116 return status;
1117 #endif
1118 accesskey_priority = get_opt_int("document.browse.accesskey.priority");
1120 if (accesskey_priority >= 2) {
1121 status = try_document_key(ses, doc_view, ev);
1123 if (status != FRAME_EVENT_IGNORED) {
1124 /* The document ate the key! */
1125 return status;
1129 status = try_prefix_key(ses, doc_view, ev);
1130 if (status != FRAME_EVENT_IGNORED)
1131 return status;
1133 if (accesskey_priority == 1) {
1134 status = try_document_key(ses, doc_view, ev);
1136 if (status != FRAME_EVENT_IGNORED) {
1137 /* The document ate the key! */
1138 return status;
1142 return FRAME_EVENT_IGNORED;
1145 #ifdef CONFIG_MOUSE
1146 static enum frame_event_status
1147 frame_ev_mouse(struct session *ses, struct document_view *doc_view, struct term_event *ev)
1149 int x = ev->info.mouse.x;
1150 int y = ev->info.mouse.y;
1151 struct link *link;
1153 if (check_mouse_wheel(ev)) {
1154 if (!check_mouse_action(ev, B_DOWN)) {
1155 /* We handle only B_DOWN case... */
1156 } else if (check_mouse_button(ev, B_WHEEL_UP)) {
1157 return scroll_mouse_up(ses, doc_view);
1158 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
1159 return scroll_mouse_down(ses, doc_view);
1162 return FRAME_EVENT_OK;
1165 link = get_link_at_coordinates(doc_view, x, y);
1166 if (link) {
1167 enum frame_event_status status = FRAME_EVENT_REFRESH;
1169 doc_view->vs->current_link = link - doc_view->document->links;
1170 ses->navigate_mode = NAVIGATE_LINKWISE;
1172 if (!link_is_textinput(link)) {
1174 status = FRAME_EVENT_OK;
1176 refresh_view(ses, doc_view, 0);
1178 if (check_mouse_button(ev, B_LEFT)
1179 || check_mouse_button(ev, B_MIDDLE)) {
1180 if (check_mouse_action(ev, B_DOWN))
1181 do_not_ignore_next_mouse_event(ses->tab->term);
1182 else if (check_mouse_button(ev, B_LEFT))
1183 status = enter(ses, doc_view, 0);
1184 else if (check_mouse_button(ev, B_MIDDLE))
1185 open_current_link_in_new_tab(ses, 1);
1186 } else {
1187 link_menu(ses->tab->term, NULL, ses);
1191 return status;
1194 if (check_mouse_button(ev, B_LEFT)) {
1195 /* Clicking the edge of screen will scroll the document. */
1197 int scrollmargin = get_opt_int("document.browse.scrolling.margin");
1199 /* XXX: This is code duplication with kbd handlers. But
1200 * repeatcount-free here. */
1202 if (y < scrollmargin) {
1203 return scroll_mouse_up(ses, doc_view);
1205 if (y >= doc_view->box.height - scrollmargin) {
1206 return scroll_mouse_down(ses, doc_view);
1209 if (x < scrollmargin * 2) {
1210 return scroll_mouse_left(ses, doc_view);
1212 if (x >= doc_view->box.width - scrollmargin * 2) {
1213 return scroll_mouse_right(ses, doc_view);
1216 return FRAME_EVENT_OK;
1219 return FRAME_EVENT_IGNORED;
1221 #endif /* CONFIG_MOUSE */
1223 static enum frame_event_status
1224 frame_ev(struct session *ses, struct document_view *doc_view, struct term_event *ev)
1226 assertm(doc_view && doc_view->document, "document not formatted");
1227 if_assert_failed return FRAME_EVENT_IGNORED;
1229 assert(ses && ev);
1230 if_assert_failed return FRAME_EVENT_IGNORED;
1232 /* When changing frame, vs may be NULL. See bug 525. */
1233 if (!doc_view->vs) return FRAME_EVENT_IGNORED;
1235 switch (ev->ev) {
1236 case EVENT_KBD:
1237 return frame_ev_kbd(ses, doc_view, ev);
1238 #ifdef CONFIG_MOUSE
1239 case EVENT_MOUSE:
1240 return frame_ev_mouse(ses, doc_view, ev);
1241 #endif /* CONFIG_MOUSE */
1242 default:
1243 return FRAME_EVENT_IGNORED;
1247 struct document_view *
1248 current_frame(struct session *ses)
1250 struct document_view *doc_view = NULL;
1251 int current_frame_number;
1253 assert(ses);
1254 if_assert_failed return NULL;
1256 if (!have_location(ses)) return NULL;
1258 current_frame_number = cur_loc(ses)->vs.current_link;
1259 if (current_frame_number == -1) current_frame_number = 0;
1261 foreach (doc_view, ses->scrn_frames) {
1262 if (document_has_frames(doc_view->document)) continue;
1263 if (!current_frame_number--) return doc_view;
1266 doc_view = ses->doc_view;
1268 assert(doc_view && doc_view->document);
1269 if_assert_failed return NULL;
1271 if (document_has_frames(doc_view->document)) return NULL;
1272 return doc_view;
1275 static enum frame_event_status
1276 send_to_frame(struct session *ses, struct document_view *doc_view,
1277 struct term_event *ev)
1279 enum frame_event_status status;
1281 assert(ses && ses->tab && ses->tab->term && ev);
1282 if_assert_failed return FRAME_EVENT_IGNORED;
1284 status = frame_ev(ses, doc_view, ev);
1286 if (status == FRAME_EVENT_REFRESH)
1287 refresh_view(ses, doc_view, 0);
1288 else
1289 print_screen_status(ses);
1291 return status;
1294 #ifdef CONFIG_MOUSE
1295 static int
1296 do_mouse_event(struct session *ses, struct term_event *ev,
1297 struct document_view *doc_view)
1299 struct term_event evv;
1300 struct document_view *matched = NULL, *first = doc_view;
1302 assert(ses && ev);
1303 if_assert_failed return 0;
1305 if (!doc_view) return 0;
1307 do {
1308 assert(doc_view && doc_view->document);
1309 if_assert_failed return 0;
1311 assertm(doc_view->document->options.box.x == doc_view->box.x
1312 && doc_view->document->options.box.y == doc_view->box.y,
1313 "Jonas' 1.565 -> 1.566 patch sucks");
1314 if_assert_failed return 0;
1316 if (check_mouse_position(ev, &doc_view->box)) {
1317 matched = doc_view;
1318 break;
1321 next_frame(ses, 1);
1322 doc_view = current_frame(ses);
1324 } while (doc_view != first);
1326 if (!matched) return 0;
1328 if (doc_view != first) draw_formatted(ses, 0);
1330 set_mouse_term_event(&evv,
1331 ev->info.mouse.x - doc_view->box.x,
1332 ev->info.mouse.y - doc_view->box.y,
1333 ev->info.mouse.button);
1335 return send_to_frame(ses, doc_view, &evv);
1338 static int
1339 is_mouse_on_tab_bar(struct session *ses, struct term_event_mouse *mouse)
1341 struct terminal *term = ses->tab->term;
1342 int y;
1344 if (ses->status.show_tabs_bar_at_top) y = ses->status.show_title_bar;
1345 else y = term->height - 1 - !!ses->status.show_status_bar;
1347 return mouse->y == y;
1349 /** @returns the session if event cleanup should be done or NULL if no
1350 * cleanup is needed. */
1351 static struct session *
1352 send_mouse_event(struct session *ses, struct document_view *doc_view,
1353 struct term_event *ev)
1355 struct terminal *term = ses->tab->term;
1356 struct term_event_mouse *mouse = &ev->info.mouse;
1358 if (mouse->y == 0
1359 && check_mouse_action(ev, B_DOWN)
1360 && !check_mouse_wheel(ev)) {
1361 struct window *m;
1363 activate_bfu_technology(ses, -1);
1364 m = term->windows.next;
1365 m->handler(m, ev);
1367 return ses;
1370 /* Handle tabs navigation if tabs bar is displayed. */
1371 if (ses->status.show_tabs_bar && is_mouse_on_tab_bar(ses, mouse)) {
1372 int tab_num = get_tab_number_by_xpos(term, mouse->x);
1373 struct window *current_tab = get_current_tab(term);
1375 if (check_mouse_action(ev, B_UP)) {
1376 if (check_mouse_button(ev, B_MIDDLE)
1377 && term->current_tab == tab_num
1378 && mouse->y == term->prev_mouse_event.y) {
1379 if (current_tab->data == ses) ses = NULL;
1381 close_tab(term, current_tab->data);
1384 return ses;
1387 if (check_mouse_button(ev, B_WHEEL_UP)) {
1388 switch_current_tab(ses, -1);
1390 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
1391 switch_current_tab(ses, 1);
1393 } else if (tab_num != -1) {
1394 switch_to_tab(term, tab_num, -1);
1396 if (check_mouse_button(ev, B_MIDDLE)) {
1397 do_not_ignore_next_mouse_event(term);
1398 } else if (check_mouse_button(ev, B_RIGHT)) {
1399 tab_menu(current_tab->data, mouse->x, mouse->y, 1);
1403 return ses;
1406 if (!do_mouse_event(ses, ev, doc_view)
1407 && check_mouse_button(ev, B_RIGHT)) {
1408 tab_menu(ses, mouse->x, mouse->y, 0);
1409 return NULL;
1412 #ifdef CONFIG_LEDS
1413 if (ses->status.show_leds
1414 && mouse->y == term->height - 1
1415 && mouse->x >= term->width - LEDS_COUNT - 3) {
1416 menu_leds_info(term, NULL, NULL);
1417 return NULL;
1419 #endif
1421 return NULL;
1423 #endif /* CONFIG_MOUSE */
1425 static void
1426 try_typeahead(struct session *ses, struct document_view *doc_view,
1427 struct term_event *ev, enum main_action action_id)
1429 switch (get_opt_int("document.browse.search.typeahead")) {
1430 case 0:
1431 return;
1432 case 1:
1433 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_LINK;
1434 break;
1435 case 2:
1436 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_TEXT;
1437 break;
1438 default:
1439 INTERNAL("invalid value for document.browse.search.typeahead");
1442 search_typeahead(ses, doc_view, action_id);
1444 /* Cross your fingers -- I'm just asking
1445 * for an infinite loop! -- Miciah */
1446 term_send_event(ses->tab->term, ev);
1449 /** @returns the session if event cleanup should be done or NULL if no
1450 * cleanup is needed. */
1451 static struct session *
1452 send_kbd_event(struct session *ses, struct document_view *doc_view,
1453 struct term_event *ev)
1455 int event;
1456 enum main_action action_id;
1458 if (doc_view && send_to_frame(ses, doc_view, ev) != FRAME_EVENT_IGNORED)
1459 return NULL;
1461 action_id = kbd_action(KEYMAP_MAIN, ev, &event);
1463 if (action_id == ACT_MAIN_QUIT) {
1464 if (check_kbd_key(ev, KBD_CTRL_C))
1465 quit:
1466 action_id = ACT_MAIN_REALLY_QUIT;
1469 switch (do_action(ses, action_id, 0)) {
1470 case FRAME_EVENT_SESSION_DESTROYED:
1471 return NULL;
1472 case FRAME_EVENT_IGNORED:
1473 break;
1474 case FRAME_EVENT_OK:
1475 case FRAME_EVENT_REFRESH:
1476 return ses;
1479 if (action_id == ACT_MAIN_SCRIPTING_FUNCTION) {
1480 #ifdef CONFIG_SCRIPTING
1481 trigger_event(event, ses);
1482 #endif
1483 return NULL;
1486 if (check_kbd_key(ev, KBD_CTRL_C)) goto quit;
1488 /* Ctrl-Alt-F should not open the File menu like Alt-f does. */
1489 if (check_kbd_modifier(ev, KBD_MOD_ALT)) {
1490 struct window *win;
1492 get_kbd_modifier(ev) &= ~KBD_MOD_ALT;
1493 activate_bfu_technology(ses, -1);
1494 win = ses->tab->term->windows.next;
1495 win->handler(win, ev);
1496 if (ses->tab->term->windows.next == win) {
1497 deselect_mainmenu(win->term, win->data);
1498 print_screen_status(ses);
1500 if (ses->tab != ses->tab->term->windows.next)
1501 return NULL;
1502 get_kbd_modifier(ev) |= KBD_MOD_ALT;
1504 if (doc_view
1505 && get_opt_int("document.browse.accesskey"
1506 ".priority") <= 0
1507 && try_document_key(ses, doc_view, ev)
1508 == FRAME_EVENT_REFRESH) {
1509 /* The document ate the key! */
1510 refresh_view(ses, doc_view, 0);
1512 return NULL;
1515 return ses;
1518 if (!(get_kbd_modifier(ev) & KBD_MOD_CTRL)) {
1519 if (doc_view) try_typeahead(ses, doc_view, ev, action_id);
1522 return NULL;
1525 void
1526 send_event(struct session *ses, struct term_event *ev)
1528 assert(ses && ev);
1529 if_assert_failed return;
1531 if (ev->ev == EVENT_KBD) {
1532 struct document_view *doc_view = current_frame(ses);
1534 ses = send_kbd_event(ses, doc_view, ev);
1536 #ifdef CONFIG_MOUSE
1537 else if (ev->ev == EVENT_MOUSE) {
1538 struct document_view *doc_view = current_frame(ses);
1540 ses = send_mouse_event(ses, doc_view, ev);
1542 #endif /* CONFIG_MOUSE */
1544 /* @ses may disappear ie. in close_tab() */
1545 if (ses) ses->kbdprefix.repeat_count = 0;
1548 enum frame_event_status
1549 download_link(struct session *ses, struct document_view *doc_view,
1550 action_id_T action_id)
1552 struct link *link = get_current_link(doc_view);
1553 void (*download)(void *ses, unsigned char *file) = start_download;
1555 if (!link) return FRAME_EVENT_OK;
1557 if (ses->download_uri) {
1558 done_uri(ses->download_uri);
1559 ses->download_uri = NULL;
1562 switch (action_id) {
1563 case ACT_MAIN_LINK_DOWNLOAD_RESUME:
1564 download = resume_download;
1565 case ACT_MAIN_LINK_DOWNLOAD:
1566 ses->download_uri = get_link_uri(ses, doc_view, link);
1567 break;
1569 case ACT_MAIN_LINK_DOWNLOAD_IMAGE:
1570 if (!link->where_img) break;
1571 ses->download_uri = get_uri(link->where_img, 0);
1572 break;
1574 default:
1575 INTERNAL("I think you forgot to take your medication, mental boy!");
1576 return FRAME_EVENT_OK;
1579 if (!ses->download_uri) return FRAME_EVENT_OK;
1581 set_session_referrer(ses, doc_view->document->uri);
1582 query_file(ses, ses->download_uri, ses, download, NULL, 1);
1584 return FRAME_EVENT_OK;
1587 enum frame_event_status
1588 view_image(struct session *ses, struct document_view *doc_view, int xxxx)
1590 struct link *link = get_current_link(doc_view);
1592 if (link && link->where_img)
1593 goto_url(ses, link->where_img);
1595 return FRAME_EVENT_OK;
1598 enum frame_event_status
1599 save_as(struct session *ses, struct document_view *doc_view, int magic)
1601 assert(ses);
1602 if_assert_failed return FRAME_EVENT_OK;
1604 if (!have_location(ses)) return FRAME_EVENT_OK;
1606 if (ses->download_uri) done_uri(ses->download_uri);
1607 ses->download_uri = get_uri_reference(cur_loc(ses)->vs.uri);
1609 assert(doc_view && doc_view->document && doc_view->document->uri);
1610 if_assert_failed return FRAME_EVENT_OK;
1612 set_session_referrer(ses, doc_view->document->uri);
1613 query_file(ses, ses->download_uri, ses, start_download, NULL, 1);
1615 return FRAME_EVENT_OK;
1618 /*! save_formatted() passes this function as a ::cdf_callback_T to
1619 * create_download_finish(). */
1620 static void
1621 save_formatted_finish(struct terminal *term, int h,
1622 void *data, enum download_flags flags)
1624 struct document *document = data;
1626 assert(term && document);
1627 if_assert_failed return;
1629 if (h == -1) return;
1630 if (dump_to_file(document, h)) {
1631 info_box(term, 0, N_("Save error"), ALIGN_CENTER,
1632 N_("Error writing to file"));
1634 close(h);
1637 static void
1638 save_formatted(void *data, unsigned char *file)
1640 struct session *ses = data;
1641 struct document_view *doc_view;
1643 assert(ses && ses->tab && ses->tab->term && file);
1644 if_assert_failed return;
1645 doc_view = current_frame(ses);
1646 assert(doc_view && doc_view->document);
1647 if_assert_failed return;
1649 create_download_file(ses->tab->term, file, NULL,
1650 DOWNLOAD_RESUME_DISABLED,
1651 save_formatted_finish, doc_view->document);
1654 enum frame_event_status
1655 save_formatted_dlg(struct session *ses, struct document_view *doc_view, int xxxx)
1657 query_file(ses, doc_view->vs->uri, ses, save_formatted, NULL, 1);
1658 return FRAME_EVENT_OK;