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 detach_formatted(struct document_view
*doc_view
)
67 if_assert_failed
return;
69 #ifdef CONFIG_ECMASCRIPT
70 if (doc_view
->session
) {
71 mem_free_set(&doc_view
->session
->status
.window_status
, NULL
);
74 if (doc_view
->document
) {
75 release_document(doc_view
->document
);
76 doc_view
->document
= NULL
;
79 doc_view
->vs
->doc_view
= NULL
;
82 mem_free_set(&doc_view
->name
, NULL
);
85 /*! @a type == 0 -> PAGE_DOWN;
86 * @a type == 1 -> DOWN */
88 move_down(struct session
*ses
, struct document_view
*doc_view
, int type
)
92 assert(ses
&& doc_view
&& doc_view
->vs
);
93 if_assert_failed
return;
95 assert(ses
->navigate_mode
== NAVIGATE_LINKWISE
); /* XXX: drop it at some time. --Zas */
97 newpos
= doc_view
->vs
->y
+ doc_view
->box
.height
;
98 if (newpos
< doc_view
->document
->height
)
99 doc_view
->vs
->y
= newpos
;
101 if (current_link_is_visible(doc_view
))
105 find_link_down(doc_view
);
107 find_link_page_down(doc_view
);
112 enum frame_event_status
113 move_page_down(struct session
*ses
, struct document_view
*doc_view
)
115 int oldy
= doc_view
->vs
->y
;
116 int count
= eat_kbd_repeat_count(ses
);
118 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
120 do move_down(ses
, doc_view
, 0); while (--count
> 0);
122 return doc_view
->vs
->y
== oldy
? FRAME_EVENT_OK
: FRAME_EVENT_REFRESH
;
125 /*! @a type == 0 -> PAGE_UP;
126 * @a type == 1 -> UP */
128 move_up(struct session
*ses
, struct document_view
*doc_view
, int type
)
130 assert(ses
&& doc_view
&& doc_view
->vs
);
131 if_assert_failed
return;
133 assert(ses
->navigate_mode
== NAVIGATE_LINKWISE
); /* XXX: drop it at some time. --Zas */
135 if (doc_view
->vs
->y
== 0) return;
137 doc_view
->vs
->y
-= doc_view
->box
.height
;
138 int_lower_bound(&doc_view
->vs
->y
, 0);
140 if (current_link_is_visible(doc_view
))
144 find_link_up(doc_view
);
146 find_link_page_up(doc_view
);
151 enum frame_event_status
152 move_page_up(struct session
*ses
, struct document_view
*doc_view
)
154 int oldy
= doc_view
->vs
->y
;
155 int count
= eat_kbd_repeat_count(ses
);
157 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
159 do move_up(ses
, doc_view
, 0); while (--count
> 0);
161 return doc_view
->vs
->y
== oldy
? FRAME_EVENT_OK
: FRAME_EVENT_REFRESH
;
165 enum frame_event_status
166 move_link(struct session
*ses
, struct document_view
*doc_view
, int direction
,
167 int wraparound_bound
, int wraparound_link
)
172 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
173 if_assert_failed
return FRAME_EVENT_OK
;
175 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
177 if (doc_view
->document
->nlinks
< 2) {
178 /* Wraparound only makes sense with more than one link. */
179 wraparound_bound
= -1;
181 /* We only bother this option if there's some links
183 wraparound
= get_opt_bool("document.browse.links.wraparound", ses
);
186 count
= eat_kbd_repeat_count(ses
);
189 int current_link
= doc_view
->vs
->current_link
;
191 if (current_link
== wraparound_bound
) {
193 jump_to_link_number(ses
, doc_view
, wraparound_link
);
194 /* FIXME: This needs further work, we should call
195 * page_down() and set_textarea() under some conditions
196 * as well. --pasky */
201 if (next_link_in_view_y(doc_view
, current_link
+ direction
,
206 /* This is a work around for the case where the index of
207 * @wraparound_bound is not necessarily the index of the first
208 * or the last link in the view. It means that the link moving
209 * could end up calling next_link_in_view() in the condition
210 * above. This is bad because next_link_in_view() will then
211 * 'reset' doc_view->vs->current_link to -1 and the effect will
212 * be that the current link will 'wrap around'. By restoring
213 * the index of the @current_link nothing will be wrapped
214 * around and move_{up,down} will take care of finding the next
216 doc_view
->vs
->current_link
= current_link
;
219 move_down(ses
, doc_view
, 1);
221 move_up(ses
, doc_view
, 1);
224 if (current_link
!= wraparound_bound
225 && current_link
!= doc_view
->vs
->current_link
) {
226 set_textarea(doc_view
, -direction
);
228 } while (--count
> 0);
230 return FRAME_EVENT_REFRESH
;
233 enum frame_event_status
234 move_link_dir(struct session
*ses
, struct document_view
*doc_view
, int dir_x
, int dir_y
)
238 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
239 if_assert_failed
return FRAME_EVENT_OK
;
241 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
243 count
= eat_kbd_repeat_count(ses
);
246 int current_link
= doc_view
->vs
->current_link
;
248 if (next_link_in_dir(doc_view
, dir_x
, dir_y
))
251 /* FIXME: This won't preserve the column! */
253 move_down(ses
, doc_view
, 1);
255 move_up(ses
, doc_view
, 1);
257 if (dir_y
&& current_link
!= doc_view
->vs
->current_link
) {
258 set_textarea(doc_view
, -dir_y
);
260 } while (--count
> 0);
262 return FRAME_EVENT_REFRESH
;
265 /*! @a steps > 0 -> down */
266 static enum frame_event_status
267 vertical_scroll(struct session
*ses
, struct document_view
*doc_view
, int steps
)
271 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
272 if_assert_failed
return FRAME_EVENT_OK
;
274 y
= doc_view
->vs
->y
+ steps
;
277 int max_height
= doc_view
->document
->height
- doc_view
->box
.height
;
279 if (doc_view
->vs
->y
>= max_height
) return FRAME_EVENT_OK
;
280 int_upper_bound(&y
, max_height
);
283 int_lower_bound(&y
, 0);
285 if (doc_view
->vs
->y
== y
) return FRAME_EVENT_OK
;
289 if (current_link_is_visible(doc_view
))
290 return FRAME_EVENT_REFRESH
;
293 find_link_page_down(doc_view
);
295 find_link_page_up(doc_view
);
297 return FRAME_EVENT_REFRESH
;
300 /*! @a steps > 0 -> right */
301 static enum frame_event_status
302 horizontal_scroll(struct session
*ses
, struct document_view
*doc_view
, int steps
)
306 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
307 if_assert_failed
return FRAME_EVENT_OK
;
309 x
= doc_view
->vs
->x
+ steps
;
311 if (get_opt_bool("document.browse.scrolling.horizontal_extended", ses
)) {
312 max
= doc_view
->document
->width
- 1;
314 max
= int_max(doc_view
->vs
->x
,
315 doc_view
->document
->width
- doc_view
->box
.width
);
318 int_bounds(&x
, 0, max
);
319 if (doc_view
->vs
->x
== x
) return FRAME_EVENT_OK
;
323 if (current_link_is_visible(doc_view
))
324 return FRAME_EVENT_REFRESH
;
326 find_link_page_down(doc_view
);
328 return FRAME_EVENT_REFRESH
;
331 enum frame_event_status
332 scroll_up(struct session
*ses
, struct document_view
*doc_view
)
334 int steps
= eat_kbd_repeat_count(ses
);
337 steps
= get_opt_int("document.browse.scrolling.vertical_step", ses
);
339 return vertical_scroll(ses
, doc_view
, -steps
);
342 enum frame_event_status
343 scroll_down(struct session
*ses
, struct document_view
*doc_view
)
345 int steps
= eat_kbd_repeat_count(ses
);
348 steps
= get_opt_int("document.browse.scrolling.vertical_step", ses
);
350 return vertical_scroll(ses
, doc_view
, steps
);
353 enum frame_event_status
354 scroll_left(struct session
*ses
, struct document_view
*doc_view
)
356 int steps
= eat_kbd_repeat_count(ses
);
359 steps
= get_opt_int("document.browse.scrolling.horizontal_step", ses
);
361 return horizontal_scroll(ses
, doc_view
, -steps
);
364 enum frame_event_status
365 scroll_right(struct session
*ses
, struct document_view
*doc_view
)
367 int steps
= eat_kbd_repeat_count(ses
);
370 steps
= get_opt_int("document.browse.scrolling.horizontal_step", ses
);
372 return horizontal_scroll(ses
, doc_view
, steps
);
376 static enum frame_event_status
377 scroll_mouse_up(struct session
*ses
, struct document_view
*doc_view
)
379 int steps
= get_opt_int("document.browse.scrolling.vertical_step", ses
);
381 return vertical_scroll(ses
, doc_view
, -steps
);
384 static enum frame_event_status
385 scroll_mouse_down(struct session
*ses
, struct document_view
*doc_view
)
387 int steps
= get_opt_int("document.browse.scrolling.vertical_step", ses
);
389 return vertical_scroll(ses
, doc_view
, steps
);
392 static enum frame_event_status
393 scroll_mouse_left(struct session
*ses
, struct document_view
*doc_view
)
395 int steps
= get_opt_int("document.browse.scrolling.horizontal_step", ses
);
397 return horizontal_scroll(ses
, doc_view
, -steps
);
400 static enum frame_event_status
401 scroll_mouse_right(struct session
*ses
, struct document_view
*doc_view
)
403 int steps
= get_opt_int("document.browse.scrolling.horizontal_step", ses
);
405 return horizontal_scroll(ses
, doc_view
, steps
);
407 #endif /* CONFIG_MOUSE */
409 enum frame_event_status
410 move_document_start(struct session
*ses
, struct document_view
*doc_view
)
412 assert(ses
&& doc_view
&& doc_view
->vs
);
413 if_assert_failed
return FRAME_EVENT_OK
;
415 doc_view
->vs
->y
= doc_view
->vs
->x
= 0;
417 if (ses
->navigate_mode
== NAVIGATE_CURSOR_ROUTING
) {
418 /* Move to the first line and the first column. */
419 move_cursor(ses
, doc_view
, doc_view
->box
.x
, doc_view
->box
.y
);
421 find_link_page_down(doc_view
);
424 return FRAME_EVENT_REFRESH
;
427 enum frame_event_status
428 move_document_end(struct session
*ses
, struct document_view
*doc_view
)
432 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
433 if_assert_failed
return FRAME_EVENT_OK
;
435 max_height
= doc_view
->document
->height
- doc_view
->box
.height
;
437 int_lower_bound(&doc_view
->vs
->y
, int_max(0, max_height
));
439 if (ses
->navigate_mode
== NAVIGATE_CURSOR_ROUTING
) {
440 /* Move to the last line of the document,
441 * but preserve the column. This is done to avoid
442 * moving the cursor backwards if it is already
443 * on the last line but is not on the first column. */
444 move_cursor(ses
, doc_view
, ses
->tab
->x
,
445 doc_view
->document
->height
- doc_view
->vs
->y
);
447 find_link_page_up(doc_view
);
450 return FRAME_EVENT_REFRESH
;
453 enum frame_event_status
454 set_frame(struct session
*ses
, struct document_view
*doc_view
, int xxxx
)
456 assert(ses
&& ses
->doc_view
&& doc_view
&& doc_view
->vs
);
457 if_assert_failed
return FRAME_EVENT_OK
;
459 if (doc_view
== ses
->doc_view
) return FRAME_EVENT_OK
;
460 goto_uri(ses
, doc_view
->vs
->uri
);
461 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
463 return FRAME_EVENT_OK
;
468 toggle_plain_html(struct session
*ses
, struct document_view
*doc_view
, int xxxx
)
470 assert(ses
&& doc_view
&& ses
->tab
&& ses
->tab
->term
);
471 if_assert_failed
return;
474 nowhere_box(ses
->tab
->term
, NULL
);
478 doc_view
->vs
->plain
= !doc_view
->vs
->plain
;
479 draw_formatted(ses
, 1);
483 toggle_wrap_text(struct session
*ses
, struct document_view
*doc_view
, int xxxx
)
485 assert(ses
&& doc_view
&& ses
->tab
&& ses
->tab
->term
);
486 if_assert_failed
return;
489 nowhere_box(ses
->tab
->term
, NULL
);
493 doc_view
->vs
->wrap
= !doc_view
->vs
->wrap
;
494 draw_formatted(ses
, 1);
497 /** Move the cursor to the document view co-ordinates provided
498 * as @a x and @a y, scroll the document if necessary, put us in
499 * cursor-routing navigation mode if that is not the current mode,
500 * and select any link under the cursor. */
501 enum frame_event_status
502 move_cursor(struct session
*ses
, struct document_view
*doc_view
, int x
, int y
)
504 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
505 struct terminal
*term
= ses
->tab
->term
;
506 struct box
*box
= &doc_view
->box
;
509 /* If cursor was moved outside the document view scroll it, but only
510 * within the document canvas */
511 if (!is_in_box(box
, x
, y
)) {
512 int max_height
= doc_view
->document
->height
- doc_view
->vs
->y
;
513 int max_width
= doc_view
->document
->width
- doc_view
->vs
->x
;
516 status
= vertical_scroll(ses
, doc_view
, y
- box
->y
);
518 } else if (y
>= box
->y
+ box
->height
&& y
<= max_height
) {
519 status
= vertical_scroll(ses
, doc_view
,
520 y
- (box
->y
+ box
->height
- 1));
522 } else if (x
< box
->x
) {
523 status
= horizontal_scroll(ses
, doc_view
, x
- box
->x
);
525 } else if (x
>= box
->x
+ box
->width
&& x
<= max_width
) {
526 status
= horizontal_scroll(ses
, doc_view
,
527 x
- (box
->x
+ box
->width
- 1));
530 /* If the view was not scrolled there's nothing more to do */
531 if (status
!= FRAME_EVENT_REFRESH
)
534 /* Restrict the cursor position within the current view */
535 int_bounds(&x
, box
->x
, box
->x
+ box
->width
- 1);
536 int_bounds(&y
, box
->y
, box
->y
+ box
->height
- 1);
539 /* Scrolling could have changed the navigation mode */
540 ses
->navigate_mode
= NAVIGATE_CURSOR_ROUTING
;
542 link
= get_link_at_coordinates(doc_view
, x
- box
->x
, y
- box
->y
);
544 doc_view
->vs
->current_link
= link
- doc_view
->document
->links
;
546 doc_view
->vs
->current_link
= -1;
549 /* Set the unblockable cursor position and update the window pointer so
550 * stuff like the link menu will be drawn relative to the cursor. */
551 set_cursor(term
, x
, y
, 0);
552 set_window_ptr(ses
->tab
, x
, y
);
557 static enum frame_event_status
558 move_cursor_rel(struct session
*ses
, struct document_view
*view
,
561 int count
= eat_kbd_repeat_count(ses
);
564 int_lower_bound(&count
, 1);
566 x
= ses
->tab
->x
+ rx
*count
;
567 y
= ses
->tab
->y
+ ry
*count
;
568 return move_cursor(ses
, view
, x
, y
);
571 enum frame_event_status
572 move_cursor_left(struct session
*ses
, struct document_view
*view
)
574 return move_cursor_rel(ses
, view
, -1, 0);
577 enum frame_event_status
578 move_cursor_right(struct session
*ses
, struct document_view
*view
)
580 return move_cursor_rel(ses
, view
, 1, 0);
583 enum frame_event_status
584 move_cursor_up(struct session
*ses
, struct document_view
*view
)
586 return move_cursor_rel(ses
, view
, 0, -1);
589 enum frame_event_status
590 move_cursor_down(struct session
*ses
, struct document_view
*view
)
592 return move_cursor_rel(ses
, view
, 0, 1);
595 enum frame_event_status
596 move_link_up_line(struct session
*ses
, struct document_view
*doc_view
)
598 struct document
*document
;
599 struct view_state
*vs
;
603 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
604 if_assert_failed
return FRAME_EVENT_OK
;
606 document
= doc_view
->document
;
607 box
= &doc_view
->box
;
608 if (!document
->lines1
) {
610 vs
->y
-= box
->height
;
611 int_lower_bound(&vs
->y
, 0);
612 return FRAME_EVENT_REFRESH
;
614 return FRAME_EVENT_OK
;
616 min_y
= vs
->y
- box
->height
;
617 int_lower_bound(&min_y
, 0);
618 y1
= y
= vs
->y
+ ses
->tab
->y
- box
->y
;
619 int_upper_bound(&y
, document
->height
- 1);
620 for (y
--; y
>= min_y
; y
--) {
621 struct link
*link
= document
->lines1
[y
];
624 for (; link
<= document
->lines2
[y
]; link
++) {
625 enum frame_event_status status
;
627 if (link
->points
[0].y
!= y
) continue;
629 int mini
= int_min(box
->height
, vs
->y
);
634 status
= move_cursor_rel(ses
, doc_view
, 0, y
- y1
);
635 if (link
== get_current_link(doc_view
))
636 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
641 vs
->y
-= box
->height
;
642 int_lower_bound(&vs
->y
, 0);
643 return FRAME_EVENT_REFRESH
;
645 return FRAME_EVENT_OK
;
648 enum frame_event_status
649 move_link_down_line(struct session
*ses
, struct document_view
*doc_view
)
651 struct document
*document
;
652 struct view_state
*vs
;
656 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
657 if_assert_failed
return FRAME_EVENT_OK
;
659 document
= doc_view
->document
;
660 box
= &doc_view
->box
;
661 if (!document
->lines1
) {
662 if (vs
->y
+ box
->height
< document
->height
) {
663 vs
->y
+= box
->height
;
664 return FRAME_EVENT_REFRESH
;
666 return FRAME_EVENT_OK
;
668 max_y
= vs
->y
+ box
->height
* 2 - 1;
669 int_upper_bound(&max_y
, document
->height
- 1);
670 y1
= y
= vs
->y
+ ses
->tab
->y
- box
->y
;
671 for (y
++; y
<= max_y
; y
++) {
672 struct link
*link
= document
->lines1
[y
];
675 for (; link
<= document
->lines2
[y
]; link
++) {
676 enum frame_event_status status
;
678 if (link
->points
[0].y
!= y
) continue;
679 if (y
>= vs
->y
+ box
->height
) {
680 int mini
= int_min(box
->height
, document
->height
- vs
->y
- 1);
685 status
= move_cursor_rel(ses
, doc_view
, 0, y
- y1
);
686 if (link
== get_current_link(doc_view
))
687 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
691 if (vs
->y
+ box
->height
< document
->height
) {
692 vs
->y
+= box
->height
;
693 return FRAME_EVENT_REFRESH
;
695 return FRAME_EVENT_OK
;
698 enum frame_event_status
699 move_link_prev_line(struct session
*ses
, struct document_view
*doc_view
)
701 struct view_state
*vs
;
702 struct document
*document
;
704 struct link
*link
, *last
= NULL
;
705 int y1
, y
, min_y
, min_x
, max_x
, x1
;
707 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
708 if_assert_failed
return FRAME_EVENT_OK
;
711 document
= doc_view
->document
;
712 box
= &doc_view
->box
;
713 if (!document
->lines1
) {
715 vs
->y
-= box
->height
;
716 int_lower_bound(&vs
->y
, 0);
717 return FRAME_EVENT_REFRESH
;
719 return FRAME_EVENT_OK
;
721 y
= y1
= vs
->y
+ ses
->tab
->y
- box
->y
;
722 x1
= vs
->x
+ ses
->tab
->x
- box
->x
;
724 link
= get_current_link(doc_view
);
726 get_link_x_bounds(link
, y1
, &min_x
, &max_x
);
730 int_upper_bound(&y
, document
->height
- 1);
731 min_y
= int_max(0, vs
->y
- box
->height
);
733 for (; y
>= min_y
; y
--, min_x
= INT_MAX
) {
734 link
= document
->lines1
[y
];
736 for (; link
<= document
->lines2
[y
]; link
++) {
737 if (link
->points
[0].y
!= y
) continue;
738 if (link
->points
[0].x
>= min_x
) continue;
739 if (!last
) last
= link
;
740 else if (link
->points
[0].x
> last
->points
[0].x
) last
= link
;
743 enum frame_event_status status
;
745 y
= last
->points
[0].y
;
747 int mini
= int_min(box
->height
, vs
->y
);
752 status
= move_cursor_rel(ses
, doc_view
, last
->points
[0].x
- x1
, y
- y1
);
753 if (link
== get_current_link(doc_view
))
754 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
759 vs
->y
-= box
->height
;
760 int_lower_bound(&vs
->y
, 0);
761 return FRAME_EVENT_REFRESH
;
763 return FRAME_EVENT_OK
;
766 enum frame_event_status
767 move_link_next_line(struct session
*ses
, struct document_view
*doc_view
)
769 struct view_state
*vs
;
770 struct document
*document
;
772 struct link
*link
, *last
= NULL
;
773 int y1
, y
, max_y
, min_x
, max_x
, x1
;
775 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
776 if_assert_failed
return FRAME_EVENT_OK
;
779 document
= doc_view
->document
;
780 box
= &doc_view
->box
;
781 if (!document
->lines1
) {
782 if (vs
->y
+ box
->height
< document
->height
) {
783 vs
->y
+= box
->height
;
784 return FRAME_EVENT_REFRESH
;
786 return FRAME_EVENT_OK
;
788 y
= y1
= vs
->y
+ ses
->tab
->y
- box
->y
;
789 x1
= vs
->x
+ ses
->tab
->x
- box
->x
;
791 link
= get_current_link(doc_view
);
793 get_link_x_bounds(link
, y1
, &min_x
, &max_x
);
797 int_upper_bound(&y
, document
->height
- 1);
798 max_y
= int_min(vs
->y
+ 2 * box
->height
- 1, document
->height
- 1);
800 for (; y
<= max_y
; y
++, min_x
= -1) {
801 link
= document
->lines1
[y
];
803 for (; link
<= document
->lines2
[y
]; link
++) {
804 if (link
->points
[0].y
!= y
) continue;
805 if (link
->points
[0].x
<= min_x
) continue;
806 if (!last
) last
= link
;
807 else if (link
->points
[0].x
< last
->points
[0].x
) last
= link
;
810 enum frame_event_status status
;
812 y
= last
->points
[0].y
;
813 if (y
>= vs
->y
+ box
->height
) {
814 int mini
= int_min(box
->height
, document
->height
- vs
->y
- 1);
819 status
= move_cursor_rel(ses
, doc_view
, last
->points
[0].x
- x1
, y
- y1
);
820 if (link
== get_current_link(doc_view
))
821 ses
->navigate_mode
= NAVIGATE_LINKWISE
;
825 if (vs
->y
+ box
->height
< document
->height
) {
826 vs
->y
+= box
->height
;
827 return FRAME_EVENT_REFRESH
;
829 return FRAME_EVENT_OK
;
832 enum frame_event_status
833 copy_current_link_to_clipboard(struct session
*ses
,
834 struct document_view
*doc_view
,
839 unsigned char *uristring
;
841 link
= get_current_link(doc_view
);
842 if (!link
) return FRAME_EVENT_OK
;
844 uri
= get_link_uri(ses
, doc_view
, link
);
845 if (!uri
) return FRAME_EVENT_OK
;
847 uristring
= get_uri_string(uri
, URI_ORIGINAL
);
851 set_clipboard_text(uristring
);
855 return FRAME_EVENT_OK
;
860 try_jump_to_link_number(struct session
*ses
, struct document_view
*doc_view
)
862 int link_number
= eat_kbd_repeat_count(ses
) - 1;
864 if (link_number
< 0) return 1;
866 if (!doc_view
) return 0;
868 if (link_number
>= doc_view
->document
->nlinks
)
871 jump_to_link_number(ses
, doc_view
, link_number
);
872 refresh_view(ses
, doc_view
, 0);
878 enum frame_event_status
879 try_mark_key(struct session
*ses
, struct document_view
*doc_view
,
880 struct term_event
*ev
)
882 term_event_key_T key
= get_kbd_key(ev
);
885 /* set_mark and goto_mark allow only a subset of the ASCII
886 * character repertoire as mark characters. If get_kbd_key(ev)
887 * is something else (i.e. a special key or a non-ASCII
888 * character), map it to an ASCII character that the functions
889 * will not accept, so the results are consistent.
890 * When CONFIG_UTF8 is not defined, this assumes that codes
891 * 0 to 0x7F in all codepages match ASCII. */
892 if (key
>= 0 && key
<= 0x7F)
893 mark
= (unsigned char) key
;
897 switch (ses
->kbdprefix
.mark
) {
898 case KP_MARK_NOTHING
:
899 return FRAME_EVENT_IGNORED
;
902 /* It is intentional to set the mark
903 * to NULL if !doc_view->vs. */
904 set_mark(mark
, doc_view
->vs
);
908 goto_mark(mark
, doc_view
->vs
);
912 ses
->kbdprefix
.repeat_count
= 0;
913 ses
->kbdprefix
.mark
= KP_MARK_NOTHING
;
915 return FRAME_EVENT_REFRESH
;
919 static enum frame_event_status
920 try_prefix_key(struct session
*ses
, struct document_view
*doc_view
,
921 struct term_event
*ev
)
923 struct document
*document
= doc_view
->document
;
924 struct document_options
*doc_opts
= &document
->options
;
925 int digit
= get_kbd_key(ev
) - '0';
927 if (digit
< 0 || digit
> 9)
928 return FRAME_EVENT_IGNORED
;
930 if (get_kbd_modifier(ev
)
931 || ses
->kbdprefix
.repeat_count
/* The user has already begun
932 * entering a prefix. */
933 || !doc_opts
->num_links_key
934 || (doc_opts
->num_links_key
== 1 && !doc_opts
->links_numbering
)) {
936 * ses->kbdprefix.repeat_count is initialized to zero
937 * the first time by init_session() calloc() call.
938 * When used, it has to be reset to zero. */
940 /* Clear the highlighting for the previous partial prefix. */
941 if (ses
->kbdprefix
.repeat_count
) draw_formatted(ses
, 0);
943 ses
->kbdprefix
.repeat_count
*= 10;
944 ses
->kbdprefix
.repeat_count
+= digit
;
946 /* If too big, just restart from zero, so pressing
947 * '0' six times or more will reset the count. */
948 if (ses
->kbdprefix
.repeat_count
> 99999)
949 ses
->kbdprefix
.repeat_count
= 0;
950 else if (ses
->kbdprefix
.repeat_count
)
951 highlight_links_with_prefixes_that_start_with_n(
952 ses
->tab
->term
, doc_view
,
953 ses
->kbdprefix
.repeat_count
);
955 return FRAME_EVENT_OK
;
958 if (digit
>= 1 && !get_kbd_modifier(ev
)) {
959 int nlinks
= document
->nlinks
, length
;
960 unsigned char d
[2] = { get_kbd_key(ev
), 0 };
962 ses
->kbdprefix
.repeat_count
= 0;
964 if (!nlinks
) return FRAME_EVENT_OK
;
966 for (length
= 1; nlinks
; nlinks
/= 10)
969 input_dialog(ses
->tab
->term
, NULL
,
970 N_("Go to link"), N_("Enter link number"),
972 length
, d
, 1, document
->nlinks
, check_number
,
973 (void (*)(void *, unsigned char *)) goto_link_number
, NULL
);
975 return FRAME_EVENT_OK
;
978 return FRAME_EVENT_IGNORED
;
981 static enum frame_event_status
982 try_form_insert_mode(struct session
*ses
, struct document_view
*doc_view
,
983 struct link
*link
, struct term_event
*ev
)
985 enum frame_event_status status
= FRAME_EVENT_IGNORED
;
986 enum edit_action action_id
;
988 if (!link_is_textinput(link
))
989 return FRAME_EVENT_IGNORED
;
991 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
993 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
994 if (action_id
== ACT_EDIT_ENTER
) {
995 ses
->insert_mode
= INSERT_MODE_ON
;
996 status
= FRAME_EVENT_REFRESH
;
1003 static enum frame_event_status
1004 try_form_action(struct session
*ses
, struct document_view
*doc_view
,
1005 struct link
*link
, struct term_event
*ev
)
1007 enum frame_event_status status
;
1011 if (!link_is_textinput(link
))
1012 return FRAME_EVENT_IGNORED
;
1014 status
= field_op(ses
, doc_view
, link
, ev
);
1016 if (status
!= FRAME_EVENT_IGNORED
1017 && ses
->insert_mode
== INSERT_MODE_ON
) {
1018 assert(link
== get_current_link(doc_view
));
1024 static enum frame_event_status
1025 frame_ev_kbd(struct session
*ses
, struct document_view
*doc_view
, struct term_event
*ev
)
1027 enum frame_event_status status
= FRAME_EVENT_IGNORED
;
1028 int accesskey_priority
;
1029 struct link
*link
= get_current_link(doc_view
);
1032 status
= try_form_insert_mode(ses
, doc_view
, link
, ev
);
1033 if (status
!= FRAME_EVENT_IGNORED
)
1036 status
= try_form_action(ses
, doc_view
, link
, ev
);
1037 if (status
!= FRAME_EVENT_IGNORED
)
1042 status
= try_mark_key(ses
, doc_view
, ev
);
1043 if (status
!= FRAME_EVENT_IGNORED
)
1046 accesskey_priority
= get_opt_int("document.browse.accesskey.priority", ses
);
1048 if (accesskey_priority
>= 2) {
1049 status
= try_document_key(ses
, doc_view
, ev
);
1051 if (status
!= FRAME_EVENT_IGNORED
) {
1052 /* The document ate the key! */
1057 status
= try_prefix_key(ses
, doc_view
, ev
);
1058 if (status
!= FRAME_EVENT_IGNORED
)
1061 if (accesskey_priority
== 1) {
1062 status
= try_document_key(ses
, doc_view
, ev
);
1064 if (status
!= FRAME_EVENT_IGNORED
) {
1065 /* The document ate the key! */
1070 return FRAME_EVENT_IGNORED
;
1074 static enum frame_event_status
1075 frame_ev_mouse(struct session
*ses
, struct document_view
*doc_view
, struct term_event
*ev
)
1077 int x
= ev
->info
.mouse
.x
;
1078 int y
= ev
->info
.mouse
.y
;
1081 if (check_mouse_wheel(ev
)) {
1082 if (!check_mouse_action(ev
, B_DOWN
)) {
1083 /* We handle only B_DOWN case... */
1084 } else if (check_mouse_button(ev
, B_WHEEL_UP
)) {
1085 return scroll_mouse_up(ses
, doc_view
);
1086 } else if (check_mouse_button(ev
, B_WHEEL_DOWN
)) {
1087 return scroll_mouse_down(ses
, doc_view
);
1090 return FRAME_EVENT_OK
;
1093 link
= get_link_at_coordinates(doc_view
, x
, y
);
1095 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1097 doc_view
->vs
->current_link
= link
- doc_view
->document
->links
;
1099 if (!link_is_textinput(link
)) {
1101 status
= FRAME_EVENT_OK
;
1103 refresh_view(ses
, doc_view
, 0);
1105 if (check_mouse_button(ev
, B_LEFT
)
1106 || check_mouse_button(ev
, B_MIDDLE
)) {
1107 if (check_mouse_action(ev
, B_DOWN
))
1108 do_not_ignore_next_mouse_event(ses
->tab
->term
);
1109 else if (check_mouse_button(ev
, B_LEFT
))
1110 status
= enter(ses
, doc_view
, 0);
1111 else if (check_mouse_button(ev
, B_MIDDLE
))
1112 open_current_link_in_new_tab(ses
, 1);
1114 link_menu(ses
->tab
->term
, NULL
, ses
);
1121 if (check_mouse_button(ev
, B_LEFT
)) {
1122 /* Clicking the edge of screen will scroll the document. */
1124 int scrollmargin
= get_opt_int("document.browse.scrolling.margin", ses
);
1126 /* XXX: This is code duplication with kbd handlers. But
1127 * repeatcount-free here. */
1129 if (y
< scrollmargin
) {
1130 return scroll_mouse_up(ses
, doc_view
);
1132 if (y
>= doc_view
->box
.height
- scrollmargin
) {
1133 return scroll_mouse_down(ses
, doc_view
);
1136 if (x
< scrollmargin
* 2) {
1137 return scroll_mouse_left(ses
, doc_view
);
1139 if (x
>= doc_view
->box
.width
- scrollmargin
* 2) {
1140 return scroll_mouse_right(ses
, doc_view
);
1143 return FRAME_EVENT_OK
;
1146 return FRAME_EVENT_IGNORED
;
1148 #endif /* CONFIG_MOUSE */
1150 static enum frame_event_status
1151 frame_ev(struct session
*ses
, struct document_view
*doc_view
, struct term_event
*ev
)
1153 assertm(doc_view
&& doc_view
->document
, "document not formatted");
1154 if_assert_failed
return FRAME_EVENT_IGNORED
;
1157 if_assert_failed
return FRAME_EVENT_IGNORED
;
1159 /* When changing frame, vs may be NULL. See bug 525. */
1160 if (!doc_view
->vs
) return FRAME_EVENT_IGNORED
;
1164 return frame_ev_kbd(ses
, doc_view
, ev
);
1167 return frame_ev_mouse(ses
, doc_view
, ev
);
1168 #endif /* CONFIG_MOUSE */
1170 return FRAME_EVENT_IGNORED
;
1174 struct document_view
*
1175 current_frame(struct session
*ses
)
1177 struct document_view
*doc_view
= NULL
;
1178 int current_frame_number
;
1181 if_assert_failed
return NULL
;
1183 if (!have_location(ses
)) return NULL
;
1185 current_frame_number
= cur_loc(ses
)->vs
.current_link
;
1186 if (current_frame_number
== -1) current_frame_number
= 0;
1188 foreach (doc_view
, ses
->scrn_frames
) {
1189 if (document_has_frames(doc_view
->document
)) continue;
1190 if (!current_frame_number
--) return doc_view
;
1193 doc_view
= ses
->doc_view
;
1195 assert(doc_view
&& doc_view
->document
);
1196 if_assert_failed
return NULL
;
1198 if (document_has_frames(doc_view
->document
)) return NULL
;
1202 static enum frame_event_status
1203 send_to_frame(struct session
*ses
, struct document_view
*doc_view
,
1204 struct term_event
*ev
)
1206 enum frame_event_status status
;
1208 assert(ses
&& ses
->tab
&& ses
->tab
->term
&& ev
);
1209 if_assert_failed
return FRAME_EVENT_IGNORED
;
1211 status
= frame_ev(ses
, doc_view
, ev
);
1213 if (status
== FRAME_EVENT_REFRESH
)
1214 refresh_view(ses
, doc_view
, 0);
1216 print_screen_status(ses
);
1223 do_mouse_event(struct session
*ses
, struct term_event
*ev
,
1224 struct document_view
*doc_view
)
1226 struct term_event evv
;
1227 struct document_view
*matched
= NULL
, *first
= doc_view
;
1230 if_assert_failed
return 0;
1232 if (!doc_view
) return 0;
1235 assert(doc_view
&& doc_view
->document
);
1236 if_assert_failed
return 0;
1238 assertm(doc_view
->document
->options
.box
.x
== doc_view
->box
.x
1239 && doc_view
->document
->options
.box
.y
== doc_view
->box
.y
,
1240 "Jonas' 1.565 -> 1.566 patch sucks");
1241 if_assert_failed
return 0;
1243 if (check_mouse_position(ev
, &doc_view
->box
)) {
1249 doc_view
= current_frame(ses
);
1251 } while (doc_view
!= first
);
1253 if (!matched
) return 0;
1255 if (doc_view
!= first
) draw_formatted(ses
, 0);
1257 set_mouse_term_event(&evv
,
1258 ev
->info
.mouse
.x
- doc_view
->box
.x
,
1259 ev
->info
.mouse
.y
- doc_view
->box
.y
,
1260 ev
->info
.mouse
.button
);
1262 return send_to_frame(ses
, doc_view
, &evv
);
1266 is_mouse_on_tab_bar(struct session
*ses
, struct term_event_mouse
*mouse
)
1268 struct terminal
*term
= ses
->tab
->term
;
1271 if (ses
->status
.show_tabs_bar_at_top
) y
= ses
->status
.show_title_bar
;
1272 else y
= term
->height
- 1 - !!ses
->status
.show_status_bar
;
1274 return mouse
->y
== y
;
1276 /** @returns the session if event cleanup should be done or NULL if no
1277 * cleanup is needed. */
1278 static struct session
*
1279 send_mouse_event(struct session
*ses
, struct document_view
*doc_view
,
1280 struct term_event
*ev
)
1282 struct terminal
*term
= ses
->tab
->term
;
1283 struct term_event_mouse
*mouse
= &ev
->info
.mouse
;
1286 && check_mouse_action(ev
, B_DOWN
)
1287 && !check_mouse_wheel(ev
)) {
1290 activate_bfu_technology(ses
, -1);
1291 m
= term
->windows
.next
;
1297 /* Handle tabs navigation if tabs bar is displayed. */
1298 if (ses
->status
.show_tabs_bar
&& is_mouse_on_tab_bar(ses
, mouse
)) {
1299 int tab_num
= get_tab_number_by_xpos(term
, mouse
->x
);
1300 struct window
*current_tab
= get_current_tab(term
);
1302 if (check_mouse_action(ev
, B_UP
)) {
1303 if (check_mouse_button(ev
, B_MIDDLE
)
1304 && term
->current_tab
== tab_num
1305 && mouse
->y
== term
->prev_mouse_event
.y
) {
1306 if (current_tab
->data
== ses
) ses
= NULL
;
1308 close_tab(term
, current_tab
->data
);
1314 if (check_mouse_button(ev
, B_WHEEL_UP
)) {
1315 switch_current_tab(ses
, -1);
1317 } else if (check_mouse_button(ev
, B_WHEEL_DOWN
)) {
1318 switch_current_tab(ses
, 1);
1320 } else if (tab_num
!= -1) {
1321 switch_to_tab(term
, tab_num
, -1);
1323 if (check_mouse_button(ev
, B_MIDDLE
)) {
1324 do_not_ignore_next_mouse_event(term
);
1325 } else if (check_mouse_button(ev
, B_RIGHT
)) {
1326 tab_menu(current_tab
->data
, mouse
->x
, mouse
->y
, 1);
1333 if (!do_mouse_event(ses
, ev
, doc_view
)
1334 && check_mouse_button(ev
, B_RIGHT
)) {
1335 tab_menu(ses
, mouse
->x
, mouse
->y
, 0);
1340 if (ses
->status
.show_leds
1341 && mouse
->y
== term
->height
- 1
1342 && mouse
->x
>= term
->width
- LEDS_COUNT
- 3) {
1343 menu_leds_info(term
, NULL
, NULL
);
1350 #endif /* CONFIG_MOUSE */
1353 try_typeahead(struct session
*ses
, struct document_view
*doc_view
,
1354 struct term_event
*ev
, enum main_action action_id
)
1356 switch (get_opt_int("document.browse.search.typeahead", ses
)) {
1360 action_id
= ACT_MAIN_SEARCH_TYPEAHEAD_LINK
;
1363 action_id
= ACT_MAIN_SEARCH_TYPEAHEAD_TEXT
;
1366 INTERNAL("invalid value for document.browse.search.typeahead");
1369 search_typeahead(ses
, doc_view
, action_id
);
1371 /* Cross your fingers -- I'm just asking
1372 * for an infinite loop! -- Miciah */
1373 term_send_event(ses
->tab
->term
, ev
);
1376 /** @returns the session if event cleanup should be done or NULL if no
1377 * cleanup is needed. */
1378 static struct session
*
1379 send_kbd_event(struct session
*ses
, struct document_view
*doc_view
,
1380 struct term_event
*ev
)
1383 enum main_action action_id
;
1385 if (doc_view
&& send_to_frame(ses
, doc_view
, ev
) != FRAME_EVENT_IGNORED
)
1388 action_id
= kbd_action(KEYMAP_MAIN
, ev
, &event
);
1390 if (action_id
== ACT_MAIN_QUIT
) {
1391 if (check_kbd_key(ev
, KBD_CTRL_C
))
1393 action_id
= ACT_MAIN_REALLY_QUIT
;
1396 switch (do_action(ses
, action_id
, 0)) {
1397 case FRAME_EVENT_SESSION_DESTROYED
:
1399 case FRAME_EVENT_IGNORED
:
1401 case FRAME_EVENT_OK
:
1402 case FRAME_EVENT_REFRESH
:
1406 if (action_id
== ACT_MAIN_SCRIPTING_FUNCTION
) {
1407 #ifdef CONFIG_SCRIPTING
1408 trigger_event(event
, ses
);
1413 if (check_kbd_key(ev
, KBD_CTRL_C
)) goto quit
;
1415 /* Ctrl-Alt-F should not open the File menu like Alt-f does. */
1416 if (check_kbd_modifier(ev
, KBD_MOD_ALT
)) {
1419 get_kbd_modifier(ev
) &= ~KBD_MOD_ALT
;
1420 activate_bfu_technology(ses
, -1);
1421 win
= ses
->tab
->term
->windows
.next
;
1422 win
->handler(win
, ev
);
1423 if (ses
->tab
->term
->windows
.next
== win
) {
1424 deselect_mainmenu(win
->term
, win
->data
);
1425 print_screen_status(ses
);
1427 if (ses
->tab
!= ses
->tab
->term
->windows
.next
)
1429 get_kbd_modifier(ev
) |= KBD_MOD_ALT
;
1432 && get_opt_int("document.browse.accesskey"
1433 ".priority", ses
) <= 0
1434 && try_document_key(ses
, doc_view
, ev
)
1435 == FRAME_EVENT_REFRESH
) {
1436 /* The document ate the key! */
1437 refresh_view(ses
, doc_view
, 0);
1445 if (!(get_kbd_modifier(ev
) & KBD_MOD_CTRL
)) {
1446 if (doc_view
) try_typeahead(ses
, doc_view
, ev
, action_id
);
1453 send_event(struct session
*ses
, struct term_event
*ev
)
1456 if_assert_failed
return;
1458 if (ev
->ev
== EVENT_KBD
) {
1459 struct document_view
*doc_view
= current_frame(ses
);
1461 ses
= send_kbd_event(ses
, doc_view
, ev
);
1464 else if (ev
->ev
== EVENT_MOUSE
) {
1465 struct document_view
*doc_view
= current_frame(ses
);
1467 ses
= send_mouse_event(ses
, doc_view
, ev
);
1469 #endif /* CONFIG_MOUSE */
1471 /* @ses may disappear ie. in close_tab() */
1472 if (ses
) ses
->kbdprefix
.repeat_count
= 0;
1475 enum frame_event_status
1476 download_link(struct session
*ses
, struct document_view
*doc_view
,
1477 action_id_T action_id
)
1479 struct link
*link
= get_current_link(doc_view
);
1480 void (*download
)(void *ses
, unsigned char *file
) = start_download
;
1482 if (!link
) return FRAME_EVENT_OK
;
1484 if (ses
->download_uri
) {
1485 done_uri(ses
->download_uri
);
1486 ses
->download_uri
= NULL
;
1489 switch (action_id
) {
1490 case ACT_MAIN_LINK_DOWNLOAD_RESUME
:
1491 download
= resume_download
;
1492 case ACT_MAIN_LINK_DOWNLOAD
:
1493 ses
->download_uri
= get_link_uri(ses
, doc_view
, link
);
1496 case ACT_MAIN_LINK_DOWNLOAD_IMAGE
:
1497 if (!link
->where_img
) break;
1498 ses
->download_uri
= get_uri(link
->where_img
, 0);
1502 INTERNAL("I think you forgot to take your medication, mental boy!");
1503 return FRAME_EVENT_OK
;
1506 if (!ses
->download_uri
) return FRAME_EVENT_OK
;
1508 set_session_referrer(ses
, doc_view
->document
->uri
);
1509 query_file(ses
, ses
->download_uri
, ses
, download
, NULL
, 1);
1511 return FRAME_EVENT_OK
;
1514 enum frame_event_status
1515 view_image(struct session
*ses
, struct document_view
*doc_view
, int xxxx
)
1517 struct link
*link
= get_current_link(doc_view
);
1519 if (link
&& link
->where_img
)
1520 goto_url(ses
, link
->where_img
);
1522 return FRAME_EVENT_OK
;
1525 enum frame_event_status
1526 save_as(struct session
*ses
, struct document_view
*doc_view
, int magic
)
1529 if_assert_failed
return FRAME_EVENT_OK
;
1531 if (!have_location(ses
)) return FRAME_EVENT_OK
;
1533 if (ses
->download_uri
) done_uri(ses
->download_uri
);
1534 ses
->download_uri
= get_uri_reference(cur_loc(ses
)->vs
.uri
);
1536 assert(doc_view
&& doc_view
->document
&& doc_view
->document
->uri
);
1537 if_assert_failed
return FRAME_EVENT_OK
;
1539 set_session_referrer(ses
, doc_view
->document
->uri
);
1540 query_file(ses
, ses
->download_uri
, ses
, start_download
, NULL
, 1);
1542 return FRAME_EVENT_OK
;
1546 save_formatted_finish(struct terminal
*term
, int h
, void *data
, download_flags_T flags
)
1548 struct document
*document
= data
;
1550 assert(term
&& document
);
1551 if_assert_failed
return;
1553 if (h
== -1) return;
1554 if (dump_to_file(document
, h
)) {
1555 info_box(term
, 0, N_("Save error"), ALIGN_CENTER
,
1556 N_("Error writing to file"));
1562 save_formatted(void *data
, unsigned char *file
)
1564 struct session
*ses
= data
;
1565 struct document_view
*doc_view
;
1567 assert(ses
&& ses
->tab
&& ses
->tab
->term
&& file
);
1568 if_assert_failed
return;
1569 doc_view
= current_frame(ses
);
1570 assert(doc_view
&& doc_view
->document
);
1571 if_assert_failed
return;
1573 create_download_file(ses
->tab
->term
, file
, NULL
, DOWNLOAD_START
,
1574 save_formatted_finish
, doc_view
->document
);
1577 enum frame_event_status
1578 save_formatted_dlg(struct session
*ses
, struct document_view
*doc_view
, int xxxx
)
1580 query_file(ses
, doc_view
->vs
->uri
, ses
, save_formatted
, NULL
, 1);
1581 return FRAME_EVENT_OK
;