1 /* HTML viewer (and much more) */
2
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6
7 #include <stdlib.h>
8 #include <string.h>
9 #ifdef HAVE_UNISTD_H
10 #include <unistd.h>
11 #endif
12
13 #include "elinks.h"
14
15 #include "bfu/leds.h"
16 #include "bfu/menu.h"
17 #include "bfu/dialog.h"
18 #include "config/kbdbind.h"
19 #include "config/options.h"
20 #include "dialogs/document.h"
21 #include "dialogs/menu.h"
22 #include "dialogs/options.h"
23 #include "dialogs/status.h"
24 #include "document/document.h"
25 #include "document/html/frames.h"
26 #include "document/options.h"
27 #include "document/renderer.h"
28 #include "document/view.h"
29 #include "intl/charsets.h"
30 #include "intl/gettext/libintl.h"
31 #include "main/event.h"
32 #include "osdep/osdep.h"
33 #include "protocol/uri.h"
34 #include "session/download.h"
35 #include "session/location.h"
36 #include "session/session.h"
37 #include "session/task.h"
38 #include "terminal/draw.h"
39 #include "terminal/event.h"
40 #include "terminal/kbd.h"
41 #include "terminal/mouse.h"
42 #include "terminal/tab.h"
43 #include "terminal/terminal.h"
44 #include "terminal/window.h"
45 #include "util/color.h"
46 #include "util/conv.h"
47 #include "util/error.h"
48 #include "util/memory.h"
49 #include "util/snprintf.h"
50 #include "util/string.h"
51 #include "viewer/action.h"
52 #include "viewer/dump/dump.h"
53 #include "viewer/text/draw.h"
54 #include "viewer/text/form.h"
55 #include "viewer/text/link.h"
56 #include "viewer/text/marks.h"
57 #include "viewer/text/search.h"
58 #include "viewer/text/textarea.h"
59 #include "viewer/text/view.h"
60 #include "viewer/text/vs.h"
61
62
63
64 void
detach_formatted(struct document_view * doc_view)65 detach_formatted(struct document_view *doc_view)
66 {
67 assert(doc_view);
68 if_assert_failed return;
69
70 if (doc_view->document) {
71 release_document(doc_view->document);
72 doc_view->document = NULL;
73 }
74 if (doc_view->vs) {
75 doc_view->vs->doc_view = NULL;
76 doc_view->vs = NULL;
77 }
78 if (doc_view->link_bg) free_link(doc_view);
79 mem_free_set(&doc_view->name, NULL);
80 }
81
82 /* type == 0 -> PAGE_DOWN
83 * type == 1 -> DOWN */
84 static enum frame_event_status
move_down(struct session * ses,struct document_view * doc_view,int type)85 move_down(struct session *ses, struct document_view *doc_view, int type)
86 {
87 int newpos;
88
89 assert(ses && doc_view && doc_view->vs);
90 if_assert_failed return FRAME_EVENT_OK;
91
92 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
93
94 newpos = doc_view->vs->y + doc_view->box.height;
95 if (newpos < doc_view->document->height)
96 doc_view->vs->y = newpos;
97
98 if (current_link_is_visible(doc_view))
99 return FRAME_EVENT_REFRESH;
100
101 if (type)
102 find_link_down(doc_view);
103 else
104 find_link_page_down(doc_view);
105
106 return FRAME_EVENT_REFRESH;
107 }
108
109 enum frame_event_status
move_page_down(struct session * ses,struct document_view * doc_view)110 move_page_down(struct session *ses, struct document_view *doc_view)
111 {
112 enum frame_event_status status;
113 int count = eat_kbd_repeat_count(ses);
114
115 ses->navigate_mode = NAVIGATE_LINKWISE;
116
117 do {
118 status = move_down(ses, doc_view, 0);
119 if (status != FRAME_EVENT_REFRESH) break;
120 } while (--count > 0);
121
122 return status;
123 }
124
125 /* type == 0 -> PAGE_UP
126 * type == 1 -> UP */
127 static enum frame_event_status
move_up(struct session * ses,struct document_view * doc_view,int type)128 move_up(struct session *ses, struct document_view *doc_view, int type)
129 {
130 assert(ses && doc_view && doc_view->vs);
131 if_assert_failed return FRAME_EVENT_OK;
132
133 assert(ses->navigate_mode == NAVIGATE_LINKWISE); /* XXX: drop it at some time. --Zas */
134
135 if (doc_view->vs->y == 0) return FRAME_EVENT_OK;
136
137 doc_view->vs->y -= doc_view->box.height;
138 int_lower_bound(&doc_view->vs->y, 0);
139
140 if (current_link_is_visible(doc_view))
141 return FRAME_EVENT_REFRESH;
142
143 if (type)
144 find_link_up(doc_view);
145 else
146 find_link_page_up(doc_view);
147
148 return FRAME_EVENT_REFRESH;
149 }
150
151 enum frame_event_status
move_page_up(struct session * ses,struct document_view * doc_view)152 move_page_up(struct session *ses, struct document_view *doc_view)
153 {
154 enum frame_event_status status;
155 int count = eat_kbd_repeat_count(ses);
156
157 ses->navigate_mode = NAVIGATE_LINKWISE;
158
159 do {
160 status = move_up(ses, doc_view, 0);
161 if (status != FRAME_EVENT_REFRESH) break;
162 } while (--count > 0);
163
164 return status;
165 }
166
167 enum frame_event_status
move_link(struct session * ses,struct document_view * doc_view,int direction,int wraparound_bound,int wraparound_link)168 move_link(struct session *ses, struct document_view *doc_view, int direction,
169 int wraparound_bound, int wraparound_link)
170 {
171 int wraparound = 0;
172 int count;
173
174 assert(ses && doc_view && doc_view->vs && doc_view->document);
175 if_assert_failed return FRAME_EVENT_OK;
176
177 ses->navigate_mode = NAVIGATE_LINKWISE;
178
179 if (doc_view->document->nlinks < 2) {
180 /* Wraparound only makes sense with more than one link. */
181 wraparound_bound = -1;
182 } else {
183 /* We only bother this option if there's some links
184 * in document. */
185 wraparound = get_opt_bool("document.browse.links.wraparound");
186 }
187
188 count = eat_kbd_repeat_count(ses);
189
190 do {
191 int current_link = doc_view->vs->current_link;
192
193 if (current_link == wraparound_bound) {
194 if (wraparound) {
195 jump_to_link_number(ses, doc_view, wraparound_link);
196 /* FIXME: This needs further work, we should call
197 * page_down() and set_textarea() under some conditions
198 * as well. --pasky */
199 continue;
200 }
201
202 } else {
203 if (next_link_in_view_y(doc_view, current_link + direction,
204 direction))
205 continue;
206 }
207
208 /* This is a work around for the case where the index of
209 * @wraparound_bound is not necessarily the index of the first
210 * or the last link in the view. It means that the link moving
211 * could end up calling next_link_in_view() in the condition
212 * above. This is bad because next_link_in_view() will then
213 * 'reset' doc_view->vs->current_link to -1 and the effect will
214 * be that the current link will 'wrap around'. By restoring
215 * the index of the @current_link nothing will be wrapped
216 * around and move_{up,down} will take care of finding the next
217 * link. */
218 doc_view->vs->current_link = current_link;
219
220 if (direction > 0) {
221 move_down(ses, doc_view, 1);
222 } else {
223 move_up(ses, doc_view, 1);
224 }
225
226 if (current_link != wraparound_bound
227 && current_link != doc_view->vs->current_link) {
228 set_textarea(doc_view, -direction);
229 }
230 } while (--count > 0);
231
232 return FRAME_EVENT_REFRESH;
233 }
234
235 enum frame_event_status
move_link_dir(struct session * ses,struct document_view * doc_view,int dir_x,int dir_y)236 move_link_dir(struct session *ses, struct document_view *doc_view, int dir_x, int dir_y)
237 {
238 int count;
239
240 assert(ses && doc_view && doc_view->vs && doc_view->document);
241 if_assert_failed return FRAME_EVENT_OK;
242
243 ses->navigate_mode = NAVIGATE_LINKWISE;
244
245 count = eat_kbd_repeat_count(ses);
246
247 do {
248 int current_link = doc_view->vs->current_link;
249
250 if (next_link_in_dir(doc_view, dir_x, dir_y))
251 continue;
252
253 /* FIXME: This won't preserve the column! */
254 if (dir_y > 0)
255 move_down(ses, doc_view, 1);
256 else if (dir_y < 0)
257 move_up(ses, doc_view, 1);
258
259 if (dir_y && current_link != doc_view->vs->current_link) {
260 set_textarea(doc_view, -dir_y);
261 }
262 } while (--count > 0);
263
264 return FRAME_EVENT_REFRESH;
265 }
266
267 /* @steps > 0 -> down */
268 static enum frame_event_status
vertical_scroll(struct session * ses,struct document_view * doc_view,int steps)269 vertical_scroll(struct session *ses, struct document_view *doc_view, int steps)
270 {
271 int y;
272
273 assert(ses && doc_view && doc_view->vs && doc_view->document);
274 if_assert_failed return FRAME_EVENT_OK;
275
276 y = doc_view->vs->y + steps;
277 if (steps > 0) {
278 /* DOWN */
279 int max_height = doc_view->document->height - doc_view->box.height;
280
281 if (doc_view->vs->y >= max_height) return FRAME_EVENT_OK;
282 int_upper_bound(&y, max_height);
283 }
284
285 int_lower_bound(&y, 0);
286
287 if (doc_view->vs->y == y) return FRAME_EVENT_OK;
288
289 doc_view->vs->y = y;
290
291 if (current_link_is_visible(doc_view))
292 return FRAME_EVENT_REFRESH;
293
294 if (steps > 0)
295 find_link_page_down(doc_view);
296 else
297 find_link_page_up(doc_view);
298
299 return FRAME_EVENT_REFRESH;
300 }
301
302 /* @steps > 0 -> right */
303 static enum frame_event_status
horizontal_scroll(struct session * ses,struct document_view * doc_view,int steps)304 horizontal_scroll(struct session *ses, struct document_view *doc_view, int steps)
305 {
306 int x, max;
307
308 assert(ses && doc_view && doc_view->vs && doc_view->document);
309 if_assert_failed return FRAME_EVENT_OK;
310
311 x = doc_view->vs->x + steps;
312
313 if (get_opt_bool("document.browse.scrolling.horizontal_extended")) {
314 max = doc_view->document->width - 1;
315 } else {
316 max = int_max(doc_view->vs->x,
317 doc_view->document->width - doc_view->box.width);
318 }
319
320 int_bounds(&x, 0, max);
321 if (doc_view->vs->x == x) return FRAME_EVENT_OK;
322
323 doc_view->vs->x = x;
324
325 if (current_link_is_visible(doc_view))
326 return FRAME_EVENT_REFRESH;
327
328 find_link_page_down(doc_view);
329
330 return FRAME_EVENT_REFRESH;
331 }
332
333 enum frame_event_status
scroll_up(struct session * ses,struct document_view * doc_view)334 scroll_up(struct session *ses, struct document_view *doc_view)
335 {
336 int steps = eat_kbd_repeat_count(ses);
337
338 if (!steps)
339 steps = get_opt_int("document.browse.scrolling.vertical_step");
340
341 return vertical_scroll(ses, doc_view, -steps);
342 }
343
344 enum frame_event_status
scroll_down(struct session * ses,struct document_view * doc_view)345 scroll_down(struct session *ses, struct document_view *doc_view)
346 {
347 int steps = eat_kbd_repeat_count(ses);
348
349 if (!steps)
350 steps = get_opt_int("document.browse.scrolling.vertical_step");
351
352 return vertical_scroll(ses, doc_view, steps);
353 }
354
355 enum frame_event_status
scroll_left(struct session * ses,struct document_view * doc_view)356 scroll_left(struct session *ses, struct document_view *doc_view)
357 {
358 int steps = eat_kbd_repeat_count(ses);
359
360 if (!steps)
361 steps = get_opt_int("document.browse.scrolling.horizontal_step");
362
363 return horizontal_scroll(ses, doc_view, -steps);
364 }
365
366 enum frame_event_status
scroll_right(struct session * ses,struct document_view * doc_view)367 scroll_right(struct session *ses, struct document_view *doc_view)
368 {
369 int steps = eat_kbd_repeat_count(ses);
370
371 if (!steps)
372 steps = get_opt_int("document.browse.scrolling.horizontal_step");
373
374 return horizontal_scroll(ses, doc_view, steps);
375 }
376
377 #ifdef CONFIG_MOUSE
378 static enum frame_event_status
scroll_mouse_up(struct session * ses,struct document_view * doc_view)379 scroll_mouse_up(struct session *ses, struct document_view *doc_view)
380 {
381 int steps = get_opt_int("document.browse.scrolling.vertical_step");
382
383 return vertical_scroll(ses, doc_view, -steps);
384 }
385
386 static enum frame_event_status
scroll_mouse_down(struct session * ses,struct document_view * doc_view)387 scroll_mouse_down(struct session *ses, struct document_view *doc_view)
388 {
389 int steps = get_opt_int("document.browse.scrolling.vertical_step");
390
391 return vertical_scroll(ses, doc_view, steps);
392 }
393
394 static enum frame_event_status
scroll_mouse_left(struct session * ses,struct document_view * doc_view)395 scroll_mouse_left(struct session *ses, struct document_view *doc_view)
396 {
397 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
398
399 return horizontal_scroll(ses, doc_view, -steps);
400 }
401
402 static enum frame_event_status
scroll_mouse_right(struct session * ses,struct document_view * doc_view)403 scroll_mouse_right(struct session *ses, struct document_view *doc_view)
404 {
405 int steps = get_opt_int("document.browse.scrolling.horizontal_step");
406
407 return horizontal_scroll(ses, doc_view, steps);
408 }
409 #endif /* CONFIG_MOUSE */
410
411 enum frame_event_status
move_document_start(struct session * ses,struct document_view * doc_view)412 move_document_start(struct session *ses, struct document_view *doc_view)
413 {
414 assert(ses && doc_view && doc_view->vs);
415 if_assert_failed return FRAME_EVENT_OK;
416
417 doc_view->vs->y = doc_view->vs->x = 0;
418
419 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
420 /* Move to the first line and the first column. */
421 move_cursor(ses, doc_view, doc_view->box.x, doc_view->box.y);
422 } else {
423 find_link_page_down(doc_view);
424 }
425
426 return FRAME_EVENT_REFRESH;
427 }
428
429 enum frame_event_status
move_document_end(struct session * ses,struct document_view * doc_view)430 move_document_end(struct session *ses, struct document_view *doc_view)
431 {
432 int max_height;
433
434 assert(ses && doc_view && doc_view->vs && doc_view->document);
435 if_assert_failed return FRAME_EVENT_OK;
436
437 max_height = doc_view->document->height - doc_view->box.height;
438 doc_view->vs->x = 0;
439 int_lower_bound(&doc_view->vs->y, int_max(0, max_height));
440
441 if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
442 /* Move to the last line of the document,
443 * but preserve the column. This is done to avoid
444 * moving the cursor backwards if it is already
445 * on the last line but is not on the first column. */
446 move_cursor(ses, doc_view, ses->tab->x,
447 doc_view->document->height - doc_view->vs->y);
448 } else {
449 find_link_page_up(doc_view);
450 }
451
452 return FRAME_EVENT_REFRESH;
453 }
454
455 enum frame_event_status
set_frame(struct session * ses,struct document_view * doc_view,int xxxx)456 set_frame(struct session *ses, struct document_view *doc_view, int xxxx)
457 {
458 assert(ses && ses->doc_view && doc_view && doc_view->vs);
459 if_assert_failed return FRAME_EVENT_OK;
460
461 if (doc_view == ses->doc_view) return FRAME_EVENT_OK;
462 goto_uri(ses, doc_view->vs->uri);
463 ses->navigate_mode = NAVIGATE_LINKWISE;
464
465 return FRAME_EVENT_OK;
466 }
467
468
469 void
toggle_plain_html(struct session * ses,struct document_view * doc_view,int xxxx)470 toggle_plain_html(struct session *ses, struct document_view *doc_view, int xxxx)
471 {
472 assert(ses && doc_view && ses->tab && ses->tab->term);
473 if_assert_failed return;
474
475 if (!doc_view->vs) {
476 nowhere_box(ses->tab->term, NULL);
477 return;
478 }
479
480 doc_view->vs->plain = !doc_view->vs->plain;
481 draw_formatted(ses, 1);
482 }
483
484 void
toggle_wrap_text(struct session * ses,struct document_view * doc_view,int xxxx)485 toggle_wrap_text(struct session *ses, struct document_view *doc_view, int xxxx)
486 {
487 assert(ses && doc_view && ses->tab && ses->tab->term);
488 if_assert_failed return;
489
490 if (!doc_view->vs) {
491 nowhere_box(ses->tab->term, NULL);
492 return;
493 }
494
495 doc_view->vs->wrap = !doc_view->vs->wrap;
496 draw_formatted(ses, 1);
497 }
498
499 /* Move the cursor to the document view co-ordinates provided as @x and @y,
500 * scroll the document if necessary, put us in cursor-routing navigation mode if
501 * that is not the current mode, and select any link under the cursor. */
502 enum frame_event_status
move_cursor(struct session * ses,struct document_view * doc_view,int x,int y)503 move_cursor(struct session *ses, struct document_view *doc_view, int x, int y)
504 {
505 enum frame_event_status status = FRAME_EVENT_REFRESH;
506 struct terminal *term = ses->tab->term;
507 struct box *box = &doc_view->box;
508 struct link *link;
509
510 /* If cursor was moved outside the document view scroll it, but only
511 * within the document canvas */
512 if (!is_in_box(box, x, y)) {
513 int max_height = doc_view->document->height - doc_view->vs->y;
514 int max_width = doc_view->document->width - doc_view->vs->x;
515
516 if (y < box->y) {
517 status = vertical_scroll(ses, doc_view, y - box->y);
518
519 } else if (y >= box->y + box->height && y <= max_height) {
520 status = vertical_scroll(ses, doc_view,
521 y - (box->y + box->height - 1));
522
523 } else if (x < box->x) {
524 status = horizontal_scroll(ses, doc_view, x - box->x);
525
526 } else if (x >= box->x + box->width && x <= max_width) {
527 status = horizontal_scroll(ses, doc_view,
528 x - (box->x + box->width - 1));
529 }
530
531 /* If the view was not scrolled there's nothing more to do */
532 if (status != FRAME_EVENT_REFRESH)
533 return status;
534
535 /* Restrict the cursor position within the current view */
536 int_bounds(&x, box->x, box->x + box->width - 1);
537 int_bounds(&y, box->y, box->y + box->height - 1);
538 }
539
540 /* Scrolling could have changed the navigation mode */
541 ses->navigate_mode = NAVIGATE_CURSOR_ROUTING;
542
543 link = get_link_at_coordinates(doc_view, x - box->x, y - box->y);
544 if (link) {
545 doc_view->vs->current_link = link - doc_view->document->links;
546 } else {
547 doc_view->vs->current_link = -1;
548 }
549
550 /* Set the unblockable cursor position and update the window pointer so
551 * stuff like the link menu will be drawn relative to the cursor. */
552 set_cursor(term, x, y, 0);
553 set_window_ptr(ses->tab, x, y);
554
555 return status;
556 }
557
558 enum frame_event_status
move_cursor_rel(struct session * ses,struct document_view * view,int rx,int ry)559 move_cursor_rel(struct session *ses, struct document_view *view,
560 int rx, int ry)
561 {
562 int count = eat_kbd_repeat_count(ses);
563 int x, y;
564
565 int_lower_bound(&count, 1);
566
567 x = ses->tab->x + rx*count;
568 y = ses->tab->y + ry*count;
569 return move_cursor(ses, view, x, y);
570 }
571
572 enum frame_event_status
move_cursor_left(struct session * ses,struct document_view * view)573 move_cursor_left(struct session *ses, struct document_view *view)
574 {
575 return move_cursor_rel(ses, view, -1, 0);
576 }
577
578 enum frame_event_status
move_cursor_right(struct session * ses,struct document_view * view)579 move_cursor_right(struct session *ses, struct document_view *view)
580 {
581 return move_cursor_rel(ses, view, 1, 0);
582 }
583
584 enum frame_event_status
move_cursor_up(struct session * ses,struct document_view * view)585 move_cursor_up(struct session *ses, struct document_view *view)
586 {
587 return move_cursor_rel(ses, view, 0, -1);
588 }
589
590 enum frame_event_status
move_cursor_down(struct session * ses,struct document_view * view)591 move_cursor_down(struct session *ses, struct document_view *view)
592 {
593 return move_cursor_rel(ses, view, 0, 1);
594 }
595
596
597 enum frame_event_status
copy_current_link_to_clipboard(struct session * ses,struct document_view * doc_view,int xxx)598 copy_current_link_to_clipboard(struct session *ses,
599 struct document_view *doc_view,
600 int xxx)
601 {
602 struct link *link;
603 struct uri *uri;
604 unsigned char *uristring;
605
606 link = get_current_link(doc_view);
607 if (!link) return FRAME_EVENT_OK;
608
609 uri = get_link_uri(ses, doc_view, link);
610 if (!uri) return FRAME_EVENT_OK;
611
612 uristring = get_uri_string(uri, URI_ORIGINAL);
613 done_uri(uri);
614
615 if (uristring) {
616 set_clipboard_text(uristring);
617 mem_free(uristring);
618 }
619
620 return FRAME_EVENT_OK;
621 }
622
623
624 int
try_jump_to_link_number(struct session * ses,struct document_view * doc_view)625 try_jump_to_link_number(struct session *ses, struct document_view *doc_view)
626 {
627 int link_number = eat_kbd_repeat_count(ses) - 1;
628
629 if (link_number < 0) return 1;
630
631 if (!doc_view) return 0;
632
633 if (link_number >= doc_view->document->nlinks)
634 return 0;
635
636 jump_to_link_number(ses, doc_view, link_number);
637 refresh_view(ses, doc_view, 0);
638
639 return 1;
640 }
641
642 #ifdef CONFIG_MARKS
643 enum frame_event_status
try_mark_key(struct session * ses,struct document_view * doc_view,struct term_event * ev)644 try_mark_key(struct session *ses, struct document_view *doc_view,
645 struct term_event *ev)
646 {
647 unsigned char mark = get_kbd_key(ev);
648
649 switch (ses->kbdprefix.mark) {
650 case KP_MARK_NOTHING:
651 return FRAME_EVENT_IGNORED;
652
653 case KP_MARK_SET:
654 /* It is intentional to set the mark
655 * to NULL if !doc_view->vs. */
656 set_mark(mark, doc_view->vs);
657 break;
658
659 case KP_MARK_GOTO:
660 goto_mark(mark, doc_view->vs);
661 break;
662 }
663
664 ses->kbdprefix.repeat_count = 0;
665 ses->kbdprefix.mark = KP_MARK_NOTHING;
666
667 return FRAME_EVENT_REFRESH;
668 }
669 #endif
670
671 static enum frame_event_status
try_prefix_key(struct session * ses,struct document_view * doc_view,struct term_event * ev)672 try_prefix_key(struct session *ses, struct document_view *doc_view,
673 struct term_event *ev)
674 {
675 struct document *document = doc_view->document;
676 struct document_options *doc_opts = &document->options;
677 int digit = get_kbd_key(ev) - '0';
678
679 if (digit < 0 || digit > 9)
680 return FRAME_EVENT_IGNORED;
681
682 if (get_kbd_modifier(ev)
683 || ses->kbdprefix.repeat_count /* The user has already begun
684 * entering a prefix. */
685 || !doc_opts->num_links_key
686 || (doc_opts->num_links_key == 1 && !doc_opts->links_numbering)) {
687 /* Repeat count.
688 * ses->kbdprefix.repeat_count is initialized to zero
689 * the first time by init_session() calloc() call.
690 * When used, it has to be reset to zero. */
691
692 ses->kbdprefix.repeat_count *= 10;
693 ses->kbdprefix.repeat_count += digit;
694
695 /* If too big, just restart from zero, so pressing
696 * '0' six times or more will reset the count. */
697 if (ses->kbdprefix.repeat_count > 99999)
698 ses->kbdprefix.repeat_count = 0;
699
700 return FRAME_EVENT_OK;
701 }
702
703 if (digit >= 1 && !get_kbd_modifier(ev)) {
704 int nlinks = document->nlinks, length;
705 unsigned char d[2] = { get_kbd_key(ev), 0 };
706
707 ses->kbdprefix.repeat_count = 0;
708
709 if (!nlinks) return FRAME_EVENT_OK;
710
711 for (length = 1; nlinks; nlinks /= 10)
712 length++;
713
714 input_dialog(ses->tab->term, NULL,
715 N_("Go to link"), N_("Enter link number"),
716 ses, NULL,
717 length, d, 1, document->nlinks, check_number,
718 (void (*)(void *, unsigned char *)) goto_link_number, NULL);
719
720 return FRAME_EVENT_OK;
721 }
722
723 return FRAME_EVENT_IGNORED;
724 }
725
726 static enum frame_event_status
try_form_insert_mode(struct session * ses,struct document_view * doc_view,struct link * link,struct term_event * ev)727 try_form_insert_mode(struct session *ses, struct document_view *doc_view,
728 struct link *link, struct term_event *ev)
729 {
730 enum frame_event_status status = FRAME_EVENT_IGNORED;
731 enum edit_action action_id;
732
733 if (!link_is_textinput(link))
734 return FRAME_EVENT_IGNORED;
735
736 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
737
738 if (ses->insert_mode == INSERT_MODE_OFF) {
739 if (action_id == ACT_EDIT_ENTER) {
740 ses->insert_mode = INSERT_MODE_ON;
741 status = FRAME_EVENT_REFRESH;
742 }
743 }
744
745 return status;
746 }
747
748 static enum frame_event_status
try_form_action(struct session * ses,struct document_view * doc_view,struct link * link,struct term_event * ev)749 try_form_action(struct session *ses, struct document_view *doc_view,
750 struct link *link, struct term_event *ev)
751 {
752 enum frame_event_status status;
753
754 assert(link);
755
756 if (!link_is_textinput(link))
757 return FRAME_EVENT_IGNORED;
758
759 status = field_op(ses, doc_view, link, ev);
760
761 if (status != FRAME_EVENT_IGNORED
762 && ses->insert_mode == INSERT_MODE_ON) {
763 assert(link == get_current_link(doc_view));
764 }
765
766 return status;
767 }
768
769 static enum frame_event_status
frame_ev_kbd(struct session * ses,struct document_view * doc_view,struct term_event * ev)770 frame_ev_kbd(struct session *ses, struct document_view *doc_view, struct term_event *ev)
771 {
772 enum frame_event_status status = FRAME_EVENT_IGNORED;
773 int accesskey_priority;
774 struct link *link = get_current_link(doc_view);
775
776 if (link) {
777 status = try_form_insert_mode(ses, doc_view, link, ev);
778 if (status != FRAME_EVENT_IGNORED)
779 return status;
780
781 status = try_form_action(ses, doc_view, link, ev);
782 if (status != FRAME_EVENT_IGNORED)
783 return status;
784 }
785
786 #ifdef CONFIG_MARKS
787 status = try_mark_key(ses, doc_view, ev);
788 if (status != FRAME_EVENT_IGNORED)
789 return status;
790 #endif
791 accesskey_priority = get_opt_int("document.browse.accesskey.priority");
792
793 if (accesskey_priority >= 2) {
794 status = try_document_key(ses, doc_view, ev);
795
796 if (status != FRAME_EVENT_IGNORED) {
797 /* The document ate the key! */
798 return status;
799 }
800 }
801
802 status = try_prefix_key(ses, doc_view, ev);
803 if (status != FRAME_EVENT_IGNORED)
804 return status;
805
806 if (accesskey_priority == 1) {
807 status = try_document_key(ses, doc_view, ev);
808
809 if (status != FRAME_EVENT_IGNORED) {
810 /* The document ate the key! */
811 return status;
812 }
813 }
814
815 return FRAME_EVENT_IGNORED;
816 }
817
818 #ifdef CONFIG_MOUSE
819 static enum frame_event_status
frame_ev_mouse(struct session * ses,struct document_view * doc_view,struct term_event * ev)820 frame_ev_mouse(struct session *ses, struct document_view *doc_view, struct term_event *ev)
821 {
822 int x = ev->info.mouse.x;
823 int y = ev->info.mouse.y;
824 struct link *link;
825
826 if (check_mouse_wheel(ev)) {
827 if (!check_mouse_action(ev, B_DOWN)) {
828 /* We handle only B_DOWN case... */
829 } else if (check_mouse_button(ev, B_WHEEL_UP)) {
830 return scroll_mouse_up(ses, doc_view);
831 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
832 return scroll_mouse_down(ses, doc_view);
833 }
834
835 return FRAME_EVENT_OK;
836 }
837
838 link = get_link_at_coordinates(doc_view, x, y);
839 if (link) {
840 enum frame_event_status status = FRAME_EVENT_REFRESH;
841
842 doc_view->vs->current_link = link - doc_view->document->links;
843 ses->navigate_mode = NAVIGATE_LINKWISE;
844
845 if (!link_is_textinput(link)) {
846
847 status = FRAME_EVENT_OK;
848
849 refresh_view(ses, doc_view, 0);
850
851 if (check_mouse_button(ev, B_LEFT)
852 || check_mouse_button(ev, B_MIDDLE)) {
853 if (check_mouse_action(ev, B_DOWN))
854 do_not_ignore_next_mouse_event(ses->tab->term);
855 else if (check_mouse_button(ev, B_LEFT))
856 status = enter(ses, doc_view, 0);
857 else if (check_mouse_button(ev, B_MIDDLE))
858 open_current_link_in_new_tab(ses, 1);
859 } else {
860 link_menu(ses->tab->term, NULL, ses);
861 }
862 }
863
864 return status;
865 }
866
867 if (check_mouse_button(ev, B_LEFT)) {
868 /* Clicking the edge of screen will scroll the document. */
869
870 int scrollmargin = get_opt_int("document.browse.scrolling.margin");
871
872 /* XXX: This is code duplication with kbd handlers. But
873 * repeatcount-free here. */
874
875 if (y < scrollmargin) {
876 return scroll_mouse_up(ses, doc_view);
877 }
878 if (y >= doc_view->box.height - scrollmargin) {
879 return scroll_mouse_down(ses, doc_view);
880 }
881
882 if (x < scrollmargin * 2) {
883 return scroll_mouse_left(ses, doc_view);
884 }
885 if (x >= doc_view->box.width - scrollmargin * 2) {
886 return scroll_mouse_right(ses, doc_view);
887 }
888
889 return FRAME_EVENT_OK;
890 }
891
892 return FRAME_EVENT_IGNORED;
893 }
894 #endif /* CONFIG_MOUSE */
895
896 static enum frame_event_status
frame_ev(struct session * ses,struct document_view * doc_view,struct term_event * ev)897 frame_ev(struct session *ses, struct document_view *doc_view, struct term_event *ev)
898 {
899 assertm(doc_view && doc_view->document, "document not formatted");
900 if_assert_failed return FRAME_EVENT_IGNORED;
901
902 assert(ses && ev);
903 if_assert_failed return FRAME_EVENT_IGNORED;
904
905 /* When changing frame, vs may be NULL. See bug 525. */
906 if (!doc_view->vs) return FRAME_EVENT_IGNORED;
907
908 switch (ev->ev) {
909 case EVENT_KBD:
910 return frame_ev_kbd(ses, doc_view, ev);
911 #ifdef CONFIG_MOUSE
912 case EVENT_MOUSE:
913 return frame_ev_mouse(ses, doc_view, ev);
914 #endif /* CONFIG_MOUSE */
915 default:
916 return FRAME_EVENT_IGNORED;
917 }
918 }
919
920 struct document_view *
current_frame(struct session * ses)921 current_frame(struct session *ses)
922 {
923 struct document_view *doc_view = NULL;
924 int current_frame_number;
925
926 assert(ses);
927 if_assert_failed return NULL;
928
929 if (!have_location(ses)) return NULL;
930
931 current_frame_number = cur_loc(ses)->vs.current_link;
932 if (current_frame_number == -1) current_frame_number = 0;
933
934 foreach (doc_view, ses->scrn_frames) {
935 if (document_has_frames(doc_view->document)) continue;
936 if (!current_frame_number--) return doc_view;
937 }
938
939 doc_view = ses->doc_view;
940
941 assert(doc_view && doc_view->document);
942 if_assert_failed return NULL;
943
944 if (document_has_frames(doc_view->document)) return NULL;
945 return doc_view;
946 }
947
948 static enum frame_event_status
send_to_frame(struct session * ses,struct document_view * doc_view,struct term_event * ev)949 send_to_frame(struct session *ses, struct document_view *doc_view,
950 struct term_event *ev)
951 {
952 enum frame_event_status status;
953
954 assert(ses && ses->tab && ses->tab->term && ev);
955 if_assert_failed return FRAME_EVENT_IGNORED;
956
957 status = frame_ev(ses, doc_view, ev);
958
959 if (status == FRAME_EVENT_REFRESH)
960 refresh_view(ses, doc_view, 0);
961 else
962 print_screen_status(ses);
963
964 return status;
965 }
966
967 #ifdef CONFIG_MOUSE
968 static int
do_mouse_event(struct session * ses,struct term_event * ev,struct document_view * doc_view)969 do_mouse_event(struct session *ses, struct term_event *ev,
970 struct document_view *doc_view)
971 {
972 struct term_event evv;
973 struct document_view *matched = NULL, *first = doc_view;
974
975 assert(ses && ev);
976 if_assert_failed return 0;
977
978 if (!doc_view) return 0;
979
980 do {
981 assert(doc_view && doc_view->document);
982 if_assert_failed return 0;
983
984 assertm(doc_view->document->options.box.x == doc_view->box.x
985 && doc_view->document->options.box.y == doc_view->box.y,
986 "Jonas' 1.565 -> 1.566 patch sucks");
987 if_assert_failed return 0;
988
989 if (check_mouse_position(ev, &doc_view->box)) {
990 matched = doc_view;
991 break;
992 }
993
994 next_frame(ses, 1);
995 doc_view = current_frame(ses);
996
997 } while (doc_view != first);
998
999 if (!matched) return 0;
1000
1001 if (doc_view != first) draw_formatted(ses, 0);
1002
1003 set_mouse_term_event(&evv,
1004 ev->info.mouse.x - doc_view->box.x,
1005 ev->info.mouse.y - doc_view->box.y,
1006 ev->info.mouse.button);
1007
1008 return send_to_frame(ses, doc_view, &evv);
1009 }
1010
1011 /* Returns the session if event cleanup should be done or NULL if no cleanup is
1012 * needed. */
1013 static struct session *
send_mouse_event(struct session * ses,struct document_view * doc_view,struct term_event * ev)1014 send_mouse_event(struct session *ses, struct document_view *doc_view,
1015 struct term_event *ev)
1016 {
1017 struct terminal *term = ses->tab->term;
1018 struct term_event_mouse *mouse = &ev->info.mouse;
1019
1020 if (mouse->y == 0
1021 && check_mouse_action(ev, B_DOWN)
1022 && !check_mouse_wheel(ev)) {
1023 struct window *m;
1024
1025 activate_bfu_technology(ses, -1);
1026 m = term->windows.next;
1027 m->handler(m, ev);
1028
1029 return ses;
1030 }
1031
1032 /* Handle tabs navigation if tabs bar is displayed. */
1033 if (ses->status.show_tabs_bar
1034 && mouse->y == term->height - 1 - !!ses->status.show_status_bar) {
1035 int tab_num = get_tab_number_by_xpos(term, mouse->x);
1036 struct window *tab = get_current_tab(term);
1037
1038 if (check_mouse_action(ev, B_UP)) {
1039 if (check_mouse_button(ev, B_MIDDLE)
1040 && term->current_tab == tab_num
1041 && mouse->y == term->prev_mouse_event.y) {
1042 if (tab->data == ses) ses = NULL;
1043
1044 close_tab(term, tab->data);
1045 }
1046
1047 return ses;
1048 }
1049
1050 if (check_mouse_button(ev, B_WHEEL_UP)) {
1051 switch_current_tab(ses, -1);
1052
1053 } else if (check_mouse_button(ev, B_WHEEL_DOWN)) {
1054 switch_current_tab(ses, 1);
1055
1056 } else if (tab_num != -1) {
1057 switch_to_tab(term, tab_num, -1);
1058
1059 if (check_mouse_button(ev, B_MIDDLE)) {
1060 do_not_ignore_next_mouse_event(term);
1061 } else if (check_mouse_button(ev, B_RIGHT)) {
1062 tab_menu(tab->data, mouse->x, mouse->y, 1);
1063 }
1064 }
1065
1066 return ses;
1067 }
1068
1069 if (!do_mouse_event(ses, ev, doc_view)
1070 && check_mouse_button(ev, B_RIGHT)) {
1071 tab_menu(ses, mouse->x, mouse->y, 0);
1072 return NULL;
1073 }
1074
1075 #ifdef CONFIG_LEDS
1076 if (ses->status.show_leds
1077 && mouse->y == term->height - 1
1078 && mouse->x >= term->width - LEDS_COUNT - 3) {
1079 menu_leds_info(term, NULL, NULL);
1080 return NULL;
1081 }
1082 #endif
1083
1084 return NULL;
1085 }
1086 #endif /* CONFIG_MOUSE */
1087
1088 static void
try_typeahead(struct session * ses,struct document_view * doc_view,struct term_event * ev,enum main_action action_id)1089 try_typeahead(struct session *ses, struct document_view *doc_view,
1090 struct term_event *ev, enum main_action action_id)
1091 {
1092 switch (get_opt_int("document.browse.search.typeahead")) {
1093 case 0:
1094 return;
1095 case 1:
1096 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_LINK;
1097 break;
1098 case 2:
1099 action_id = ACT_MAIN_SEARCH_TYPEAHEAD_TEXT;
1100 break;
1101 default:
1102 INTERNAL("invalid value for document.browse.search.typeahead");
1103 }
1104
1105 search_typeahead(ses, doc_view, action_id);
1106
1107 /* Cross your fingers -- I'm just asking
1108 * for an infinite loop! -- Miciah */
1109 term_send_event(ses->tab->term, ev);
1110 }
1111
1112 /* Returns the session if event cleanup should be done or NULL if no cleanup is
1113 * needed. */
1114 static struct session *
send_kbd_event(struct session * ses,struct document_view * doc_view,struct term_event * ev)1115 send_kbd_event(struct session *ses, struct document_view *doc_view,
1116 struct term_event *ev)
1117 {
1118 int event;
1119 enum main_action action_id;
1120
1121 if (doc_view && send_to_frame(ses, doc_view, ev) != FRAME_EVENT_IGNORED)
1122 return NULL;
1123
1124 action_id = kbd_action(KEYMAP_MAIN, ev, &event);
1125
1126 if (action_id == ACT_MAIN_QUIT) {
1127 if (check_kbd_key(ev, KBD_CTRL_C))
1128 quit:
1129 action_id = ACT_MAIN_REALLY_QUIT;
1130 }
1131
1132 switch (do_action(ses, action_id, 0)) {
1133 case FRAME_EVENT_SESSION_DESTROYED:
1134 return NULL;
1135 case FRAME_EVENT_IGNORED:
1136 break;
1137 case FRAME_EVENT_OK:
1138 case FRAME_EVENT_REFRESH:
1139 return ses;
1140 }
1141
1142 if (action_id == ACT_MAIN_SCRIPTING_FUNCTION) {
1143 #ifdef CONFIG_SCRIPTING
1144 trigger_event(event, ses);
1145 #endif
1146 return NULL;
1147 }
1148
1149 if (check_kbd_key(ev, KBD_CTRL_C)) goto quit;
1150
1151 if (get_kbd_modifier(ev) & KBD_MOD_ALT) {
1152 struct window *win;
1153
1154 get_kbd_modifier(ev) &= ~KBD_MOD_ALT;
1155 activate_bfu_technology(ses, -1);
1156 win = ses->tab->term->windows.next;
1157 win->handler(win, ev);
1158 if (ses->tab->term->windows.next == win) {
1159 delete_window(win);
1160 get_kbd_modifier(ev) |= KBD_MOD_ALT;
1161
1162 return NULL;
1163 }
1164
1165 if (doc_view
1166 && get_opt_int("document.browse.accesskey"
1167 ".priority") <= 0
1168 && try_document_key(ses, doc_view, ev)
1169 == FRAME_EVENT_REFRESH) {
1170 /* The document ate the key! */
1171 refresh_view(ses, doc_view, 0);
1172
1173 return NULL;
1174 }
1175
1176 return ses;
1177 }
1178
1179 if (!(get_kbd_modifier(ev) & KBD_MOD_CTRL)) {
1180 if (doc_view) try_typeahead(ses, doc_view, ev, action_id);
1181 }
1182
1183 return NULL;
1184 }
1185
1186 void
send_event(struct session * ses,struct term_event * ev)1187 send_event(struct session *ses, struct term_event *ev)
1188 {
1189 assert(ses && ev);
1190 if_assert_failed return;
1191
1192 if (ev->ev == EVENT_KBD) {
1193 struct document_view *doc_view = current_frame(ses);
1194
1195 ses = send_kbd_event(ses, doc_view, ev);
1196 }
1197 #ifdef CONFIG_MOUSE
1198 else if (ev->ev == EVENT_MOUSE) {
1199 struct document_view *doc_view = current_frame(ses);
1200
1201 ses = send_mouse_event(ses, doc_view, ev);
1202 }
1203 #endif /* CONFIG_MOUSE */
1204
1205 /* @ses may disappear ie. in close_tab() */
1206 if (ses) ses->kbdprefix.repeat_count = 0;
1207 }
1208
1209 enum frame_event_status
download_link(struct session * ses,struct document_view * doc_view,action_id_T action_id)1210 download_link(struct session *ses, struct document_view *doc_view,
1211 action_id_T action_id)
1212 {
1213 struct link *link = get_current_link(doc_view);
1214 void (*download)(void *ses, unsigned char *file) = start_download;
1215
1216 if (!link) return FRAME_EVENT_OK;
1217
1218 if (ses->download_uri) {
1219 done_uri(ses->download_uri);
1220 ses->download_uri = NULL;
1221 }
1222
1223 switch (action_id) {
1224 case ACT_MAIN_LINK_DOWNLOAD_RESUME:
1225 download = resume_download;
1226 case ACT_MAIN_LINK_DOWNLOAD:
1227 ses->download_uri = get_link_uri(ses, doc_view, link);
1228 break;
1229
1230 case ACT_MAIN_LINK_DOWNLOAD_IMAGE:
1231 if (!link->where_img) break;
1232 ses->download_uri = get_uri(link->where_img, 0);
1233 break;
1234
1235 default:
1236 INTERNAL("I think you forgot to take your medication, mental boy!");
1237 return FRAME_EVENT_OK;
1238 }
1239
1240 if (!ses->download_uri) return FRAME_EVENT_OK;
1241
1242 set_session_referrer(ses, doc_view->document->uri);
1243 query_file(ses, ses->download_uri, ses, download, NULL, 1);
1244
1245 return FRAME_EVENT_OK;
1246 }
1247
1248 enum frame_event_status
view_image(struct session * ses,struct document_view * doc_view,int xxxx)1249 view_image(struct session *ses, struct document_view *doc_view, int xxxx)
1250 {
1251 struct link *link = get_current_link(doc_view);
1252
1253 if (link && link->where_img)
1254 goto_url(ses, link->where_img);
1255
1256 return FRAME_EVENT_OK;
1257 }
1258
1259 enum frame_event_status
save_as(struct session * ses,struct document_view * doc_view,int magic)1260 save_as(struct session *ses, struct document_view *doc_view, int magic)
1261 {
1262 assert(ses);
1263 if_assert_failed return FRAME_EVENT_OK;
1264
1265 if (!have_location(ses)) return FRAME_EVENT_OK;
1266
1267 if (ses->download_uri) done_uri(ses->download_uri);
1268 ses->download_uri = get_uri_reference(cur_loc(ses)->vs.uri);
1269
1270 assert(doc_view && doc_view->document && doc_view->document->uri);
1271 if_assert_failed return FRAME_EVENT_OK;
1272
1273 set_session_referrer(ses, doc_view->document->uri);
1274 query_file(ses, ses->download_uri, ses, start_download, NULL, 1);
1275
1276 return FRAME_EVENT_OK;
1277 }
1278
1279 static void
save_formatted_finish(struct terminal * term,int h,void * data,int resume)1280 save_formatted_finish(struct terminal *term, int h, void *data, int resume)
1281 {
1282 struct document *document = data;
1283
1284 assert(term && document);
1285 if_assert_failed return;
1286
1287 if (h == -1) return;
1288 if (dump_to_file(document, h)) {
1289 info_box(term, 0, N_("Save error"), ALIGN_CENTER,
1290 N_("Error writing to file"));
1291 }
1292 close(h);
1293 }
1294
1295 static void
save_formatted(void * data,unsigned char * file)1296 save_formatted(void *data, unsigned char *file)
1297 {
1298 struct session *ses = data;
1299 struct document_view *doc_view;
1300
1301 assert(ses && ses->tab && ses->tab->term && file);
1302 if_assert_failed return;
1303 doc_view = current_frame(ses);
1304 assert(doc_view && doc_view->document);
1305 if_assert_failed return;
1306
1307 create_download_file(ses->tab->term, file, NULL, 0, 0,
1308 save_formatted_finish, doc_view->document);
1309 }
1310
1311 enum frame_event_status
save_formatted_dlg(struct session * ses,struct document_view * doc_view,int xxxx)1312 save_formatted_dlg(struct session *ses, struct document_view *doc_view, int xxxx)
1313 {
1314 query_file(ses, doc_view->vs->uri, ses, save_formatted, NULL, 1);
1315 return FRAME_EVENT_OK;
1316 }
1317