Initial commit of the HEAD branch of the ELinks CVS repository, as of
[elinks/images.git] / src / viewer / text / view.c
blob148d6b1779ba06405bb51b6093e34fee7ccbff39
1 /* HTML viewer (and much more) */
2 /* $Id: view.c,v 1.705 2005/09/08 13:42:13 zas Exp $ */
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"
65 void
66 detach_formatted(struct document_view *doc_view)
68 assert(doc_view);
69 if_assert_failed return;
71 if (doc_view->document) {
72 release_document(doc_view->document);
73 doc_view->document = NULL;
75 if (doc_view->vs) {
76 doc_view->vs->doc_view = NULL;
77 doc_view->vs = NULL;
79 if (doc_view->link_bg) free_link(doc_view);
80 mem_free_set(&doc_view->name, NULL);
83 /* type == 0 -> PAGE_DOWN
84 * type == 1 -> DOWN */
85 static enum frame_event_status
86 move_down(struct session *ses, struct document_view *doc_view, int type)
88 int newpos;
90 assert(ses && doc_view && doc_view->vs);
91 if_assert_failed return FRAME_EVENT_OK;
93 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
95 newpos = doc_view->vs->y + doc_view->box.height;
96 if (newpos < doc_view->document->height)
97 doc_view->vs->y = newpos;
99 if (current_link_is_visible(doc_view))
100 return FRAME_EVENT_REFRESH;
102 if (type)
103 find_link_down(doc_view);
104 else
105 find_link_page_down(doc_view);
107 return FRAME_EVENT_REFRESH;
110 enum frame_event_status
111 move_page_down(struct session *ses, struct document_view *doc_view)
113 enum frame_event_status status;
114 int count = eat_kbd_repeat_count(ses);
116 ses->navigate_mode = NAVIGATE_LINKWISE;
118 do {
119 status = move_down(ses, doc_view, 0);
120 if (status != FRAME_EVENT_REFRESH) break;
121 } while (--count > 0);
123 return status;
126 /* type == 0 -> PAGE_UP
127 * type == 1 -> UP */
128 static enum frame_event_status
129 move_up(struct session *ses, struct document_view *doc_view, int type)
131 assert(ses && doc_view && doc_view->vs);
132 if_assert_failed return FRAME_EVENT_OK;
134 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
136 if (doc_view->vs->y == 0) return FRAME_EVENT_OK;
138 doc_view->vs->y -= doc_view->box.height;
139 int_lower_bound(&doc_view->vs->y, 0);
141 if (current_link_is_visible(doc_view))
142 return FRAME_EVENT_REFRESH;
144 if (type)
145 find_link_up(doc_view);
146 else
147 find_link_page_up(doc_view);
149 return FRAME_EVENT_REFRESH;
152 enum frame_event_status
153 move_page_up(struct session *ses, struct document_view *doc_view)
155 enum frame_event_status status;
156 int count = eat_kbd_repeat_count(ses);
158 ses->navigate_mode = NAVIGATE_LINKWISE;
160 do {
161 status = move_up(ses, doc_view, 0);
162 if (status != FRAME_EVENT_REFRESH) break;
163 } while (--count > 0);
165 return status;
168 enum frame_event_status
169 move_link(struct session *ses, struct document_view *doc_view, int direction,
170 int wraparound_bound, int wraparound_link)
172 int wraparound = 0;
173 int count;
175 assert(ses && doc_view && doc_view->vs && doc_view->document);
176 if_assert_failed return FRAME_EVENT_OK;
178 ses->navigate_mode = NAVIGATE_LINKWISE;
180 if (doc_view->document->nlinks < 2) {
181 /* Wraparound only makes sense with more than one link. */
182 wraparound_bound = -1;
183 } else {
184 /* We only bother this option if there's some links
185 * in document. */
186 wraparound = get_opt_bool("document.browse.links.wraparound");
189 count = eat_kbd_repeat_count(ses);
191 do {
192 int current_link = doc_view->vs->current_link;
194 if (current_link == wraparound_bound) {
195 if (wraparound) {
196 jump_to_link_number(ses, doc_view, wraparound_link);
197 /* FIXME: This needs further work, we should call
198 * page_down() and set_textarea() under some conditions
199 * as well. --pasky */
200 continue;
203 } else {
204 if (next_link_in_view_y(doc_view, current_link + direction,
205 direction))
206 continue;
209 /* This is a work around for the case where the index of
210 * @wraparound_bound is not necessarily the index of the first
211 * or the last link in the view. It means that the link moving
212 * could end up calling next_link_in_view() in the condition
213 * above. This is bad because next_link_in_view() will then
214 * 'reset' doc_view->vs->current_link to -1 and the effect will
215 * be that the current link will 'wrap around'. By restoring
216 * the index of the @current_link nothing will be wrapped
217 * around and move_{up,down} will take care of finding the next
218 * link. */
219 doc_view->vs->current_link = current_link;
221 if (direction > 0) {
222 move_down(ses, doc_view, 1);
223 } else {
224 move_up(ses, doc_view, 1);
227 if (current_link != wraparound_bound
228 && current_link != doc_view->vs->current_link) {
229 set_textarea(doc_view, -direction);
231 } while (--count > 0);
233 return FRAME_EVENT_REFRESH;
236 enum frame_event_status
237 move_link_dir(struct session *ses, struct document_view *doc_view, int dir_x, int dir_y)
239 int count;
241 assert(ses && doc_view && doc_view->vs && doc_view->document);
242 if_assert_failed return FRAME_EVENT_OK;
244 ses->navigate_mode = NAVIGATE_LINKWISE;
246 count = eat_kbd_repeat_count(ses);
248 do {
249 int current_link = doc_view->vs->current_link;
251 if (next_link_in_dir(doc_view, dir_x, dir_y))
252 continue;
254 /* FIXME: This won't preserve the column! */
255 if (dir_y > 0)
256 move_down(ses, doc_view, 1);
257 else if (dir_y < 0)
258 move_up(ses, doc_view, 1);
260 if (dir_y && current_link != doc_view->vs->current_link) {
261 set_textarea(doc_view, -dir_y);
263 } while (--count > 0);
265 return FRAME_EVENT_REFRESH;
268 /* @steps > 0 -> down */
269 static enum frame_event_status
270 vertical_scroll(struct session *ses, struct document_view *doc_view, int steps)
272 int y;
274 assert(ses && doc_view && doc_view->vs && doc_view->document);
275 if_assert_failed return FRAME_EVENT_OK;
277 y = doc_view->vs->y + steps;
278 if (steps > 0) {
279 /* DOWN */
280 int max_height = doc_view->document->height - doc_view->box.height;
282 if (doc_view->vs->y >= max_height) return FRAME_EVENT_OK;
283 int_upper_bound(&y, max_height);
286 int_lower_bound(&y, 0);
288 if (doc_view->vs->y == y) return FRAME_EVENT_OK;
290 doc_view->vs->y = y;
292 if (current_link_is_visible(doc_view))
293 return FRAME_EVENT_REFRESH;
295 if (steps > 0)
296 find_link_page_down(doc_view);
297 else
298 find_link_page_up(doc_view);
300 return FRAME_EVENT_REFRESH;
303 /* @steps > 0 -> right */
304 static enum frame_event_status
305 horizontal_scroll(struct session *ses, struct document_view *doc_view, int steps)
307 int x, max;
309 assert(ses && doc_view && doc_view->vs && doc_view->document);
310 if_assert_failed return FRAME_EVENT_OK;
312 x = doc_view->vs->x + steps;
314 if (get_opt_bool("document.browse.scrolling.horizontal_extended")) {
315 max = doc_view->document->width - 1;
316 } else {
317 max = int_max(doc_view->vs->x,
318 doc_view->document->width - doc_view->box.width);
321 int_bounds(&x, 0, max);
322 if (doc_view->vs->x == x) return FRAME_EVENT_OK;
324 doc_view->vs->x = x;
326 if (current_link_is_visible(doc_view))
327 return FRAME_EVENT_REFRESH;
329 find_link_page_down(doc_view);
331 return FRAME_EVENT_REFRESH;
334 enum frame_event_status
335 scroll_up(struct session *ses, struct document_view *doc_view)
337 int steps = eat_kbd_repeat_count(ses);
339 if (!steps)
340 steps = get_opt_int("document.browse.scrolling.vertical_step");
342 return vertical_scroll(ses, doc_view, -steps);
345 enum frame_event_status
346 scroll_down(struct session *ses, struct document_view *doc_view)
348 int steps = eat_kbd_repeat_count(ses);
350 if (!steps)
351 steps = get_opt_int("document.browse.scrolling.vertical_step");
353 return vertical_scroll(ses, doc_view, steps);
356 enum frame_event_status
357 scroll_left(struct session *ses, struct document_view *doc_view)
359 int steps = eat_kbd_repeat_count(ses);
361 if (!steps)
362 steps = get_opt_int("document.browse.scrolling.horizontal_step");
364 return horizontal_scroll(ses, doc_view, -steps);
367 enum frame_event_status
368 scroll_right(struct session *ses, struct document_view *doc_view)
370 int steps = eat_kbd_repeat_count(ses);
372 if (!steps)
373 steps = get_opt_int("document.browse.scrolling.horizontal_step");
375 return horizontal_scroll(ses, doc_view, steps);
378 #ifdef CONFIG_MOUSE
379 static enum frame_event_status
380 scroll_mouse_up(struct session *ses, struct document_view *doc_view)
382 int steps = get_opt_int("document.browse.scrolling.vertical_step");
384 return vertical_scroll(ses, doc_view, -steps);
387 static enum frame_event_status
388 scroll_mouse_down(struct session *ses, struct document_view *doc_view)
390 int steps = get_opt_int("document.browse.scrolling.vertical_step");
392 return vertical_scroll(ses, doc_view, steps);
395 static enum frame_event_status
396 scroll_mouse_left(struct session *ses, struct document_view *doc_view)
398 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
400 return horizontal_scroll(ses, doc_view, -steps);
403 static enum frame_event_status
404 scroll_mouse_right(struct session *ses, struct document_view *doc_view)
406 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
408 return horizontal_scroll(ses, doc_view, steps);
410 #endif /* CONFIG_MOUSE */
412 enum frame_event_status
413 move_document_start(struct session *ses, struct document_view *doc_view)
415 assert(ses && doc_view && doc_view->vs);
416 if_assert_failed return FRAME_EVENT_OK;
418 doc_view->vs->y = doc_view->vs->x = 0;
420 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
421 /* Move to the first line and the first column. */
422 move_cursor(ses, doc_view, doc_view->box.x, doc_view->box.y);
423 } else {
424 find_link_page_down(doc_view);
427 return FRAME_EVENT_REFRESH;
430 enum frame_event_status
431 move_document_end(struct session *ses, struct document_view *doc_view)
433 int max_height;
435 assert(ses && doc_view && doc_view->vs && doc_view->document);
436 if_assert_failed return FRAME_EVENT_OK;
438 max_height = doc_view->document->height - doc_view->box.height;
439 doc_view->vs->x = 0;
440 int_lower_bound(&doc_view->vs->y, int_max(0, max_height));
442 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
443 /* Move to the last line of the document,
444 * but preserve the column. This is done to avoid
445 * moving the cursor backwards if it is already
446 * on the last line but is not on the first column. */
447 move_cursor(ses, doc_view, ses->tab->x,
448 doc_view->document->height - doc_view->vs->y);
449 } else {
450 find_link_page_up(doc_view);
453 return FRAME_EVENT_REFRESH;
456 enum frame_event_status
457 set_frame(struct session *ses, struct document_view *doc_view, int xxxx)
459 assert(ses && ses->doc_view && doc_view && doc_view->vs);
460 if_assert_failed return FRAME_EVENT_OK;
462 if (doc_view == ses->doc_view) return FRAME_EVENT_OK;
463 goto_uri(ses, doc_view->vs->uri);
464 ses->navigate_mode = NAVIGATE_LINKWISE;
466 return FRAME_EVENT_OK;
470 void
471 toggle_plain_html(struct session *ses, struct document_view *doc_view, int xxxx)
473 assert(ses && doc_view && ses->tab && ses->tab->term);
474 if_assert_failed return;
476 if (!doc_view->vs) {
477 nowhere_box(ses->tab->term, NULL);
478 return;
481 doc_view->vs->plain = !doc_view->vs->plain;
482 draw_formatted(ses, 1);
485 void
486 toggle_wrap_text(struct session *ses, struct document_view *doc_view, int xxxx)
488 assert(ses && doc_view && ses->tab && ses->tab->term);
489 if_assert_failed return;
491 if (!doc_view->vs) {
492 nowhere_box(ses->tab->term, NULL);
493 return;
496 doc_view->vs->wrap = !doc_view->vs->wrap;
497 draw_formatted(ses, 1);
500 /* Move the cursor to the document view co-ordinates provided as @x and @y,
501 * scroll the document if necessary, put us in cursor-routing navigation mode if
502 * that is not the current mode, and select any link under the cursor. */
503 enum frame_event_status
504 move_cursor(struct session *ses, struct document_view *doc_view, int x, int y)
506 enum frame_event_status status = FRAME_EVENT_REFRESH;
507 struct terminal *term = ses->tab->term;
508 struct box *box = &doc_view->box;
509 struct link *link;
511 /* If cursor was moved outside the document view scroll it, but only
512 * within the document canvas */
513 if (!is_in_box(box, x, y)) {
514 int max_height = doc_view->document->height - doc_view->vs->y;
515 int max_width = doc_view->document->width - doc_view->vs->x;
517 if (y < box->y) {
518 status = vertical_scroll(ses, doc_view, y - box->y);
520 } else if (y >= box->y + box->height && y <= max_height) {
521 status = vertical_scroll(ses, doc_view,
522 y - (box->y + box->height - 1));
524 } else if (x < box->x) {
525 status = horizontal_scroll(ses, doc_view, x - box->x);
527 } else if (x >= box->x + box->width && x <= max_width) {
528 status = horizontal_scroll(ses, doc_view,
529 x - (box->x + box->width - 1));
532 /* If the view was not scrolled there's nothing more to do */
533 if (status != FRAME_EVENT_REFRESH)
534 return status;
536 /* Restrict the cursor position within the current view */
537 int_bounds(&x, box->x, box->x + box->width - 1);
538 int_bounds(&y, box->y, box->y + box->height - 1);
541 /* Scrolling could have changed the navigation mode */
542 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
544 link = get_link_at_coordinates(doc_view, x - box->x, y - box->y);
545 if (link) {
546 doc_view->vs->current_link = link - doc_view->document->links;
547 } else {
548 doc_view->vs->current_link = -1;
551 /* Set the unblockable cursor position and update the window pointer so
552 * stuff like the link menu will be drawn relative to the cursor. */
553 set_cursor(term, x, y, 0);
554 set_window_ptr(ses->tab, x, y);
556 return status;
559 enum frame_event_status
560 move_cursor_rel(struct session *ses, struct document_view *view,
561 int rx, int ry)
563 int count = eat_kbd_repeat_count(ses);
564 int x, y;
566 int_lower_bound(&count, 1);
568 x = ses->tab->x + rx*count;
569 y = ses->tab->y + ry*count;
570 return move_cursor(ses, view, x, y);
573 enum frame_event_status
574 move_cursor_left(struct session *ses, struct document_view *view)
576 return move_cursor_rel(ses, view, -1, 0);
579 enum frame_event_status
580 move_cursor_right(struct session *ses, struct document_view *view)
582 return move_cursor_rel(ses, view, 1, 0);
585 enum frame_event_status
586 move_cursor_up(struct session *ses, struct document_view *view)
588 return move_cursor_rel(ses, view, 0, -1);
591 enum frame_event_status
592 move_cursor_down(struct session *ses, struct document_view *view)
594 return move_cursor_rel(ses, view, 0, 1);
598 enum frame_event_status
599 copy_current_link_to_clipboard(struct session *ses,
600 struct document_view *doc_view,
601 int xxx)
603 struct link *link;
604 struct uri *uri;
605 unsigned char *uristring;
607 link = get_current_link(doc_view);
608 if (!link) return FRAME_EVENT_OK;
610 uri = get_link_uri(ses, doc_view, link);
611 if (!uri) return FRAME_EVENT_OK;
613 uristring = get_uri_string(uri, URI_ORIGINAL);
614 done_uri(uri);
616 if (uristring) {
617 set_clipboard_text(uristring);
618 mem_free(uristring);
621 return FRAME_EVENT_OK;
626 try_jump_to_link_number(struct session *ses, struct document_view *doc_view)
628 int link_number = eat_kbd_repeat_count(ses) - 1;
630 if (link_number < 0) return 1;
632 if (!doc_view) return 0;
634 if (link_number >= doc_view->document->nlinks)
635 return 0;
637 jump_to_link_number(ses, doc_view, link_number);
638 refresh_view(ses, doc_view, 0);
640 return 1;
643 #ifdef CONFIG_MARKS
644 enum frame_event_status
645 try_mark_key(struct session *ses, struct document_view *doc_view,
646 struct term_event *ev)
648 unsigned char mark = get_kbd_key(ev);
650 switch (ses->kbdprefix.mark) {
651 case KP_MARK_NOTHING:
652 return FRAME_EVENT_IGNORED;
654 case KP_MARK_SET:
655 /* It is intentional to set the mark
656 * to NULL if !doc_view->vs. */
657 set_mark(mark, doc_view->vs);
658 break;
660 case KP_MARK_GOTO:
661 goto_mark(mark, doc_view->vs);
662 break;
665 ses->kbdprefix.repeat_count = 0;
666 ses->kbdprefix.mark = KP_MARK_NOTHING;
668 return FRAME_EVENT_REFRESH;
670 #endif
672 static enum frame_event_status
673 try_prefix_key(struct session *ses, struct document_view *doc_view,
674 struct term_event *ev)
676 struct document *document = doc_view->document;
677 struct document_options *doc_opts = &document->options;
678 int digit = get_kbd_key(ev) - '0';
680 if (digit < 0 || digit > 9)
681 return FRAME_EVENT_IGNORED;
683 if (get_kbd_modifier(ev)
684 || ses->kbdprefix.repeat_count /* The user has already begun
685 * entering a prefix. */
686 || !doc_opts->num_links_key
687 || (doc_opts->num_links_key == 1 && !doc_opts->links_numbering)) {
688 /* Repeat count.
689 * ses->kbdprefix.repeat_count is initialized to zero
690 * the first time by init_session() calloc() call.
691 * When used, it has to be reset to zero. */
693 ses->kbdprefix.repeat_count *= 10;
694 ses->kbdprefix.repeat_count += digit;
696 /* If too big, just restart from zero, so pressing
697 * '0' six times or more will reset the count. */
698 if (ses->kbdprefix.repeat_count > 99999)
699 ses->kbdprefix.repeat_count = 0;
701 return FRAME_EVENT_OK;
704 if (digit >= 1 && !get_kbd_modifier(ev)) {
705 int nlinks = document->nlinks, length;
706 unsigned char d[2] = { get_kbd_key(ev), 0 };
708 ses->kbdprefix.repeat_count = 0;
710 if (!nlinks) return FRAME_EVENT_OK;
712 for (length = 1; nlinks; nlinks /= 10)
713 length++;
715 input_dialog(ses->tab->term, NULL,
716 N_("Go to link"), N_("Enter link number"),
717 ses, NULL,
718 length, d, 1, document->nlinks, check_number,
719 (void (*)(void *, unsigned char *)) goto_link_number, NULL);
721 return FRAME_EVENT_OK;
724 return FRAME_EVENT_IGNORED;
727 static enum frame_event_status
728 try_form_insert_mode(struct session *ses, struct document_view *doc_view,
729 struct link *link, struct term_event *ev)
731 enum frame_event_status status = FRAME_EVENT_IGNORED;
732 enum edit_action action_id;
734 if (!link_is_textinput(link))
735 return FRAME_EVENT_IGNORED;
737 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
739 if (ses->insert_mode == INSERT_MODE_OFF) {
740 if (action_id == ACT_EDIT_ENTER) {
741 ses->insert_mode = INSERT_MODE_ON;
742 status = FRAME_EVENT_REFRESH;
746 return status;
749 static enum frame_event_status
750 try_form_action(struct session *ses, struct document_view *doc_view,
751 struct link *link, struct term_event *ev)
753 enum frame_event_status status;
755 assert(link);
757 if (!link_is_textinput(link))
758 return FRAME_EVENT_IGNORED;
760 status = field_op(ses, doc_view, link, ev);
762 if (status != FRAME_EVENT_IGNORED
763 && ses->insert_mode == INSERT_MODE_ON) {
764 assert(link == get_current_link(doc_view));
767 return status;
770 static enum frame_event_status
771 frame_ev_kbd(struct session *ses, struct document_view *doc_view, struct term_event *ev)
773 enum frame_event_status status = FRAME_EVENT_IGNORED;
774 int accesskey_priority;
775 struct link *link = get_current_link(doc_view);
777 if (link) {
778 status = try_form_insert_mode(ses, doc_view, link, ev);
779 if (status != FRAME_EVENT_IGNORED)
780 return status;
782 status = try_form_action(ses, doc_view, link, ev);
783 if (status != FRAME_EVENT_IGNORED)
784 return status;
787 #ifdef CONFIG_MARKS
788 status = try_mark_key(ses, doc_view, ev);
789 if (status != FRAME_EVENT_IGNORED)
790 return status;
791 #endif
792 accesskey_priority = get_opt_int("document.browse.accesskey.priority");
794 if (accesskey_priority >= 2) {
795 status = try_document_key(ses, doc_view, ev);
797 if (status != FRAME_EVENT_IGNORED) {
798 /* The document ate the key! */
799 return status;
803 status = try_prefix_key(ses, doc_view, ev);
804 if (status != FRAME_EVENT_IGNORED)
805 return status;
807 if (accesskey_priority == 1) {
808 status = try_document_key(ses, doc_view, ev);
810 if (status != FRAME_EVENT_IGNORED) {
811 /* The document ate the key! */
812 return status;
816 return FRAME_EVENT_IGNORED;
819 #ifdef CONFIG_MOUSE
820 static enum frame_event_status
821 frame_ev_mouse(struct session *ses, struct document_view *doc_view, struct term_event *ev)
823 int x = ev->info.mouse.x;
824 int y = ev->info.mouse.y;
825 struct link *link;
827 if (check_mouse_wheel(ev)) {
828 if (!check_mouse_action(ev, B_DOWN)) {
829 /* We handle only B_DOWN case... */
830 } else if (check_mouse_button(ev, B_WHEEL_UP)) {
831 return scroll_mouse_up(ses, doc_view);
832 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
833 return scroll_mouse_down(ses, doc_view);
836 return FRAME_EVENT_OK;
839 link = get_link_at_coordinates(doc_view, x, y);
840 if (link) {
841 enum frame_event_status status = FRAME_EVENT_REFRESH;
843 doc_view->vs->current_link = link - doc_view->document->links;
845 if (!link_is_textinput(link)) {
847 status = FRAME_EVENT_OK;
849 refresh_view(ses, doc_view, 0);
851 if (check_mouse_button(ev, B_LEFT)
852 || check_mouse_button(ev, B_MIDDLE)) {
853 if (check_mouse_action(ev, B_DOWN))
854 do_not_ignore_next_mouse_event(ses->tab->term);
855 else if (check_mouse_button(ev, B_LEFT))
856 status = enter(ses, doc_view, 0);
857 else if (check_mouse_button(ev, B_MIDDLE))
858 open_current_link_in_new_tab(ses, 1);
859 } else {
860 link_menu(ses->tab->term, NULL, ses);
864 return status;
867 if (check_mouse_button(ev, B_LEFT)) {
868 /* Clicking the edge of screen will scroll the document. */
870 int scrollmargin = get_opt_int("document.browse.scrolling.margin");
872 /* XXX: This is code duplication with kbd handlers. But
873 * repeatcount-free here. */
875 if (y < scrollmargin) {
876 return scroll_mouse_up(ses, doc_view);
878 if (y >= doc_view->box.height - scrollmargin) {
879 return scroll_mouse_down(ses, doc_view);
882 if (x < scrollmargin * 2) {
883 return scroll_mouse_left(ses, doc_view);
885 if (x >= doc_view->box.width - scrollmargin * 2) {
886 return scroll_mouse_right(ses, doc_view);
889 return FRAME_EVENT_OK;
892 return FRAME_EVENT_IGNORED;
894 #endif /* CONFIG_MOUSE */
896 static enum frame_event_status
897 frame_ev(struct session *ses, struct document_view *doc_view, struct term_event *ev)
899 assertm(doc_view && doc_view->document, "document not formatted");
900 if_assert_failed return FRAME_EVENT_IGNORED;
902 assert(ses && ev);
903 if_assert_failed return FRAME_EVENT_IGNORED;
905 /* When changing frame, vs may be NULL. See bug 525. */
906 if (!doc_view->vs) return FRAME_EVENT_IGNORED;
908 switch (ev->ev) {
909 case EVENT_KBD:
910 return frame_ev_kbd(ses, doc_view, ev);
911 #ifdef CONFIG_MOUSE
912 case EVENT_MOUSE:
913 return frame_ev_mouse(ses, doc_view, ev);
914 #endif /* CONFIG_MOUSE */
915 default:
916 return FRAME_EVENT_IGNORED;
920 struct document_view *
921 current_frame(struct session *ses)
923 struct document_view *doc_view = NULL;
924 int current_frame_number;
926 assert(ses);
927 if_assert_failed return NULL;
929 if (!have_location(ses)) return NULL;
931 current_frame_number = cur_loc(ses)->vs.current_link;
932 if (current_frame_number == -1) current_frame_number = 0;
934 foreach (doc_view, ses->scrn_frames) {
935 if (document_has_frames(doc_view->document)) continue;
936 if (!current_frame_number--) return doc_view;
939 doc_view = ses->doc_view;
941 assert(doc_view && doc_view->document);
942 if_assert_failed return NULL;
944 if (document_has_frames(doc_view->document)) return NULL;
945 return doc_view;
948 static enum frame_event_status
949 send_to_frame(struct session *ses, struct document_view *doc_view,
950 struct term_event *ev)
952 enum frame_event_status status;
954 assert(ses && ses->tab && ses->tab->term && ev);
955 if_assert_failed return FRAME_EVENT_IGNORED;
957 status = frame_ev(ses, doc_view, ev);
959 if (status == FRAME_EVENT_REFRESH)
960 refresh_view(ses, doc_view, 0);
961 else
962 print_screen_status(ses);
964 return status;
967 #ifdef CONFIG_MOUSE
968 static int
969 do_mouse_event(struct session *ses, struct term_event *ev,
970 struct document_view *doc_view)
972 struct term_event evv;
973 struct document_view *matched = NULL, *first = doc_view;
975 assert(ses && ev);
976 if_assert_failed return 0;
978 if (!doc_view) return 0;
980 do {
981 assert(doc_view && doc_view->document);
982 if_assert_failed return 0;
984 assertm(doc_view->document->options.box.x == doc_view->box.x
985 && doc_view->document->options.box.y == doc_view->box.y,
986 "Jonas' 1.565 -> 1.566 patch sucks");
987 if_assert_failed return 0;
989 if (check_mouse_position(ev, &doc_view->box)) {
990 matched = doc_view;
991 break;
994 next_frame(ses, 1);
995 doc_view = current_frame(ses);
997 } while (doc_view != first);
999 if (!matched) return 0;
1001 if (doc_view != first) draw_formatted(ses, 0);
1003 set_mouse_term_event(&evv,
1004 ev->info.mouse.x - doc_view->box.x,
1005 ev->info.mouse.y - doc_view->box.y,
1006 ev->info.mouse.button);
1008 return send_to_frame(ses, doc_view, &evv);
1011 /* Returns the session if event cleanup should be done or NULL if no cleanup is
1012 * needed. */
1013 static struct session *
1014 send_mouse_event(struct session *ses, struct document_view *doc_view,
1015 struct term_event *ev)
1017 struct terminal *term = ses->tab->term;
1018 struct term_event_mouse *mouse = &ev->info.mouse;
1020 if (mouse->y == 0
1021 && check_mouse_action(ev, B_DOWN)
1022 && !check_mouse_wheel(ev)) {
1023 struct window *m;
1025 activate_bfu_technology(ses, -1);
1026 m = term->windows.next;
1027 m->handler(m, ev);
1029 return ses;
1032 /* Handle tabs navigation if tabs bar is displayed. */
1033 if (ses->status.show_tabs_bar
1034 && mouse->y == term->height - 1 - !!ses->status.show_status_bar) {
1035 int tab_num = get_tab_number_by_xpos(term, mouse->x);
1036 struct window *tab = get_current_tab(term);
1038 if (check_mouse_action(ev, B_UP)) {
1039 if (check_mouse_button(ev, B_MIDDLE)
1040 && term->current_tab == tab_num
1041 && mouse->y == term->prev_mouse_event.y) {
1042 if (tab->data == ses) ses = NULL;
1044 close_tab(term, tab->data);
1047 return ses;
1050 if (check_mouse_button(ev, B_WHEEL_UP)) {
1051 switch_current_tab(ses, -1);
1053 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
1054 switch_current_tab(ses, 1);
1056 } else if (tab_num != -1) {
1057 switch_to_tab(term, tab_num, -1);
1059 if (check_mouse_button(ev, B_MIDDLE)) {
1060 do_not_ignore_next_mouse_event(term);
1061 } else if (check_mouse_button(ev, B_RIGHT)) {
1062 tab_menu(tab->data, mouse->x, mouse->y, 1);
1066 return ses;
1069 if (!do_mouse_event(ses, ev, doc_view)
1070 && check_mouse_button(ev, B_RIGHT)) {
1071 tab_menu(ses, mouse->x, mouse->y, 0);
1072 return NULL;
1075 #ifdef CONFIG_LEDS
1076 if (ses->status.show_leds
1077 && mouse->y == term->height - 1
1078 && mouse->x >= term->width - LEDS_COUNT - 3) {
1079 menu_leds_info(term, NULL, NULL);
1080 return NULL;
1082 #endif
1084 return NULL;
1086 #endif /* CONFIG_MOUSE */
1088 static void
1089 try_typeahead(struct session *ses, struct document_view *doc_view,
1090 struct term_event *ev, enum main_action action_id)
1092 switch (get_opt_int("document.browse.search.typeahead")) {
1093 case 0:
1094 return;
1095 case 1:
1096 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_LINK;
1097 break;
1098 case 2:
1099 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_TEXT;
1100 break;
1101 default:
1102 INTERNAL("invalid value for document.browse.search.typeahead");
1105 search_typeahead(ses, doc_view, action_id);
1107 /* Cross your fingers -- I'm just asking
1108 * for an infinite loop! -- Miciah */
1109 term_send_event(ses->tab->term, ev);
1112 /* Returns the session if event cleanup should be done or NULL if no cleanup is
1113 * needed. */
1114 static struct session *
1115 send_kbd_event(struct session *ses, struct document_view *doc_view,
1116 struct term_event *ev)
1118 int event;
1119 enum main_action action_id;
1121 if (doc_view && send_to_frame(ses, doc_view, ev) != FRAME_EVENT_IGNORED)
1122 return NULL;
1124 action_id = kbd_action(KEYMAP_MAIN, ev, &event);
1126 if (action_id == ACT_MAIN_QUIT) {
1127 if (check_kbd_key(ev, KBD_CTRL_C))
1128 quit:
1129 action_id = ACT_MAIN_REALLY_QUIT;
1132 switch (do_action(ses, action_id, 0)) {
1133 case FRAME_EVENT_SESSION_DESTROYED:
1134 return NULL;
1135 case FRAME_EVENT_IGNORED:
1136 break;
1137 case FRAME_EVENT_OK:
1138 case FRAME_EVENT_REFRESH:
1139 return ses;
1142 if (action_id == ACT_MAIN_SCRIPTING_FUNCTION) {
1143 #ifdef CONFIG_SCRIPTING
1144 trigger_event(event, ses);
1145 #endif
1146 return NULL;
1149 if (check_kbd_key(ev, KBD_CTRL_C)) goto quit;
1151 if (get_kbd_modifier(ev) & KBD_MOD_ALT) {
1152 struct window *win;
1154 get_kbd_modifier(ev) &= ~KBD_MOD_ALT;
1155 activate_bfu_technology(ses, -1);
1156 win = ses->tab->term->windows.next;
1157 win->handler(win, ev);
1158 if (ses->tab->term->windows.next == win) {
1159 delete_window(win);
1160 get_kbd_modifier(ev) |= KBD_MOD_ALT;
1162 return NULL;
1165 if (doc_view
1166 && get_opt_int("document.browse.accesskey"
1167 ".priority") <= 0
1168 && try_document_key(ses, doc_view, ev)
1169 == FRAME_EVENT_REFRESH) {
1170 /* The document ate the key! */
1171 refresh_view(ses, doc_view, 0);
1173 return NULL;
1176 return ses;
1179 if (!(get_kbd_modifier(ev) & KBD_MOD_CTRL)) {
1180 if (doc_view) try_typeahead(ses, doc_view, ev, action_id);
1183 return NULL;
1186 void
1187 send_event(struct session *ses, struct term_event *ev)
1189 struct document_view *doc_view;
1191 assert(ses && ev);
1192 if_assert_failed return;
1193 doc_view = current_frame(ses);
1195 if (ev->ev == EVENT_KBD) {
1196 ses = send_kbd_event(ses, doc_view, ev);
1198 #ifdef CONFIG_MOUSE
1199 if (ev->ev == EVENT_MOUSE) {
1200 ses = send_mouse_event(ses, doc_view, ev);
1202 #endif /* CONFIG_MOUSE */
1204 /* ses may disappear ie. in close_tab() */
1205 if (ses) ses->kbdprefix.repeat_count = 0;
1208 enum frame_event_status
1209 download_link(struct session *ses, struct document_view *doc_view,
1210 action_id_T action_id)
1212 struct link *link = get_current_link(doc_view);
1213 void (*download)(void *ses, unsigned char *file) = start_download;
1215 if (!link) return FRAME_EVENT_OK;
1217 if (ses->download_uri) {
1218 done_uri(ses->download_uri);
1219 ses->download_uri = NULL;
1222 switch (action_id) {
1223 case ACT_MAIN_LINK_DOWNLOAD_RESUME:
1224 download = resume_download;
1225 case ACT_MAIN_LINK_DOWNLOAD:
1226 ses->download_uri = get_link_uri(ses, doc_view, link);
1227 break;
1229 case ACT_MAIN_LINK_DOWNLOAD_IMAGE:
1230 if (!link->where_img) break;
1231 ses->download_uri = get_uri(link->where_img, 0);
1232 break;
1234 default:
1235 INTERNAL("I think you forgot to take your medication, mental boy!");
1236 return FRAME_EVENT_OK;
1239 if (!ses->download_uri) return FRAME_EVENT_OK;
1241 set_session_referrer(ses, doc_view->document->uri);
1242 query_file(ses, ses->download_uri, ses, download, NULL, 1);
1244 return FRAME_EVENT_OK;
1247 enum frame_event_status
1248 view_image(struct session *ses, struct document_view *doc_view, int xxxx)
1250 struct link *link = get_current_link(doc_view);
1252 if (link && link->where_img)
1253 goto_url(ses, link->where_img);
1255 return FRAME_EVENT_OK;
1258 enum frame_event_status
1259 save_as(struct session *ses, struct document_view *doc_view, int magic)
1261 assert(ses);
1262 if_assert_failed return FRAME_EVENT_OK;
1264 if (!have_location(ses)) return FRAME_EVENT_OK;
1266 if (ses->download_uri) done_uri(ses->download_uri);
1267 ses->download_uri = get_uri_reference(cur_loc(ses)->vs.uri);
1269 assert(doc_view && doc_view->document && doc_view->document->uri);
1270 if_assert_failed return FRAME_EVENT_OK;
1272 set_session_referrer(ses, doc_view->document->uri);
1273 query_file(ses, ses->download_uri, ses, start_download, NULL, 1);
1275 return FRAME_EVENT_OK;
1278 static void
1279 save_formatted_finish(struct terminal *term, int h, void *data, int resume)
1281 struct document *document = data;
1283 assert(term && document);
1284 if_assert_failed return;
1286 if (h == -1) return;
1287 if (dump_to_file(document, h)) {
1288 info_box(term, 0, N_("Save error"), ALIGN_CENTER,
1289 N_("Error writing to file"));
1291 close(h);
1294 static void
1295 save_formatted(void *data, unsigned char *file)
1297 struct session *ses = data;
1298 struct document_view *doc_view;
1300 assert(ses && ses->tab && ses->tab->term && file);
1301 if_assert_failed return;
1302 doc_view = current_frame(ses);
1303 assert(doc_view && doc_view->document);
1304 if_assert_failed return;
1306 create_download_file(ses->tab->term, file, NULL, 0, 0,
1307 save_formatted_finish, doc_view->document);
1310 enum frame_event_status
1311 save_formatted_dlg(struct session *ses, struct document_view *doc_view, int xxxx)
1313 query_file(ses, doc_view->vs->uri, ses, save_formatted, NULL, 1);
1314 return FRAME_EVENT_OK;