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