1 /* Links viewing/manipulation handling */
12 #include "bfu/listmenu.h"
14 #include "dialogs/menu.h"
15 #include "dialogs/status.h"
16 #include "document/document.h"
17 #include "document/forms.h"
18 #include "document/html/parser.h"
19 #include "document/html/renderer.h"
20 #include "document/options.h"
21 #include "document/view.h"
22 #include "ecmascript/ecmascript.h"
23 #include "intl/gettext/libintl.h"
24 #include "main/object.h"
25 #include "protocol/uri.h"
26 #include "session/session.h"
27 #include "session/task.h"
28 #include "terminal/color.h"
29 #include "terminal/draw.h"
30 #include "terminal/kbd.h"
31 #include "terminal/screen.h"
32 #include "terminal/tab.h"
33 #include "terminal/terminal.h"
34 #include "util/conv.h"
35 #include "util/error.h"
36 #include "util/memory.h"
37 #include "util/string.h"
38 #include "viewer/action.h"
39 #include "viewer/text/form.h"
40 #include "viewer/text/link.h"
41 #include "viewer/text/search.h"
42 #include "viewer/text/textarea.h"
43 #include "viewer/text/view.h"
44 #include "viewer/text/vs.h"
47 /* Perhaps some of these would be more fun to have in viewer/common/, dunno.
52 current_link_evhook(struct document_view
*doc_view
, enum script_event_hook_type type
)
54 #ifdef CONFIG_ECMASCRIPT
56 struct script_event_hook
*evhook
;
58 assert(doc_view
&& doc_view
->vs
);
59 link
= get_current_link(doc_view
);
61 if (!link
->event_hooks
) return -1;
63 if (!doc_view
->vs
->ecmascript
) return -1;
65 foreach (evhook
, *link
->event_hooks
) {
66 struct string src
= INIT_STRING(evhook
->src
, strlen(evhook
->src
));
68 if (evhook
->type
!= type
) continue;
69 /* TODO: Some even handlers return a bool. */
70 if (!ecmascript_eval_boolback(doc_view
->vs
->ecmascript
, &src
))
80 #define current_link_hover(dv) \
82 current_link_evhook(dv, SEVHOOK_ONMOUSEOVER); \
83 current_link_evhook(dv, SEVHOOK_ONHOVER); \
84 current_link_evhook(dv, SEVHOOK_ONFOCUS); \
86 #define current_link_blur(dv) \
88 current_link_evhook(dv, SEVHOOK_ONMOUSEOUT); \
89 current_link_evhook(dv, SEVHOOK_ONBLUR); \
94 set_link(struct document_view
*doc_view
)
97 if_assert_failed
return;
99 if (current_link_is_visible(doc_view
)) return;
101 find_link_page_down(doc_view
);
105 get_link_cursor_offset(struct document_view
*doc_view
, struct link
*link
)
107 struct form_control
*fc
;
108 struct form_state
*fs
;
110 switch (link
->type
) {
118 fc
= get_link_form_control(link
);
119 fs
= find_form_state(doc_view
, fc
);
120 return fs
? fs
->state
- fs
->vpos
: 0;
123 fc
= get_link_form_control(link
);
124 fs
= find_form_state(doc_view
, fc
);
125 return fs
? area_cursor(fc
, fs
) : 0;
136 /* Allocate doc_view->link_bg with enough space to save the colour
137 * and attributes of each point of the given link plus one byte
138 * for the template character. Initialise that template character
139 * with the colour and attributes appropriate for an active link. */
140 static inline struct screen_char
*
141 init_link_drawing(struct document_view
*doc_view
, struct link
*link
, int invert
)
143 struct document_options
*doc_opts
;
144 struct screen_char
*template;
145 enum color_flags color_flags
;
146 enum color_mode color_mode
;
147 struct color_pair colors
;
149 /* Allocate an extra background char to work on here. */
150 doc_view
->link_bg
= mem_alloc((1 + link
->npoints
) * sizeof(*doc_view
->link_bg
));
151 if (!doc_view
->link_bg
) return NULL
;
153 doc_view
->link_bg_n
= link
->npoints
;
155 /* Setup the template char. */
156 template = &doc_view
->link_bg
[link
->npoints
].c
;
158 template->attr
= SCREEN_ATTR_STANDOUT
;
160 doc_opts
= &doc_view
->document
->options
;
162 color_flags
= (doc_opts
->color_flags
| COLOR_DECREASE_LIGHTNESS
);
163 color_mode
= doc_opts
->color_mode
;
165 if (doc_opts
->active_link
.underline
)
166 template->attr
|= SCREEN_ATTR_UNDERLINE
;
168 if (doc_opts
->active_link
.bold
)
169 template->attr
|= SCREEN_ATTR_BOLD
;
171 if (doc_opts
->active_link
.color
) {
172 colors
.foreground
= doc_opts
->active_link
.fg
;
173 colors
.background
= doc_opts
->active_link
.bg
;
175 colors
.foreground
= link
->color
.foreground
;
176 colors
.background
= link
->color
.background
;
179 if (invert
&& doc_opts
->active_link
.invert
) {
180 swap_values(color_T
, colors
.foreground
, colors
.background
);
182 /* Highlight text-input form-fields correctly if contrast
183 * correction is needed. */
184 if (link_is_textinput(link
)) {
185 /* Make sure to use the options belonging to the
186 * current document when checking for fg and bg color
188 doc_opts
= &doc_view
->document
->options
;
190 /* Are we fallen angels who didn't want to believe that
191 * nothing /is/ nothing and so were born to lose our
192 * loved ones and dear friends one by one and finally
193 * our own life, to see it proved? --Kerouac */
195 /* Wipe out all default correction for 16 color mode */
196 color_flags
= (color_flags
& ~COLOR_INCREASE_CONTRAST
);
197 /* Make contrast correction invert things properly */
198 color_flags
|= COLOR_ENSURE_INVERTED_CONTRAST
;
202 set_term_color(template, &colors
, color_flags
, color_mode
);
207 /* Save the current link's colours and attributes to doc_view->link_bg
208 * and give it the appropriate colour and attributes for an active link. */
210 draw_current_link(struct session
*ses
, struct document_view
*doc_view
)
212 struct terminal
*term
= ses
->tab
->term
;
213 struct screen_char
*template;
219 assert(term
&& doc_view
&& doc_view
->vs
);
220 if_assert_failed
return;
222 assert(ses
->tab
== get_current_tab(term
));
223 if_assert_failed
return;
225 assertm(!doc_view
->link_bg
, "link background not empty");
226 if_assert_failed
mem_free(doc_view
->link_bg
);
228 link
= get_current_link(doc_view
);
231 i
= !link_is_textinput(link
) || ses
->insert_mode
== INSERT_MODE_OFF
;
232 template = init_link_drawing(doc_view
, link
, i
);
233 if (!template) return;
235 xpos
= doc_view
->box
.x
- doc_view
->vs
->x
;
236 ypos
= doc_view
->box
.y
- doc_view
->vs
->y
;
238 if (ses
->insert_mode
== INSERT_MODE_OFF
239 && ses
->navigate_mode
== NAVIGATE_CURSOR_ROUTING
) {
240 /* If we are navigating using cursor routing and not editing a
241 * text-input form-field never set the cursor. */
244 cursor_offset
= get_link_cursor_offset(doc_view
, link
);
247 for (i
= 0; i
< link
->npoints
; i
++) {
248 int x
= link
->points
[i
].x
+ xpos
;
249 int y
= link
->points
[i
].y
+ ypos
;
250 struct screen_char
*co
;
252 if (!is_in_box(&doc_view
->box
, x
, y
)) {
253 doc_view
->link_bg
[i
].x
= -1;
254 doc_view
->link_bg
[i
].y
= -1;
258 doc_view
->link_bg
[i
].x
= x
;
259 doc_view
->link_bg
[i
].y
= y
;
261 co
= get_char(term
, x
, y
);
262 copy_screen_chars(&doc_view
->link_bg
[i
].c
, co
, 1);
264 if (i
== cursor_offset
) {
265 int blockable
= (!link_is_textinput(link
)
266 && co
->color
!= template->color
);
268 set_cursor(term
, x
, y
, blockable
);
269 set_window_ptr(ses
->tab
, x
, y
);
272 template->data
= co
->data
;
273 copy_screen_chars(co
, template, 1);
274 set_screen_dirty(term
->screen
, y
, y
);
279 free_link(struct document_view
*doc_view
)
282 if_assert_failed
return;
284 mem_free_set(&doc_view
->link_bg
, NULL
);
285 doc_view
->link_bg_n
= 0;
288 /* Restore the colours and attributes that the active link had
289 * before it was selected. */
291 clear_link(struct terminal
*term
, struct document_view
*doc_view
)
293 assert(term
&& doc_view
);
294 if_assert_failed
return;
296 if (doc_view
->link_bg
) {
297 struct link_bg
*link_bg
= doc_view
->link_bg
;
300 for (i
= doc_view
->link_bg_n
- 1; i
>= 0; i
--) {
301 struct link_bg
*bgchar
= &link_bg
[i
];
303 if (bgchar
->x
!= -1 && bgchar
->y
!= -1) {
304 struct terminal_screen
*screen
= term
->screen
;
305 struct screen_char
*co
;
307 co
= get_char(term
, bgchar
->x
, bgchar
->y
);
308 copy_screen_chars(co
, &bgchar
->c
, 1);
309 set_screen_dirty(screen
, bgchar
->y
, bgchar
->y
);
318 get_first_link(struct document_view
*doc_view
)
320 struct link
*link
, *undef
;
321 struct document
*document
;
325 assert(doc_view
&& doc_view
->document
);
326 if_assert_failed
return NULL
;
328 document
= doc_view
->document
;
330 if (!document
->lines1
) return NULL
;
332 height
= doc_view
->vs
->y
+ doc_view
->box
.height
;
333 link
= undef
= document
->links
+ document
->nlinks
;
335 for (i
= int_max(0, doc_view
->vs
->y
);
336 i
< int_min(height
, document
->height
);
338 if (document
->lines1
[i
]
339 && document
->lines1
[i
] < link
)
340 link
= document
->lines1
[i
];
343 return (link
== undef
) ? NULL
: link
;
347 get_last_link(struct document_view
*doc_view
)
349 struct link
*link
= NULL
;
350 struct document
*document
;
354 assert(doc_view
&& doc_view
->document
);
355 if_assert_failed
return NULL
;
357 document
= doc_view
->document
;
359 if (!document
->lines2
) return NULL
;
361 height
= doc_view
->vs
->y
+ doc_view
->box
.height
;
363 for (i
= int_max(0, doc_view
->vs
->y
);
364 i
< int_min(height
, document
->height
);
366 if (document
->lines2
[i
] > link
)
367 link
= document
->lines2
[i
];
373 link_in_view_x(struct document_view
*doc_view
, struct link
*link
)
377 assert(doc_view
&& link
);
378 if_assert_failed
return 0;
380 dx
= doc_view
->vs
->x
;
381 width
= doc_view
->box
.width
;
383 for (i
= 0; i
< link
->npoints
; i
++) {
384 int x
= link
->points
[i
].x
- dx
;
386 if (x
>= 0 && x
< width
)
394 link_in_view_y(struct document_view
*doc_view
, struct link
*link
)
398 assert(doc_view
&& link
);
399 if_assert_failed
return 0;
401 dy
= doc_view
->vs
->y
;
402 height
= doc_view
->box
.height
;
404 for (i
= 0; i
< link
->npoints
; i
++) {
405 int y
= link
->points
[i
].y
- dy
;
407 if (y
>= 0 && y
< height
)
415 link_in_view(struct document_view
*doc_view
, struct link
*link
)
417 assert(doc_view
&& link
);
418 if_assert_failed
return 0;
419 return link_in_view_y(doc_view
, link
) && link_in_view_x(doc_view
, link
);
423 current_link_is_visible(struct document_view
*doc_view
)
427 assert(doc_view
&& doc_view
->vs
);
428 if_assert_failed
return 0;
430 link
= get_current_link(doc_view
);
431 return (link
&& link_in_view(doc_view
, link
));
434 /* Look for the first and the last link currently visible in our
437 get_visible_links_range(struct document_view
*doc_view
, int *first
, int *last
)
439 struct document
*document
= doc_view
->document
;
440 int height
= int_min(doc_view
->vs
->y
+ doc_view
->box
.height
,
444 *first
= document
->nlinks
- 1;
447 for (y
= int_max(0, doc_view
->vs
->y
); y
< height
; y
++) {
448 if (document
->lines1
[y
])
449 int_upper_bound(first
, document
->lines1
[y
]
452 if (document
->lines2
[y
])
453 int_lower_bound(last
, document
->lines2
[y
]
459 next_link_in_view_(struct document_view
*doc_view
, int current
, int direction
,
460 int (*fn
)(struct document_view
*, struct link
*),
461 void (*cntr
)(struct document_view
*, struct link
*))
463 struct document
*document
;
464 struct view_state
*vs
;
467 assert(doc_view
&& doc_view
->document
&& doc_view
->vs
&& fn
);
468 if_assert_failed
return 0;
470 document
= doc_view
->document
;
473 get_visible_links_range(doc_view
, &start
, &end
);
475 current_link_blur(doc_view
);
477 /* Go from the @current link in @direction until either
478 * fn() is happy or we would leave the current viewport. */
479 while (current
>= start
&& current
<= end
) {
480 if (fn(doc_view
, &document
->links
[current
])) {
481 vs
->current_link
= current
;
482 if (cntr
) cntr(doc_view
, &document
->links
[current
]);
483 current_link_hover(doc_view
);
486 current
+= direction
;
489 vs
->current_link
= -1;
494 next_link_in_view(struct document_view
*doc_view
, int current
, int direction
)
496 return next_link_in_view_(doc_view
, current
, direction
, link_in_view
, NULL
);
500 next_link_in_view_y(struct document_view
*doc_view
, int current
, int direction
)
502 return next_link_in_view_(doc_view
, current
, direction
, link_in_view_y
, set_pos_x
);
505 /* Get the bounding columns of @link at line @y (or all lines if @y == -1). */
507 get_link_x_bounds(struct link
*link
, int y
, int *min_x
, int *max_x
)
511 if (min_x
) *min_x
= INT_MAX
;
512 if (max_x
) *max_x
= 0;
514 for (point
= 0; point
< link
->npoints
; point
++) {
515 if (y
>= 0 && link
->points
[point
].y
!= y
)
517 if (min_x
) int_upper_bound(min_x
, link
->points
[point
].x
);
518 if (max_x
) int_lower_bound(max_x
, link
->points
[point
].x
);
522 /* Check whether there is any point between @min_x and @max_x at the line @y
525 get_link_x_intersect(struct link
*link
, int y
, int min_x
, int max_x
)
529 for (point
= 0; point
< link
->npoints
; point
++) {
530 if (link
->points
[point
].y
!= y
)
532 if (link
->points
[point
].x
>= min_x
533 && link
->points
[point
].x
<= max_x
)
534 return link
->points
[point
].x
+ 1;
540 /* Check whether there is any point between @min_y and @max_y in the column @x
543 get_link_y_intersect(struct link
*link
, int x
, int min_y
, int max_y
)
547 for (point
= 0; point
< link
->npoints
; point
++) {
548 if (link
->points
[point
].x
!= x
)
550 if (link
->points
[point
].y
>= min_y
551 && link
->points
[point
].y
<= max_y
)
552 return link
->points
[point
].y
+ 1;
559 next_link_in_dir(struct document_view
*doc_view
, int dir_x
, int dir_y
)
561 struct document
*document
;
562 struct view_state
*vs
;
564 int min_x
= INT_MAX
, max_x
= 0;
567 assert(doc_view
&& doc_view
->document
&& doc_view
->vs
);
568 if_assert_failed
return 0;
569 assert(dir_x
|| dir_y
);
570 if_assert_failed
return 0;
572 document
= doc_view
->document
;
575 link
= get_current_link(doc_view
);
578 /* Find the link's "bounding box" coordinates. */
580 get_link_x_bounds(link
, -1, &min_x
, &max_x
);
582 min_y
= link
->points
[0].y
;
583 max_y
= link
->points
[link
->npoints
- 1].y
;
585 /* Now go from the bounding box edge in the appropriate
586 * direction and find the nearest link. */
589 /* Vertical movement */
591 /* The current line number */
592 int y
= (dir_y
> 0 ? max_y
: min_y
) + dir_y
;
593 /* The bounding line numbers */
594 int top
= int_max(0, doc_view
->vs
->y
);
595 int bottom
= int_min(doc_view
->vs
->y
+ doc_view
->box
.height
,
598 for (; dir_y
> 0 ? y
< bottom
: y
>= top
; y
+= dir_y
) {
599 /* @backup points to the nearest link from the left
600 * to the desired position. */
601 struct link
*backup
= NULL
;
603 link
= document
->lines1
[y
];
606 /* Go through all the links on line. */
607 for (; link
<= document
->lines2
[y
]; link
++) {
608 int l_min_x
, l_max_x
;
610 /* Some links can be totally out of order here,
611 * ie. in tables or when using tabindex. */
612 if (y
< link
->points
[0].y
613 || y
> link
->points
[link
->npoints
- 1].y
)
616 get_link_x_bounds(link
, y
, &l_min_x
, &l_max_x
);
617 if (l_min_x
> max_x
) {
618 /* This link is too at the right. */
623 if (l_max_x
< min_x
) {
624 /* This link is too at the left. */
628 /* This link is aligned with the current one. */
638 if (!y
|| y
== document
->height
) {
639 /* We just stay at the same place, do not invalidate
640 * the link number. */
645 /* Horizontal movement */
647 /* The current column number */
648 int x
= (dir_x
> 0 ? max_x
: min_x
) + dir_x
;
649 /* How many lines are already past their last link */
652 while ((last
< max_y
- min_y
+ 1) && (x
+= dir_x
) >= 0) {
657 /* Go through all the lines */
658 for (y
= min_y
; y
<= max_y
; y
++) {
659 link
= document
->lines1
[y
];
662 /* Go through all the links on line. */
663 while (link
<= document
->lines2
[y
]) {
664 if (get_link_y_intersect(link
, x
,
671 /* Check if we already aren't past the last
672 * link on this line. */
673 if (!get_link_x_intersect(document
->lines2
[y
],
683 current_link_blur(doc_view
);
684 vs
->current_link
= -1;
688 /* The link is in bounds, take it. */
689 current_link_blur(doc_view
);
690 vs
->current_link
= get_link_index(document
, link
);
691 set_pos_x(doc_view
, link
);
692 current_link_hover(doc_view
);
697 set_pos_x(struct document_view
*doc_view
, struct link
*link
)
703 assert(doc_view
&& link
);
704 if_assert_failed
return;
706 for (i
= 0; i
< link
->npoints
; i
++) {
707 int y
= link
->points
[i
].y
- doc_view
->vs
->y
;
709 if (y
>= 0 && y
< doc_view
->box
.height
) {
710 int_lower_bound(&xm
, link
->points
[i
].x
+ 1);
711 int_upper_bound(&xl
, link
->points
[i
].x
);
716 int_bounds(&doc_view
->vs
->x
, xm
- doc_view
->box
.width
, xl
);
720 set_pos_y(struct document_view
*doc_view
, struct link
*link
)
726 assert(doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
727 if_assert_failed
return;
729 height
= doc_view
->document
->height
;
730 for (i
= 0; i
< link
->npoints
; i
++) {
731 int_lower_bound(&ym
, link
->points
[i
].y
+ 1);
732 int_upper_bound(&height
, link
->points
[i
].y
);
734 doc_view
->vs
->y
= (ym
+ height
- doc_view
->box
.height
) / 2;
735 int_bounds(&doc_view
->vs
->y
, 0,
736 doc_view
->document
->height
- doc_view
->box
.height
);
739 /* direction == 1 -> DOWN
740 * direction == -1 -> UP */
742 find_link(struct document_view
*doc_view
, int direction
, int page_mode
)
745 struct link
*link
= NULL
;
749 assert(doc_view
&& doc_view
->document
&& doc_view
->vs
);
750 if_assert_failed
return;
752 if (direction
== -1) {
754 line
= doc_view
->document
->lines2
;
755 if (!line
) goto nolink
;
756 y
= doc_view
->vs
->y
+ doc_view
->box
.height
- 1;
757 int_upper_bound(&y
, doc_view
->document
->height
- 1);
758 if (y
< 0) goto nolink
;
761 line
= doc_view
->document
->lines1
;
762 if (!line
) goto nolink
;
764 int_lower_bound(&y
, 0);
765 if (y
>= doc_view
->document
->height
) goto nolink
;
768 ymin
= int_max(0, doc_view
->vs
->y
);
769 ymax
= int_min(doc_view
->document
->height
,
770 doc_view
->vs
->y
+ doc_view
->box
.height
);
772 if (direction
== -1) {
775 struct link
*cur
= line
[y
--];
777 if (cur
&& (!link
|| cur
> link
))
779 } while (y
>= ymin
&& y
< ymax
);
784 struct link
*cur
= line
[y
++];
786 if (cur
&& (!link
|| cur
< link
))
788 } while (y
>= ymin
&& y
< ymax
);
791 if (!link
) goto nolink
;
793 link_pos
= link
- doc_view
->document
->links
;
796 next_link_in_view(doc_view
, link_pos
, direction
);
799 current_link_blur(doc_view
);
800 doc_view
->vs
->current_link
= link_pos
;
801 set_pos_x(doc_view
, link
);
802 current_link_hover(doc_view
);
806 current_link_blur(doc_view
);
807 doc_view
->vs
->current_link
= -1;
811 find_link_up(struct document_view
*doc_view
)
813 find_link(doc_view
, -1, 0);
817 find_link_page_up(struct document_view
*doc_view
)
819 find_link(doc_view
, -1, 1);
823 find_link_down(struct document_view
*doc_view
)
825 find_link(doc_view
, 1, 0);
829 find_link_page_down(struct document_view
*doc_view
)
831 find_link(doc_view
, 1, 1);
835 get_link_uri(struct session
*ses
, struct document_view
*doc_view
,
838 assert(ses
&& doc_view
&& link
);
839 if_assert_failed
return NULL
;
841 switch (link
->type
) {
844 if (link
->where
) return get_uri(link
->where
, 0);
845 return get_uri(link
->where_img
, 0);
849 return get_form_uri(ses
, doc_view
,
850 get_link_form_control(link
));
858 try_submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
859 struct form
*form
, int do_reload
)
861 #ifdef CONFIG_ECMASCRIPT
862 if (form
->onsubmit
) {
865 if (init_string(&code
)) {
866 struct view_state
*vs
= doc_view
->vs
;
867 struct ecmascript_interpreter
*interpreter
;
869 unsigned char *ret
= form
->onsubmit
;
871 if (vs
->ecmascript_fragile
)
872 ecmascript_reset_state(vs
);
873 interpreter
= vs
->ecmascript
;
875 #ifdef CONFIG_ECMASCRIPT_SEE
876 /* SEE doesn't like return outside functions */
877 while ((ret
= strstr(ret
, "return "))) {
878 while (*ret
!= ' ') *ret
++ = ' ';
881 add_to_string(&code
, form
->onsubmit
);
882 res
= ecmascript_eval_boolback(interpreter
, &code
);
888 submit_given_form(ses
, doc_view
, form
, do_reload
);
892 goto_current_link(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
897 assert(doc_view
&& ses
);
898 if_assert_failed
return NULL
;
900 link
= get_current_link(doc_view
);
901 if (!link
) return NULL
;
903 if (link_is_form(link
)) {
904 struct form_control
*fc
= link
->data
.form_control
;
905 struct form
*form
= fc
->form
;
907 try_submit_given_form(ses
, doc_view
, form
, do_reload
);
910 uri
= get_link_uri(ses
, doc_view
, link
);
912 if (!uri
) return NULL
;
914 if (link
->type
== LINK_MAP
) {
915 /* TODO: Test reload? */
916 goto_imgmap(ses
, uri
, null_or_stracpy(link
->target
));
919 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
922 goto_uri_frame(ses
, uri
, link
->target
, mode
);
929 static enum frame_event_status
930 activate_link(struct session
*ses
, struct document_view
*doc_view
,
931 struct link
*link
, int do_reload
)
933 struct form_control
*link_fc
;
934 struct form_state
*fs
;
937 switch (link
->type
) {
943 if (goto_current_link(ses
, doc_view
, do_reload
))
944 return FRAME_EVENT_OK
;
949 link_fc
= get_link_form_control(link
);
951 if (form_field_is_readonly(link_fc
))
952 return FRAME_EVENT_OK
;
954 fs
= find_form_state(doc_view
, link_fc
);
955 if (!fs
) return FRAME_EVENT_OK
;
957 if (link_fc
->type
== FC_CHECKBOX
) {
958 fs
->state
= !fs
->state
;
960 return FRAME_EVENT_REFRESH
;
963 foreach (form
, doc_view
->document
->forms
) {
964 struct form_control
*fc
;
966 if (form
!= link_fc
->form
)
969 foreach (fc
, form
->items
) {
970 if (fc
->type
== FC_RADIO
971 && !xstrcmp(fc
->name
, link_fc
->name
)) {
972 struct form_state
*frm_st
;
974 frm_st
= find_form_state(doc_view
, fc
);
975 if (frm_st
) frm_st
->state
= 0;
984 link_fc
= get_link_form_control(link
);
986 if (form_field_is_readonly(link_fc
))
987 return FRAME_EVENT_OK
;
989 object_lock(doc_view
->document
);
990 add_empty_window(ses
->tab
->term
,
991 (void (*)(void *)) release_document
,
993 do_select_submenu(ses
->tab
->term
, link_fc
->menu
, ses
);
998 INTERNAL("bad link type %d", link
->type
);
1001 return FRAME_EVENT_REFRESH
;
1004 enum frame_event_status
1005 enter(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1007 enum frame_event_status ret
;
1010 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
1011 if_assert_failed
return FRAME_EVENT_REFRESH
;
1013 link
= get_current_link(doc_view
);
1014 if (!link
) return FRAME_EVENT_REFRESH
;
1016 ret
= activate_link(ses
, doc_view
, link
, do_reload
);
1017 if (ret
!= FRAME_EVENT_IGNORED
)
1018 if (!current_link_evhook(doc_view
, SEVHOOK_ONCLICK
))
1019 return FRAME_EVENT_REFRESH
;
1025 get_link_at_coordinates(struct document_view
*doc_view
, int x
, int y
)
1027 struct link
*l1
, *l2
, *link
;
1030 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
1031 if_assert_failed
return NULL
;
1033 /* If there are no links in in document, there is nothing to do. */
1034 if (!doc_view
->document
->nlinks
) return NULL
;
1036 /* If the coordinates are outside document view, no need to go further. */
1037 if (x
< 0 || x
>= doc_view
->box
.width
) return NULL
;
1038 if (y
< 0 || y
>= doc_view
->box
.height
) return NULL
;
1040 /* FIXME: This doesn't work. --Zas
1041 if (!check_mouse_position(ev, &doc_view->box))
1045 /* Find link candidates. */
1046 l1
= doc_view
->document
->links
+ doc_view
->document
->nlinks
;
1047 l2
= doc_view
->document
->links
;
1048 height
= int_min(doc_view
->document
->height
,
1049 doc_view
->vs
->y
+ doc_view
->box
.height
);
1051 for (i
= doc_view
->vs
->y
; i
< height
; i
++) {
1052 if (doc_view
->document
->lines1
[i
]
1053 && doc_view
->document
->lines1
[i
] < l1
)
1054 l1
= doc_view
->document
->lines1
[i
];
1056 if (doc_view
->document
->lines2
[i
]
1057 && doc_view
->document
->lines2
[i
] > l2
)
1058 l2
= doc_view
->document
->lines2
[i
];
1061 /* Is there a link at the given coordinates? */
1062 x
+= doc_view
->vs
->x
;
1063 y
+= doc_view
->vs
->y
;
1065 for (link
= l1
; link
<= l2
; link
++) {
1066 for (i
= 0; i
< link
->npoints
; i
++)
1067 if (link
->points
[i
].x
== x
1068 && link
->points
[i
].y
== y
)
1075 /* This is backend of the backend goto_link_number_do() below ;)). */
1077 jump_to_link_number(struct session
*ses
, struct document_view
*doc_view
, int n
)
1079 assert(ses
&& doc_view
&& doc_view
->vs
&& doc_view
->document
);
1080 if_assert_failed
return;
1082 if (n
< 0 || n
>= doc_view
->document
->nlinks
) return;
1083 current_link_blur(doc_view
);
1084 doc_view
->vs
->current_link
= n
;
1085 if (ses
->navigate_mode
== NAVIGATE_CURSOR_ROUTING
) {
1086 struct link
*link
= get_current_link(doc_view
);
1087 int offset
= get_link_cursor_offset(doc_view
, link
);
1089 if (link
->npoints
> offset
) {
1090 int x
= link
->points
[offset
].x
1091 + doc_view
->box
.x
- doc_view
->vs
->x
;
1092 int y
= link
->points
[offset
].y
1093 + doc_view
->box
.y
- doc_view
->vs
->y
;
1095 move_cursor(ses
, doc_view
, x
, y
);
1099 current_link_hover(doc_view
);
1102 /* This is common backend for goto_link_number() and try_document_key(). */
1104 goto_link_number_do(struct session
*ses
, struct document_view
*doc_view
, int n
)
1108 assert(ses
&& doc_view
&& doc_view
->document
);
1109 if_assert_failed
return;
1110 if (n
< 0 || n
>= doc_view
->document
->nlinks
) return;
1111 jump_to_link_number(ses
, doc_view
, n
);
1113 link
= &doc_view
->document
->links
[n
];
1114 if (!link_is_textinput(link
)
1115 && get_opt_bool("document.browse.accesskey.auto_follow"))
1116 enter(ses
, doc_view
, 0);
1120 goto_link_number(struct session
*ses
, unsigned char *num
)
1122 struct document_view
*doc_view
;
1125 if_assert_failed
return;
1126 doc_view
= current_frame(ses
);
1128 if_assert_failed
return;
1129 goto_link_number_do(ses
, doc_view
, atoi(num
) - 1);
1132 /* See if this document is interested in the key user pressed. */
1133 enum frame_event_status
1134 try_document_key(struct session
*ses
, struct document_view
*doc_view
,
1135 struct term_event
*ev
)
1139 int i
; /* GOD I HATE C! --FF */ /* YEAH, BRAINFUCK RULEZ! --pasky */
1141 assert(ses
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& ev
);
1142 if_assert_failed
return FRAME_EVENT_IGNORED
;
1144 if (!check_kbd_modifier(ev
, KBD_MOD_ALT
)) {
1145 /* We accept those only in alt-combo. */
1146 return FRAME_EVENT_IGNORED
;
1149 /* Run through all the links and see if one of them is bound to the
1151 key
= get_kbd_key(ev
);
1153 for (i
= 0; i
< doc_view
->document
->nlinks
; i
++) {
1154 struct link
*link
= &doc_view
->document
->links
[i
];
1156 if (key
== link
->accesskey
) { /* FIXME: key vs unicode ... */
1157 if (passed
!= i
&& i
<= doc_view
->vs
->current_link
) {
1158 /* This is here in order to rotate between
1159 * links with same accesskey. */
1160 if (passed
< 0) passed
= i
;
1163 ses
->kbdprefix
.repeat_count
= 0;
1164 goto_link_number_do(ses
, doc_view
, i
);
1165 return FRAME_EVENT_REFRESH
;
1168 if (i
== doc_view
->document
->nlinks
- 1 && passed
>= 0) {
1169 /* Return to the start. */
1174 return FRAME_EVENT_IGNORED
;
1177 /* Open a contextual menu on a link, form or image element. */
1178 /* TODO: This should be completely configurable. */
1180 link_menu(struct terminal
*term
, void *xxx
, void *ses_
)
1182 struct session
*ses
= ses_
;
1183 struct document_view
*doc_view
;
1185 struct menu_item
*mi
;
1186 struct form_control
*fc
;
1188 assert(term
&& ses
);
1189 if_assert_failed
return;
1191 doc_view
= current_frame(ses
);
1192 mi
= new_menu(FREE_LIST
);
1194 if (!doc_view
) goto end
;
1196 assert(doc_view
->vs
&& doc_view
->document
);
1197 if_assert_failed
return;
1199 link
= get_current_link(doc_view
);
1200 if (!link
) goto end
;
1202 if (link
->where
&& !link_is_form(link
)) {
1203 if (link
->type
== LINK_MAP
) {
1204 add_to_menu(&mi
, N_("Display ~usemap"), NULL
, ACT_MAIN_LINK_FOLLOW
,
1205 NULL
, NULL
, SUBMENU
);
1207 add_menu_action(&mi
, N_("~Follow link"), ACT_MAIN_LINK_FOLLOW
);
1209 add_menu_action(&mi
, N_("Follow link and r~eload"), ACT_MAIN_LINK_FOLLOW_RELOAD
);
1211 add_menu_separator(&mi
);
1213 add_new_win_to_menu(&mi
, N_("Open in new ~window"), term
);
1215 add_menu_action(&mi
, N_("Open in new ~tab"), ACT_MAIN_OPEN_LINK_IN_NEW_TAB
);
1217 add_menu_action(&mi
, N_("Open in new tab in ~background"),
1218 ACT_MAIN_OPEN_LINK_IN_NEW_TAB_IN_BACKGROUND
);
1220 if (!get_cmd_opt_bool("anonymous")) {
1221 add_menu_separator(&mi
);
1222 add_menu_action(&mi
, N_("~Download link"), ACT_MAIN_LINK_DOWNLOAD
);
1224 #ifdef CONFIG_BOOKMARKS
1225 add_menu_action(&mi
, N_("~Add link to bookmarks"),
1226 ACT_MAIN_ADD_BOOKMARK_LINK
);
1228 add_uri_command_to_menu(&mi
, PASS_URI_LINK
);
1233 fc
= get_link_form_control(link
);
1237 add_menu_action(&mi
, N_("~Reset form"), ACT_MAIN_RESET_FORM
);
1241 if (!form_field_is_readonly(fc
)) {
1242 struct string keystroke
;
1244 if (init_string(&keystroke
))
1245 add_keystroke_action_to_string(
1247 ACT_EDIT_OPEN_EXTERNAL
,
1250 add_to_menu(&mi
, N_("Open in ~external editor"),
1251 keystroke
.source
, ACT_MAIN_NONE
,
1252 menu_textarea_edit
, NULL
, FREE_RTEXT
);
1256 add_menu_action(&mi
, N_("~Submit form"), ACT_MAIN_SUBMIT_FORM
);
1257 add_menu_action(&mi
, N_("Submit form and rel~oad"), ACT_MAIN_SUBMIT_FORM_RELOAD
);
1260 if (fc
->form
->method
== FORM_METHOD_GET
) {
1261 add_new_win_to_menu(&mi
, N_("Submit form and open in new ~window"), term
);
1263 add_menu_action(&mi
, N_("Submit form and open in new ~tab"),
1264 ACT_MAIN_OPEN_LINK_IN_NEW_TAB
);
1266 add_menu_action(&mi
, N_("Submit form and open in new tab in ~background"),
1267 ACT_MAIN_OPEN_LINK_IN_NEW_TAB_IN_BACKGROUND
);
1270 if (!get_cmd_opt_bool("anonymous"))
1271 add_menu_action(&mi
, N_("Submit form and ~download"), ACT_MAIN_LINK_DOWNLOAD
);
1273 add_menu_action(&mi
, N_("~Reset form"), ACT_MAIN_RESET_FORM
);
1276 add_to_menu(&mi
, N_("Form f~ields"), NULL
, ACT_MAIN_LINK_FORM_MENU
,
1277 NULL
, NULL
, SUBMENU
);
1280 if (link
->where_img
) {
1281 add_menu_action(&mi
, N_("V~iew image"), ACT_MAIN_VIEW_IMAGE
);
1282 if (!get_cmd_opt_bool("anonymous"))
1283 add_menu_action(&mi
, N_("Download ima~ge"), ACT_MAIN_LINK_DOWNLOAD_IMAGE
);
1286 /* TODO: Make it possible to trigger any script event hooks associated
1287 * to the link. --pasky */
1291 add_to_menu(&mi
, N_("No link selected"), NULL
, ACT_MAIN_NONE
,
1292 NULL
, NULL
, NO_SELECT
);
1295 do_menu(term
, mi
, ses
, 1);
1298 /* Return current link's title. Pretty trivial. */
1300 get_current_link_title(struct document_view
*doc_view
)
1304 assert(doc_view
&& doc_view
->document
&& doc_view
->vs
);
1305 if_assert_failed
return NULL
;
1307 if (doc_view
->document
->frame_desc
)
1310 link
= get_current_link(doc_view
);
1312 return (link
&& link
->title
&& *link
->title
) ? stracpy(link
->title
) : NULL
;
1316 get_current_link_info(struct session
*ses
, struct document_view
*doc_view
)
1320 assert(ses
&& doc_view
&& doc_view
->document
&& doc_view
->vs
);
1321 if_assert_failed
return NULL
;
1323 if (doc_view
->document
->frame_desc
)
1326 link
= get_current_link(doc_view
);
1327 if (!link
) return NULL
;
1329 /* TODO: Provide info about script event hooks too. --pasky */
1331 if (!link_is_form(link
)) {
1332 struct terminal
*term
= ses
->tab
->term
;
1334 unsigned char *uristring
= link
->where
;
1336 if (!init_string(&str
)) return NULL
;
1338 if (!link
->where
&& link
->where_img
) {
1339 add_to_string(&str
, _("Image", term
));
1340 add_char_to_string(&str
, ' ');
1341 uristring
= link
->where_img
;
1343 } else if (link
->type
== LINK_MAP
) {
1344 add_to_string(&str
, _("Usemap", term
));
1345 add_char_to_string(&str
, ' ');
1348 /* Add the uri with password and post info stripped */
1349 add_string_uri_to_string(&str
, uristring
, URI_PUBLIC
);
1350 if (link
->accesskey
> 0
1351 && get_opt_bool("document.browse.accesskey.display")) {
1352 add_to_string(&str
, " (");
1353 add_accesskey_to_string(&str
, link
->accesskey
);
1354 add_char_to_string(&str
, ')');
1357 decode_uri_string_for_display(&str
);
1361 if (!get_link_form_control(link
)) return NULL
;
1363 return get_form_info(ses
, doc_view
);