1 /** HTML viewer (and much more)
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
);
67 detach_formatted(struct document_view
*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
);
77 if (doc_view
->document
) {
78 release_document(doc_view
->document
);
79 doc_view
->document
= NULL
;
82 doc_view
->vs
->doc_view
= NULL
;
85 mem_free_set(&doc_view
->name
, NULL
);
88 /*! @a type == 0 -> PAGE_DOWN;
89 * @a type == 1 -> DOWN */
91 move_down(struct session
*ses
, struct document_view
*doc_view
, int type
)
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
))
108 find_link_down(doc_view
);
110 find_link_page_down(doc_view
);
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 */
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
))
147 find_link_up(doc_view
);
149 find_link_page_up(doc_view
);
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
;
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
;
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
);
189 get_link_x_bounds(link
, y1
, &min_x
, &max_x
);
192 int_upper_bound(&y
, document
->height
- 1);
195 for (; y
>= 0; y
--, min_x
= INT_MAX
) {
196 link
= document
->lines1
[y
];
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
;
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
;
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
;
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
;
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
);
238 get_link_x_bounds(link
, y1
, &min_x
, &max_x
);
241 int_upper_bound(&y
, document
->height
- 1);
244 for (; y
< document
->height
; y
++, min_x
= -1) {
245 link
= document
->lines1
[y
];
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
;
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
;
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
)
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;
280 /* We only bother this option if there's some links
282 wraparound
= get_opt_bool("document.browse.links.wraparound");
285 count
= eat_kbd_repeat_count(ses
);
288 int current_link
= doc_view
->vs
->current_link
;
290 if (current_link
== wraparound_bound
) {
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 */
300 if (next_link_in_view_y(doc_view
, current_link
+ direction
,
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
315 doc_view
->vs
->current_link
= current_link
;
318 move_down(ses
, doc_view
, 1);
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
)
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
);
345 int current_link
= doc_view
->vs
->current_link
;
347 if (next_link_in_dir(doc_view
, dir_x
, dir_y
))
350 /* FIXME: This won't preserve the column! */
352 move_down(ses
, doc_view
, 1);
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
)
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
;
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
;
388 if (current_link_is_visible(doc_view
))
389 return FRAME_EVENT_REFRESH
;
392 find_link_page_down(doc_view
);
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
)
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;
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
;
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
);
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
);
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
);
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
);
469 steps
= get_opt_int("document.browse.scrolling.horizontal_step");
471 return horizontal_scroll(ses
, doc_view
, steps
);
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
);
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
)
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
;
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
);
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
;
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;
573 nowhere_box(ses
->tab
->term
, NULL
);
577 doc_view
->vs
->plain
= !doc_view
->vs
->plain
;
578 draw_formatted(ses
, 1);
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;
588 nowhere_box(ses
->tab
->term
, NULL
);
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
;
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
;
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
)
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
);
643 doc_view
->vs
->current_link
= link
- doc_view
->document
->links
;
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
);
656 static enum frame_event_status
657 move_cursor_rel(struct session
*ses
, struct document_view
*view
,
660 int count
= eat_kbd_repeat_count(ses
);
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
;
702 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
703 if_assert_failed
return FRAME_EVENT_OK
;
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
;
712 int_upper_bound(&y
, document
->height
- 1);
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
];
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
;
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
,
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
);
752 set_clipboard_text(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
)
772 jump_to_link_number(ses
, doc_view
, link_number
);
773 refresh_view(ses
, doc_view
, 0);
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
);
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
;
798 switch (ses
->kbdprefix
.mark
) {
799 case KP_MARK_NOTHING
:
800 return FRAME_EVENT_IGNORED
;
803 /* It is intentional to set the mark
804 * to NULL if !doc_view->vs. */
805 set_mark(mark
, doc_view
->vs
);
809 goto_mark(mark
, doc_view
->vs
);
813 ses
->kbdprefix
.repeat_count
= 0;
814 ses
->kbdprefix
.mark
= KP_MARK_NOTHING
;
816 return FRAME_EVENT_REFRESH
;
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
)) {
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)
870 input_dialog(ses
->tab
->term
, NULL
,
871 N_("Go to link"), N_("Enter link number"),
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
;
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
;
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
));
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
);
933 status
= try_form_insert_mode(ses
, doc_view
, link
, ev
);
934 if (status
!= FRAME_EVENT_IGNORED
)
937 status
= try_form_action(ses
, doc_view
, link
, ev
);
938 if (status
!= FRAME_EVENT_IGNORED
)
943 status
= try_mark_key(ses
, doc_view
, ev
);
944 if (status
!= FRAME_EVENT_IGNORED
)
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! */
958 status
= try_prefix_key(ses
, doc_view
, ev
);
959 if (status
!= FRAME_EVENT_IGNORED
)
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! */
971 return FRAME_EVENT_IGNORED
;
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
;
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
);
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);
1015 link_menu(ses
->tab
->term
, NULL
, ses
);
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
;
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
;
1065 return frame_ev_kbd(ses
, doc_view
, ev
);
1068 return frame_ev_mouse(ses
, doc_view
, ev
);
1069 #endif /* CONFIG_MOUSE */
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
;
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
;
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);
1117 print_screen_status(ses
);
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
;
1131 if_assert_failed
return 0;
1133 if (!doc_view
) return 0;
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
)) {
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
);
1167 is_mouse_on_tab_bar(struct session
*ses
, struct term_event_mouse
*mouse
)
1169 struct terminal
*term
= ses
->tab
->term
;
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
;
1187 && check_mouse_action(ev
, B_DOWN
)
1188 && !check_mouse_wheel(ev
)) {
1191 activate_bfu_technology(ses
, -1);
1192 m
= term
->windows
.next
;
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
);
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);
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);
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
);
1251 #endif /* CONFIG_MOUSE */
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")) {
1261 action_id
= ACT_MAIN_SEARCH_TYPEAHEAD_LINK
;
1264 action_id
= ACT_MAIN_SEARCH_TYPEAHEAD_TEXT
;
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
)
1284 enum main_action action_id
;
1286 if (doc_view
&& send_to_frame(ses
, doc_view
, ev
) != FRAME_EVENT_IGNORED
)
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
))
1294 action_id
= ACT_MAIN_REALLY_QUIT
;
1297 switch (do_action(ses
, action_id
, 0)) {
1298 case FRAME_EVENT_SESSION_DESTROYED
:
1300 case FRAME_EVENT_IGNORED
:
1302 case FRAME_EVENT_OK
:
1303 case FRAME_EVENT_REFRESH
:
1307 if (action_id
== ACT_MAIN_SCRIPTING_FUNCTION
) {
1308 #ifdef CONFIG_SCRIPTING
1309 trigger_event(event
, ses
);
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
)) {
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
)
1330 get_kbd_modifier(ev
) |= KBD_MOD_ALT
;
1333 && get_opt_int("document.browse.accesskey"
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);
1346 if (!(get_kbd_modifier(ev
) & KBD_MOD_CTRL
)) {
1347 if (doc_view
) try_typeahead(ses
, doc_view
, ev
, action_id
);
1354 send_event(struct session
*ses
, struct term_event
*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
);
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
);
1397 case ACT_MAIN_LINK_DOWNLOAD_IMAGE
:
1398 if (!link
->where_img
) break;
1399 ses
->download_uri
= get_uri(link
->where_img
, 0);
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
)
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
;
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"));
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
;