move-link-down-line, move-link-prev-line, etc.:
[elinks.git] / src / viewer / text / view.c
bloba0ebefa439ec7dd824581ab4515d3a953f036602
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"
64 static enum frame_event_status move_cursor_rel(struct session *ses, struct document_view *view, int rx, int ry);
66 void
67 detach_formatted(struct document_view *doc_view)
69 assert(doc_view);
70 if_assert_failed return;
72 #ifdef CONFIG_ECMASCRIPT
73 if (doc_view->session) {
74 mem_free_set(&doc_view->session->status.window_status, NULL);
76 #endif
77 if (doc_view->document) {
78 release_document(doc_view->document);
79 doc_view->document = NULL;
81 if (doc_view->vs) {
82 doc_view->vs->doc_view = NULL;
83 doc_view->vs = NULL;
85 mem_free_set(&doc_view->name, NULL);
88 /*! @a type == 0 -> PAGE_DOWN;
89 * @a type == 1 -> DOWN */
90 static void
91 move_down(struct session *ses, struct document_view *doc_view, int type)
93 int newpos;
95 assert(ses && doc_view && doc_view->vs);
96 if_assert_failed return;
98 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
100 newpos = doc_view->vs->y + doc_view->box.height;
101 if (newpos < doc_view->document->height)
102 doc_view->vs->y = newpos;
104 if (current_link_is_visible(doc_view))
105 return;
107 if (type)
108 find_link_down(doc_view);
109 else
110 find_link_page_down(doc_view);
112 return;
115 enum frame_event_status
116 move_page_down(struct session *ses, struct document_view *doc_view)
118 int oldy = doc_view->vs->y;
119 int count = eat_kbd_repeat_count(ses);
121 ses->navigate_mode = NAVIGATE_LINKWISE;
123 do move_down(ses, doc_view, 0); while (--count > 0);
125 return doc_view->vs->y == oldy ? FRAME_EVENT_OK : FRAME_EVENT_REFRESH;
128 /*! @a type == 0 -> PAGE_UP;
129 * @a type == 1 -> UP */
130 static void
131 move_up(struct session *ses, struct document_view *doc_view, int type)
133 assert(ses && doc_view && doc_view->vs);
134 if_assert_failed return;
136 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
138 if (doc_view->vs->y == 0) return;
140 doc_view->vs->y -= doc_view->box.height;
141 int_lower_bound(&doc_view->vs->y, 0);
143 if (current_link_is_visible(doc_view))
144 return;
146 if (type)
147 find_link_up(doc_view);
148 else
149 find_link_page_up(doc_view);
151 return;
154 enum frame_event_status
155 move_page_up(struct session *ses, struct document_view *doc_view)
157 int oldy = doc_view->vs->y;
158 int count = eat_kbd_repeat_count(ses);
160 ses->navigate_mode = NAVIGATE_LINKWISE;
162 do move_up(ses, doc_view, 0); while (--count > 0);
164 return doc_view->vs->y == oldy ? FRAME_EVENT_OK : FRAME_EVENT_REFRESH;
167 enum frame_event_status
168 move_link_prev_line(struct session *ses, struct document_view *doc_view)
170 struct view_state *vs;
171 struct document *document;
172 struct box *box;
173 struct link *link, *last = NULL;
174 int y1, y, min_x, max_x, x1;
176 assert(ses && doc_view && doc_view->vs && doc_view->document);
177 if_assert_failed return FRAME_EVENT_OK;
179 vs = doc_view->vs;
180 document = doc_view->document;
181 if (!document->lines1) return FRAME_EVENT_OK;
182 box = &doc_view->box;
184 y = y1 = vs->y + ses->tab->y - box->y;
185 x1 = vs->x + ses->tab->x - box->x;
187 link = get_current_link(doc_view);
188 if (link) {
189 get_link_x_bounds(link, y1, &min_x, &max_x);
190 } else {
191 min_x = max_x = x1;
192 int_upper_bound(&y, document->height - 1);
195 for (; y >= 0; y--, min_x = INT_MAX) {
196 link = document->lines1[y];
197 if (!link) continue;
198 for (; link <= document->lines2[y]; link++) {
199 if (link->points[0].y != y) continue;
200 if (link->points[0].x >= min_x) continue;
201 if (!last) last = link;
202 else if (link->points[0].x > last->points[0].x) last = link;
204 if (last) {
205 enum frame_event_status status = move_cursor_rel(ses, doc_view,
206 last->points[0].x - x1, last->points[0].y - y1);
208 ses->navigate_mode = NAVIGATE_LINKWISE;
209 return status;
212 return FRAME_EVENT_OK;
216 enum frame_event_status
217 move_link_next_line(struct session *ses, struct document_view *doc_view)
219 struct view_state *vs;
220 struct document *document;
221 struct box *box;
222 struct link *link, *last = NULL;
223 int y1, y, min_x, max_x, x1;
225 assert(ses && doc_view && doc_view->vs && doc_view->document);
226 if_assert_failed return FRAME_EVENT_OK;
228 vs = doc_view->vs;
229 document = doc_view->document;
230 if (!document->lines1) return FRAME_EVENT_OK;
231 box = &doc_view->box;
233 y = y1 = vs->y + ses->tab->y - box->y;
234 x1 = vs->x + ses->tab->x - box->x;
236 link = get_current_link(doc_view);
237 if (link) {
238 get_link_x_bounds(link, y1, &min_x, &max_x);
239 } else {
240 min_x = max_x = x1;
241 int_upper_bound(&y, document->height - 1);
244 for (; y < document->height; y++, min_x = -1) {
245 link = document->lines1[y];
246 if (!link) continue;
247 for (; link <= document->lines2[y]; link++) {
248 if (link->points[0].y != y) continue;
249 if (link->points[0].x <= min_x) continue;
250 if (!last) last = link;
251 else if (link->points[0].x < last->points[0].x) last = link;
253 if (last) {
254 enum frame_event_status status = move_cursor_rel(ses, doc_view,
255 last->points[0].x - x1, last->points[0].y - y1);
257 ses->navigate_mode = NAVIGATE_LINKWISE;
258 return status;
261 return FRAME_EVENT_OK;
264 enum frame_event_status
265 move_link(struct session *ses, struct document_view *doc_view, int direction,
266 int wraparound_bound, int wraparound_link)
268 int wraparound = 0;
269 int count;
271 assert(ses && doc_view && doc_view->vs && doc_view->document);
272 if_assert_failed return FRAME_EVENT_OK;
274 ses->navigate_mode = NAVIGATE_LINKWISE;
276 if (doc_view->document->nlinks < 2) {
277 /* Wraparound only makes sense with more than one link. */
278 wraparound_bound = -1;
279 } else {
280 /* We only bother this option if there's some links
281 * in document. */
282 wraparound = get_opt_bool("document.browse.links.wraparound");
285 count = eat_kbd_repeat_count(ses);
287 do {
288 int current_link = doc_view->vs->current_link;
290 if (current_link == wraparound_bound) {
291 if (wraparound) {
292 jump_to_link_number(ses, doc_view, wraparound_link);
293 /* FIXME: This needs further work, we should call
294 * page_down() and set_textarea() under some conditions
295 * as well. --pasky */
296 continue;
299 } else {
300 if (next_link_in_view_y(doc_view, current_link + direction,
301 direction))
302 continue;
305 /* This is a work around for the case where the index of
306 * @wraparound_bound is not necessarily the index of the first
307 * or the last link in the view. It means that the link moving
308 * could end up calling next_link_in_view() in the condition
309 * above. This is bad because next_link_in_view() will then
310 * 'reset' doc_view->vs->current_link to -1 and the effect will
311 * be that the current link will 'wrap around'. By restoring
312 * the index of the @current_link nothing will be wrapped
313 * around and move_{up,down} will take care of finding the next
314 * link. */
315 doc_view->vs->current_link = current_link;
317 if (direction > 0) {
318 move_down(ses, doc_view, 1);
319 } else {
320 move_up(ses, doc_view, 1);
323 if (current_link != wraparound_bound
324 && current_link != doc_view->vs->current_link) {
325 set_textarea(doc_view, -direction);
327 } while (--count > 0);
329 return FRAME_EVENT_REFRESH;
332 enum frame_event_status
333 move_link_dir(struct session *ses, struct document_view *doc_view, int dir_x, int dir_y)
335 int count;
337 assert(ses && doc_view && doc_view->vs && doc_view->document);
338 if_assert_failed return FRAME_EVENT_OK;
340 ses->navigate_mode = NAVIGATE_LINKWISE;
342 count = eat_kbd_repeat_count(ses);
344 do {
345 int current_link = doc_view->vs->current_link;
347 if (next_link_in_dir(doc_view, dir_x, dir_y))
348 continue;
350 /* FIXME: This won't preserve the column! */
351 if (dir_y > 0)
352 move_down(ses, doc_view, 1);
353 else if (dir_y < 0)
354 move_up(ses, doc_view, 1);
356 if (dir_y && current_link != doc_view->vs->current_link) {
357 set_textarea(doc_view, -dir_y);
359 } while (--count > 0);
361 return FRAME_EVENT_REFRESH;
364 /*! @a steps > 0 -> down */
365 static enum frame_event_status
366 vertical_scroll(struct session *ses, struct document_view *doc_view, int steps)
368 int y;
370 assert(ses && doc_view && doc_view->vs && doc_view->document);
371 if_assert_failed return FRAME_EVENT_OK;
373 y = doc_view->vs->y + steps;
374 if (steps > 0) {
375 /* DOWN */
376 int max_height = doc_view->document->height - doc_view->box.height;
378 if (doc_view->vs->y >= max_height) return FRAME_EVENT_OK;
379 int_upper_bound(&y, max_height);
382 int_lower_bound(&y, 0);
384 if (doc_view->vs->y == y) return FRAME_EVENT_OK;
386 doc_view->vs->y = y;
388 if (current_link_is_visible(doc_view))
389 return FRAME_EVENT_REFRESH;
391 if (steps > 0)
392 find_link_page_down(doc_view);
393 else
394 find_link_page_up(doc_view);
396 return FRAME_EVENT_REFRESH;
399 /*! @a steps > 0 -> right */
400 static enum frame_event_status
401 horizontal_scroll(struct session *ses, struct document_view *doc_view, int steps)
403 int x, max;
405 assert(ses && doc_view && doc_view->vs && doc_view->document);
406 if_assert_failed return FRAME_EVENT_OK;
408 x = doc_view->vs->x + steps;
410 if (get_opt_bool("document.browse.scrolling.horizontal_extended")) {
411 max = doc_view->document->width - 1;
412 } else {
413 max = int_max(doc_view->vs->x,
414 doc_view->document->width - doc_view->box.width);
417 int_bounds(&x, 0, max);
418 if (doc_view->vs->x == x) return FRAME_EVENT_OK;
420 doc_view->vs->x = x;
422 if (current_link_is_visible(doc_view))
423 return FRAME_EVENT_REFRESH;
425 find_link_page_down(doc_view);
427 return FRAME_EVENT_REFRESH;
430 enum frame_event_status
431 scroll_up(struct session *ses, struct document_view *doc_view)
433 int steps = eat_kbd_repeat_count(ses);
435 if (!steps)
436 steps = get_opt_int("document.browse.scrolling.vertical_step");
438 return vertical_scroll(ses, doc_view, -steps);
441 enum frame_event_status
442 scroll_down(struct session *ses, struct document_view *doc_view)
444 int steps = eat_kbd_repeat_count(ses);
446 if (!steps)
447 steps = get_opt_int("document.browse.scrolling.vertical_step");
449 return vertical_scroll(ses, doc_view, steps);
452 enum frame_event_status
453 scroll_left(struct session *ses, struct document_view *doc_view)
455 int steps = eat_kbd_repeat_count(ses);
457 if (!steps)
458 steps = get_opt_int("document.browse.scrolling.horizontal_step");
460 return horizontal_scroll(ses, doc_view, -steps);
463 enum frame_event_status
464 scroll_right(struct session *ses, struct document_view *doc_view)
466 int steps = eat_kbd_repeat_count(ses);
468 if (!steps)
469 steps = get_opt_int("document.browse.scrolling.horizontal_step");
471 return horizontal_scroll(ses, doc_view, steps);
474 #ifdef CONFIG_MOUSE
475 static enum frame_event_status
476 scroll_mouse_up(struct session *ses, struct document_view *doc_view)
478 int steps = get_opt_int("document.browse.scrolling.vertical_step");
480 return vertical_scroll(ses, doc_view, -steps);
483 static enum frame_event_status
484 scroll_mouse_down(struct session *ses, struct document_view *doc_view)
486 int steps = get_opt_int("document.browse.scrolling.vertical_step");
488 return vertical_scroll(ses, doc_view, steps);
491 static enum frame_event_status
492 scroll_mouse_left(struct session *ses, struct document_view *doc_view)
494 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
496 return horizontal_scroll(ses, doc_view, -steps);
499 static enum frame_event_status
500 scroll_mouse_right(struct session *ses, struct document_view *doc_view)
502 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
504 return horizontal_scroll(ses, doc_view, steps);
506 #endif /* CONFIG_MOUSE */
508 enum frame_event_status
509 move_document_start(struct session *ses, struct document_view *doc_view)
511 assert(ses && doc_view && doc_view->vs);
512 if_assert_failed return FRAME_EVENT_OK;
514 doc_view->vs->y = doc_view->vs->x = 0;
516 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
517 /* Move to the first line and the first column. */
518 move_cursor(ses, doc_view, doc_view->box.x, doc_view->box.y);
519 } else {
520 find_link_page_down(doc_view);
523 return FRAME_EVENT_REFRESH;
526 enum frame_event_status
527 move_document_end(struct session *ses, struct document_view *doc_view)
529 int max_height;
531 assert(ses && doc_view && doc_view->vs && doc_view->document);
532 if_assert_failed return FRAME_EVENT_OK;
534 max_height = doc_view->document->height - doc_view->box.height;
535 doc_view->vs->x = 0;
536 int_lower_bound(&doc_view->vs->y, int_max(0, max_height));
538 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
539 /* Move to the last line of the document,
540 * but preserve the column. This is done to avoid
541 * moving the cursor backwards if it is already
542 * on the last line but is not on the first column. */
543 move_cursor(ses, doc_view, ses->tab->x,
544 doc_view->document->height - doc_view->vs->y);
545 } else {
546 find_link_page_up(doc_view);
549 return FRAME_EVENT_REFRESH;
552 enum frame_event_status
553 set_frame(struct session *ses, struct document_view *doc_view, int xxxx)
555 assert(ses && ses->doc_view && doc_view && doc_view->vs);
556 if_assert_failed return FRAME_EVENT_OK;
558 if (doc_view == ses->doc_view) return FRAME_EVENT_OK;
559 goto_uri(ses, doc_view->vs->uri);
560 ses->navigate_mode = NAVIGATE_LINKWISE;
562 return FRAME_EVENT_OK;
566 void
567 toggle_plain_html(struct session *ses, struct document_view *doc_view, int xxxx)
569 assert(ses && doc_view && ses->tab && ses->tab->term);
570 if_assert_failed return;
572 if (!doc_view->vs) {
573 nowhere_box(ses->tab->term, NULL);
574 return;
577 doc_view->vs->plain = !doc_view->vs->plain;
578 draw_formatted(ses, 1);
581 void
582 toggle_wrap_text(struct session *ses, struct document_view *doc_view, int xxxx)
584 assert(ses && doc_view && ses->tab && ses->tab->term);
585 if_assert_failed return;
587 if (!doc_view->vs) {
588 nowhere_box(ses->tab->term, NULL);
589 return;
592 doc_view->vs->wrap = !doc_view->vs->wrap;
593 draw_formatted(ses, 1);
596 /** Move the cursor to the document view co-ordinates provided
597 * as @a x and @a y, scroll the document if necessary, put us in
598 * cursor-routing navigation mode if that is not the current mode,
599 * and select any link under the cursor. */
600 enum frame_event_status
601 move_cursor(struct session *ses, struct document_view *doc_view, int x, int y)
603 enum frame_event_status status = FRAME_EVENT_REFRESH;
604 struct terminal *term = ses->tab->term;
605 struct box *box = &doc_view->box;
606 struct link *link;
608 /* If cursor was moved outside the document view scroll it, but only
609 * within the document canvas */
610 if (!is_in_box(box, x, y)) {
611 int max_height = doc_view->document->height - doc_view->vs->y;
612 int max_width = doc_view->document->width - doc_view->vs->x;
614 if (y < box->y) {
615 status = vertical_scroll(ses, doc_view, y - box->y);
617 } else if (y >= box->y + box->height && y <= max_height) {
618 status = vertical_scroll(ses, doc_view,
619 y - (box->y + box->height - 1));
621 } else if (x < box->x) {
622 status = horizontal_scroll(ses, doc_view, x - box->x);
624 } else if (x >= box->x + box->width && x <= max_width) {
625 status = horizontal_scroll(ses, doc_view,
626 x - (box->x + box->width - 1));
629 /* If the view was not scrolled there's nothing more to do */
630 if (status != FRAME_EVENT_REFRESH)
631 return status;
633 /* Restrict the cursor position within the current view */
634 int_bounds(&x, box->x, box->x + box->width - 1);
635 int_bounds(&y, box->y, box->y + box->height - 1);
638 /* Scrolling could have changed the navigation mode */
639 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
641 link = get_link_at_coordinates(doc_view, x - box->x, y - box->y);
642 if (link) {
643 doc_view->vs->current_link = link - doc_view->document->links;
644 } else {
645 doc_view->vs->current_link = -1;
648 /* Set the unblockable cursor position and update the window pointer so
649 * stuff like the link menu will be drawn relative to the cursor. */
650 set_cursor(term, x, y, 0);
651 set_window_ptr(ses->tab, x, y);
653 return status;
656 static enum frame_event_status
657 move_cursor_rel(struct session *ses, struct document_view *view,
658 int rx, int ry)
660 int count = eat_kbd_repeat_count(ses);
661 int x, y;
663 int_lower_bound(&count, 1);
665 x = ses->tab->x + rx*count;
666 y = ses->tab->y + ry*count;
667 return move_cursor(ses, view, x, y);
670 enum frame_event_status
671 move_cursor_left(struct session *ses, struct document_view *view)
673 return move_cursor_rel(ses, view, -1, 0);
676 enum frame_event_status
677 move_cursor_right(struct session *ses, struct document_view *view)
679 return move_cursor_rel(ses, view, 1, 0);
682 enum frame_event_status
683 move_cursor_up(struct session *ses, struct document_view *view)
685 return move_cursor_rel(ses, view, 0, -1);
688 enum frame_event_status
689 move_cursor_down(struct session *ses, struct document_view *view)
691 return move_cursor_rel(ses, view, 0, 1);
694 enum frame_event_status
695 move_link_vertical(struct session *ses, struct document_view *doc_view, int dir_y)
697 struct document *document;
698 struct view_state *vs;
699 struct box *box;
700 int y, y1;
702 assert(ses && doc_view && doc_view->vs && doc_view->document);
703 if_assert_failed return FRAME_EVENT_OK;
704 vs = doc_view->vs;
705 document = doc_view->document;
706 if (!document->lines1) return FRAME_EVENT_OK;
708 box = &doc_view->box;
709 y1 = vs->y + ses->tab->y - box->y;
710 y = y1 + dir_y;
711 if (dir_y < 0)
712 int_upper_bound(&y, document->height - 1);
713 else
714 int_lower_bound(&y, 0);
715 for (; dir_y > 0 ? y < document->height : y >= 0; y += dir_y) {
716 struct link *link = document->lines1[y];
718 if (!link) continue;
719 for (; link <= document->lines2[y]; link++) {
720 if (link->points[0].y == y) {
721 enum frame_event_status status = move_cursor_rel(ses,
722 doc_view, 0, y - y1);
724 if (vs->current_link != -1)
725 ses->navigate_mode = NAVIGATE_LINKWISE;
726 return status;
730 return FRAME_EVENT_OK;
733 enum frame_event_status
734 copy_current_link_to_clipboard(struct session *ses,
735 struct document_view *doc_view,
736 int xxx)
738 struct link *link;
739 struct uri *uri;
740 unsigned char *uristring;
742 link = get_current_link(doc_view);
743 if (!link) return FRAME_EVENT_OK;
745 uri = get_link_uri(ses, doc_view, link);
746 if (!uri) return FRAME_EVENT_OK;
748 uristring = get_uri_string(uri, URI_ORIGINAL);
749 done_uri(uri);
751 if (uristring) {
752 set_clipboard_text(uristring);
753 mem_free(uristring);
756 return FRAME_EVENT_OK;
761 try_jump_to_link_number(struct session *ses, struct document_view *doc_view)
763 int link_number = eat_kbd_repeat_count(ses) - 1;
765 if (link_number < 0) return 1;
767 if (!doc_view) return 0;
769 if (link_number >= doc_view->document->nlinks)
770 return 0;
772 jump_to_link_number(ses, doc_view, link_number);
773 refresh_view(ses, doc_view, 0);
775 return 1;
778 #ifdef CONFIG_MARKS
779 enum frame_event_status
780 try_mark_key(struct session *ses, struct document_view *doc_view,
781 struct term_event *ev)
783 term_event_key_T key = get_kbd_key(ev);
784 unsigned char mark;
786 /* set_mark and goto_mark allow only a subset of the ASCII
787 * character repertoire as mark characters. If get_kbd_key(ev)
788 * is something else (i.e. a special key or a non-ASCII
789 * character), map it to an ASCII character that the functions
790 * will not accept, so the results are consistent.
791 * When CONFIG_UTF8 is not defined, this assumes that codes
792 * 0 to 0x7F in all codepages match ASCII. */
793 if (key >= 0 && key <= 0x7F)
794 mark = (unsigned char) key;
795 else
796 mark = 0;
798 switch (ses->kbdprefix.mark) {
799 case KP_MARK_NOTHING:
800 return FRAME_EVENT_IGNORED;
802 case KP_MARK_SET:
803 /* It is intentional to set the mark
804 * to NULL if !doc_view->vs. */
805 set_mark(mark, doc_view->vs);
806 break;
808 case KP_MARK_GOTO:
809 goto_mark(mark, doc_view->vs);
810 break;
813 ses->kbdprefix.repeat_count = 0;
814 ses->kbdprefix.mark = KP_MARK_NOTHING;
816 return FRAME_EVENT_REFRESH;
818 #endif
820 static enum frame_event_status
821 try_prefix_key(struct session *ses, struct document_view *doc_view,
822 struct term_event *ev)
824 struct document *document = doc_view->document;
825 struct document_options *doc_opts = &document->options;
826 int digit = get_kbd_key(ev) - '0';
828 if (digit < 0 || digit > 9)
829 return FRAME_EVENT_IGNORED;
831 if (get_kbd_modifier(ev)
832 || ses->kbdprefix.repeat_count /* The user has already begun
833 * entering a prefix. */
834 || !doc_opts->num_links_key
835 || (doc_opts->num_links_key == 1 && !doc_opts->links_numbering)) {
836 /* Repeat count.
837 * ses->kbdprefix.repeat_count is initialized to zero
838 * the first time by init_session() calloc() call.
839 * When used, it has to be reset to zero. */
841 /* Clear the highlighting for the previous partial prefix. */
842 if (ses->kbdprefix.repeat_count) draw_formatted(ses, 0);
844 ses->kbdprefix.repeat_count *= 10;
845 ses->kbdprefix.repeat_count += digit;
847 /* If too big, just restart from zero, so pressing
848 * '0' six times or more will reset the count. */
849 if (ses->kbdprefix.repeat_count > 99999)
850 ses->kbdprefix.repeat_count = 0;
851 else if (ses->kbdprefix.repeat_count)
852 highlight_links_with_prefixes_that_start_with_n(
853 ses->tab->term, doc_view,
854 ses->kbdprefix.repeat_count);
856 return FRAME_EVENT_OK;
859 if (digit >= 1 && !get_kbd_modifier(ev)) {
860 int nlinks = document->nlinks, length;
861 unsigned char d[2] = { get_kbd_key(ev), 0 };
863 ses->kbdprefix.repeat_count = 0;
865 if (!nlinks) return FRAME_EVENT_OK;
867 for (length = 1; nlinks; nlinks /= 10)
868 length++;
870 input_dialog(ses->tab->term, NULL,
871 N_("Go to link"), N_("Enter link number"),
872 ses, NULL,
873 length, d, 1, document->nlinks, check_number,
874 (void (*)(void *, unsigned char *)) goto_link_number, NULL);
876 return FRAME_EVENT_OK;
879 return FRAME_EVENT_IGNORED;
882 static enum frame_event_status
883 try_form_insert_mode(struct session *ses, struct document_view *doc_view,
884 struct link *link, struct term_event *ev)
886 enum frame_event_status status = FRAME_EVENT_IGNORED;
887 enum edit_action action_id;
889 if (!link_is_textinput(link))
890 return FRAME_EVENT_IGNORED;
892 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
894 if (ses->insert_mode == INSERT_MODE_OFF) {
895 if (action_id == ACT_EDIT_ENTER) {
896 ses->insert_mode = INSERT_MODE_ON;
897 status = FRAME_EVENT_REFRESH;
901 return status;
904 static enum frame_event_status
905 try_form_action(struct session *ses, struct document_view *doc_view,
906 struct link *link, struct term_event *ev)
908 enum frame_event_status status;
910 assert(link);
912 if (!link_is_textinput(link))
913 return FRAME_EVENT_IGNORED;
915 status = field_op(ses, doc_view, link, ev);
917 if (status != FRAME_EVENT_IGNORED
918 && ses->insert_mode == INSERT_MODE_ON) {
919 assert(link == get_current_link(doc_view));
922 return status;
925 static enum frame_event_status
926 frame_ev_kbd(struct session *ses, struct document_view *doc_view, struct term_event *ev)
928 enum frame_event_status status = FRAME_EVENT_IGNORED;
929 int accesskey_priority;
930 struct link *link = get_current_link(doc_view);
932 if (link) {
933 status = try_form_insert_mode(ses, doc_view, link, ev);
934 if (status != FRAME_EVENT_IGNORED)
935 return status;
937 status = try_form_action(ses, doc_view, link, ev);
938 if (status != FRAME_EVENT_IGNORED)
939 return status;
942 #ifdef CONFIG_MARKS
943 status = try_mark_key(ses, doc_view, ev);
944 if (status != FRAME_EVENT_IGNORED)
945 return status;
946 #endif
947 accesskey_priority = get_opt_int("document.browse.accesskey.priority");
949 if (accesskey_priority >= 2) {
950 status = try_document_key(ses, doc_view, ev);
952 if (status != FRAME_EVENT_IGNORED) {
953 /* The document ate the key! */
954 return status;
958 status = try_prefix_key(ses, doc_view, ev);
959 if (status != FRAME_EVENT_IGNORED)
960 return status;
962 if (accesskey_priority == 1) {
963 status = try_document_key(ses, doc_view, ev);
965 if (status != FRAME_EVENT_IGNORED) {
966 /* The document ate the key! */
967 return status;
971 return FRAME_EVENT_IGNORED;
974 #ifdef CONFIG_MOUSE
975 static enum frame_event_status
976 frame_ev_mouse(struct session *ses, struct document_view *doc_view, struct term_event *ev)
978 int x = ev->info.mouse.x;
979 int y = ev->info.mouse.y;
980 struct link *link;
982 if (check_mouse_wheel(ev)) {
983 if (!check_mouse_action(ev, B_DOWN)) {
984 /* We handle only B_DOWN case... */
985 } else if (check_mouse_button(ev, B_WHEEL_UP)) {
986 return scroll_mouse_up(ses, doc_view);
987 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
988 return scroll_mouse_down(ses, doc_view);
991 return FRAME_EVENT_OK;
994 link = get_link_at_coordinates(doc_view, x, y);
995 if (link) {
996 enum frame_event_status status = FRAME_EVENT_REFRESH;
998 doc_view->vs->current_link = link - doc_view->document->links;
1000 if (!link_is_textinput(link)) {
1002 status = FRAME_EVENT_OK;
1004 refresh_view(ses, doc_view, 0);
1006 if (check_mouse_button(ev, B_LEFT)
1007 || check_mouse_button(ev, B_MIDDLE)) {
1008 if (check_mouse_action(ev, B_DOWN))
1009 do_not_ignore_next_mouse_event(ses->tab->term);
1010 else if (check_mouse_button(ev, B_LEFT))
1011 status = enter(ses, doc_view, 0);
1012 else if (check_mouse_button(ev, B_MIDDLE))
1013 open_current_link_in_new_tab(ses, 1);
1014 } else {
1015 link_menu(ses->tab->term, NULL, ses);
1019 return status;
1022 if (check_mouse_button(ev, B_LEFT)) {
1023 /* Clicking the edge of screen will scroll the document. */
1025 int scrollmargin = get_opt_int("document.browse.scrolling.margin");
1027 /* XXX: This is code duplication with kbd handlers. But
1028 * repeatcount-free here. */
1030 if (y < scrollmargin) {
1031 return scroll_mouse_up(ses, doc_view);
1033 if (y >= doc_view->box.height - scrollmargin) {
1034 return scroll_mouse_down(ses, doc_view);
1037 if (x < scrollmargin * 2) {
1038 return scroll_mouse_left(ses, doc_view);
1040 if (x >= doc_view->box.width - scrollmargin * 2) {
1041 return scroll_mouse_right(ses, doc_view);
1044 return FRAME_EVENT_OK;
1047 return FRAME_EVENT_IGNORED;
1049 #endif /* CONFIG_MOUSE */
1051 static enum frame_event_status
1052 frame_ev(struct session *ses, struct document_view *doc_view, struct term_event *ev)
1054 assertm(doc_view && doc_view->document, "document not formatted");
1055 if_assert_failed return FRAME_EVENT_IGNORED;
1057 assert(ses && ev);
1058 if_assert_failed return FRAME_EVENT_IGNORED;
1060 /* When changing frame, vs may be NULL. See bug 525. */
1061 if (!doc_view->vs) return FRAME_EVENT_IGNORED;
1063 switch (ev->ev) {
1064 case EVENT_KBD:
1065 return frame_ev_kbd(ses, doc_view, ev);
1066 #ifdef CONFIG_MOUSE
1067 case EVENT_MOUSE:
1068 return frame_ev_mouse(ses, doc_view, ev);
1069 #endif /* CONFIG_MOUSE */
1070 default:
1071 return FRAME_EVENT_IGNORED;
1075 struct document_view *
1076 current_frame(struct session *ses)
1078 struct document_view *doc_view = NULL;
1079 int current_frame_number;
1081 assert(ses);
1082 if_assert_failed return NULL;
1084 if (!have_location(ses)) return NULL;
1086 current_frame_number = cur_loc(ses)->vs.current_link;
1087 if (current_frame_number == -1) current_frame_number = 0;
1089 foreach (doc_view, ses->scrn_frames) {
1090 if (document_has_frames(doc_view->document)) continue;
1091 if (!current_frame_number--) return doc_view;
1094 doc_view = ses->doc_view;
1096 assert(doc_view && doc_view->document);
1097 if_assert_failed return NULL;
1099 if (document_has_frames(doc_view->document)) return NULL;
1100 return doc_view;
1103 static enum frame_event_status
1104 send_to_frame(struct session *ses, struct document_view *doc_view,
1105 struct term_event *ev)
1107 enum frame_event_status status;
1109 assert(ses && ses->tab && ses->tab->term && ev);
1110 if_assert_failed return FRAME_EVENT_IGNORED;
1112 status = frame_ev(ses, doc_view, ev);
1114 if (status == FRAME_EVENT_REFRESH)
1115 refresh_view(ses, doc_view, 0);
1116 else
1117 print_screen_status(ses);
1119 return status;
1122 #ifdef CONFIG_MOUSE
1123 static int
1124 do_mouse_event(struct session *ses, struct term_event *ev,
1125 struct document_view *doc_view)
1127 struct term_event evv;
1128 struct document_view *matched = NULL, *first = doc_view;
1130 assert(ses && ev);
1131 if_assert_failed return 0;
1133 if (!doc_view) return 0;
1135 do {
1136 assert(doc_view && doc_view->document);
1137 if_assert_failed return 0;
1139 assertm(doc_view->document->options.box.x == doc_view->box.x
1140 && doc_view->document->options.box.y == doc_view->box.y,
1141 "Jonas' 1.565 -> 1.566 patch sucks");
1142 if_assert_failed return 0;
1144 if (check_mouse_position(ev, &doc_view->box)) {
1145 matched = doc_view;
1146 break;
1149 next_frame(ses, 1);
1150 doc_view = current_frame(ses);
1152 } while (doc_view != first);
1154 if (!matched) return 0;
1156 if (doc_view != first) draw_formatted(ses, 0);
1158 set_mouse_term_event(&evv,
1159 ev->info.mouse.x - doc_view->box.x,
1160 ev->info.mouse.y - doc_view->box.y,
1161 ev->info.mouse.button);
1163 return send_to_frame(ses, doc_view, &evv);
1166 static int
1167 is_mouse_on_tab_bar(struct session *ses, struct term_event_mouse *mouse)
1169 struct terminal *term = ses->tab->term;
1170 int y;
1172 if (ses->status.show_tabs_bar_at_top) y = ses->status.show_title_bar;
1173 else y = term->height - 1 - !!ses->status.show_status_bar;
1175 return mouse->y == y;
1177 /** @returns the session if event cleanup should be done or NULL if no
1178 * cleanup is needed. */
1179 static struct session *
1180 send_mouse_event(struct session *ses, struct document_view *doc_view,
1181 struct term_event *ev)
1183 struct terminal *term = ses->tab->term;
1184 struct term_event_mouse *mouse = &ev->info.mouse;
1186 if (mouse->y == 0
1187 && check_mouse_action(ev, B_DOWN)
1188 && !check_mouse_wheel(ev)) {
1189 struct window *m;
1191 activate_bfu_technology(ses, -1);
1192 m = term->windows.next;
1193 m->handler(m, ev);
1195 return ses;
1198 /* Handle tabs navigation if tabs bar is displayed. */
1199 if (ses->status.show_tabs_bar && is_mouse_on_tab_bar(ses, mouse)) {
1200 int tab_num = get_tab_number_by_xpos(term, mouse->x);
1201 struct window *current_tab = get_current_tab(term);
1203 if (check_mouse_action(ev, B_UP)) {
1204 if (check_mouse_button(ev, B_MIDDLE)
1205 && term->current_tab == tab_num
1206 && mouse->y == term->prev_mouse_event.y) {
1207 if (current_tab->data == ses) ses = NULL;
1209 close_tab(term, current_tab->data);
1212 return ses;
1215 if (check_mouse_button(ev, B_WHEEL_UP)) {
1216 switch_current_tab(ses, -1);
1218 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
1219 switch_current_tab(ses, 1);
1221 } else if (tab_num != -1) {
1222 switch_to_tab(term, tab_num, -1);
1224 if (check_mouse_button(ev, B_MIDDLE)) {
1225 do_not_ignore_next_mouse_event(term);
1226 } else if (check_mouse_button(ev, B_RIGHT)) {
1227 tab_menu(current_tab->data, mouse->x, mouse->y, 1);
1231 return ses;
1234 if (!do_mouse_event(ses, ev, doc_view)
1235 && check_mouse_button(ev, B_RIGHT)) {
1236 tab_menu(ses, mouse->x, mouse->y, 0);
1237 return NULL;
1240 #ifdef CONFIG_LEDS
1241 if (ses->status.show_leds
1242 && mouse->y == term->height - 1
1243 && mouse->x >= term->width - LEDS_COUNT - 3) {
1244 menu_leds_info(term, NULL, NULL);
1245 return NULL;
1247 #endif
1249 return NULL;
1251 #endif /* CONFIG_MOUSE */
1253 static void
1254 try_typeahead(struct session *ses, struct document_view *doc_view,
1255 struct term_event *ev, enum main_action action_id)
1257 switch (get_opt_int("document.browse.search.typeahead")) {
1258 case 0:
1259 return;
1260 case 1:
1261 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_LINK;
1262 break;
1263 case 2:
1264 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_TEXT;
1265 break;
1266 default:
1267 INTERNAL("invalid value for document.browse.search.typeahead");
1270 search_typeahead(ses, doc_view, action_id);
1272 /* Cross your fingers -- I'm just asking
1273 * for an infinite loop! -- Miciah */
1274 term_send_event(ses->tab->term, ev);
1277 /** @returns the session if event cleanup should be done or NULL if no
1278 * cleanup is needed. */
1279 static struct session *
1280 send_kbd_event(struct session *ses, struct document_view *doc_view,
1281 struct term_event *ev)
1283 int event;
1284 enum main_action action_id;
1286 if (doc_view && send_to_frame(ses, doc_view, ev) != FRAME_EVENT_IGNORED)
1287 return NULL;
1289 action_id = kbd_action(KEYMAP_MAIN, ev, &event);
1291 if (action_id == ACT_MAIN_QUIT) {
1292 if (check_kbd_key(ev, KBD_CTRL_C))
1293 quit:
1294 action_id = ACT_MAIN_REALLY_QUIT;
1297 switch (do_action(ses, action_id, 0)) {
1298 case FRAME_EVENT_SESSION_DESTROYED:
1299 return NULL;
1300 case FRAME_EVENT_IGNORED:
1301 break;
1302 case FRAME_EVENT_OK:
1303 case FRAME_EVENT_REFRESH:
1304 return ses;
1307 if (action_id == ACT_MAIN_SCRIPTING_FUNCTION) {
1308 #ifdef CONFIG_SCRIPTING
1309 trigger_event(event, ses);
1310 #endif
1311 return NULL;
1314 if (check_kbd_key(ev, KBD_CTRL_C)) goto quit;
1316 /* Ctrl-Alt-F should not open the File menu like Alt-f does. */
1317 if (check_kbd_modifier(ev, KBD_MOD_ALT)) {
1318 struct window *win;
1320 get_kbd_modifier(ev) &= ~KBD_MOD_ALT;
1321 activate_bfu_technology(ses, -1);
1322 win = ses->tab->term->windows.next;
1323 win->handler(win, ev);
1324 if (ses->tab->term->windows.next == win) {
1325 deselect_mainmenu(win->term, win->data);
1326 print_screen_status(ses);
1328 if (ses->tab != ses->tab->term->windows.next)
1329 return NULL;
1330 get_kbd_modifier(ev) |= KBD_MOD_ALT;
1332 if (doc_view
1333 && get_opt_int("document.browse.accesskey"
1334 ".priority") <= 0
1335 && try_document_key(ses, doc_view, ev)
1336 == FRAME_EVENT_REFRESH) {
1337 /* The document ate the key! */
1338 refresh_view(ses, doc_view, 0);
1340 return NULL;
1343 return ses;
1346 if (!(get_kbd_modifier(ev) & KBD_MOD_CTRL)) {
1347 if (doc_view) try_typeahead(ses, doc_view, ev, action_id);
1350 return NULL;
1353 void
1354 send_event(struct session *ses, struct term_event *ev)
1356 assert(ses && ev);
1357 if_assert_failed return;
1359 if (ev->ev == EVENT_KBD) {
1360 struct document_view *doc_view = current_frame(ses);
1362 ses = send_kbd_event(ses, doc_view, ev);
1364 #ifdef CONFIG_MOUSE
1365 else if (ev->ev == EVENT_MOUSE) {
1366 struct document_view *doc_view = current_frame(ses);
1368 ses = send_mouse_event(ses, doc_view, ev);
1370 #endif /* CONFIG_MOUSE */
1372 /* @ses may disappear ie. in close_tab() */
1373 if (ses) ses->kbdprefix.repeat_count = 0;
1376 enum frame_event_status
1377 download_link(struct session *ses, struct document_view *doc_view,
1378 action_id_T action_id)
1380 struct link *link = get_current_link(doc_view);
1381 void (*download)(void *ses, unsigned char *file) = start_download;
1383 if (!link) return FRAME_EVENT_OK;
1385 if (ses->download_uri) {
1386 done_uri(ses->download_uri);
1387 ses->download_uri = NULL;
1390 switch (action_id) {
1391 case ACT_MAIN_LINK_DOWNLOAD_RESUME:
1392 download = resume_download;
1393 case ACT_MAIN_LINK_DOWNLOAD:
1394 ses->download_uri = get_link_uri(ses, doc_view, link);
1395 break;
1397 case ACT_MAIN_LINK_DOWNLOAD_IMAGE:
1398 if (!link->where_img) break;
1399 ses->download_uri = get_uri(link->where_img, 0);
1400 break;
1402 default:
1403 INTERNAL("I think you forgot to take your medication, mental boy!");
1404 return FRAME_EVENT_OK;
1407 if (!ses->download_uri) return FRAME_EVENT_OK;
1409 set_session_referrer(ses, doc_view->document->uri);
1410 query_file(ses, ses->download_uri, ses, download, NULL, 1);
1412 return FRAME_EVENT_OK;
1415 enum frame_event_status
1416 view_image(struct session *ses, struct document_view *doc_view, int xxxx)
1418 struct link *link = get_current_link(doc_view);
1420 if (link && link->where_img)
1421 goto_url(ses, link->where_img);
1423 return FRAME_EVENT_OK;
1426 enum frame_event_status
1427 save_as(struct session *ses, struct document_view *doc_view, int magic)
1429 assert(ses);
1430 if_assert_failed return FRAME_EVENT_OK;
1432 if (!have_location(ses)) return FRAME_EVENT_OK;
1434 if (ses->download_uri) done_uri(ses->download_uri);
1435 ses->download_uri = get_uri_reference(cur_loc(ses)->vs.uri);
1437 assert(doc_view && doc_view->document && doc_view->document->uri);
1438 if_assert_failed return FRAME_EVENT_OK;
1440 set_session_referrer(ses, doc_view->document->uri);
1441 query_file(ses, ses->download_uri, ses, start_download, NULL, 1);
1443 return FRAME_EVENT_OK;
1446 static void
1447 save_formatted_finish(struct terminal *term, int h, void *data, int resume)
1449 struct document *document = data;
1451 assert(term && document);
1452 if_assert_failed return;
1454 if (h == -1) return;
1455 if (dump_to_file(document, h)) {
1456 info_box(term, 0, N_("Save error"), ALIGN_CENTER,
1457 N_("Error writing to file"));
1459 close(h);
1462 static void
1463 save_formatted(void *data, unsigned char *file)
1465 struct session *ses = data;
1466 struct document_view *doc_view;
1468 assert(ses && ses->tab && ses->tab->term && file);
1469 if_assert_failed return;
1470 doc_view = current_frame(ses);
1471 assert(doc_view && doc_view->document);
1472 if_assert_failed return;
1474 create_download_file(ses->tab->term, file, NULL, 0, 0,
1475 save_formatted_finish, doc_view->document);
1478 enum frame_event_status
1479 save_formatted_dlg(struct session *ses, struct document_view *doc_view, int xxxx)
1481 query_file(ses, doc_view->vs->uri, ses, save_formatted, NULL, 1);
1482 return FRAME_EVENT_OK;