1 /** Text mode drawing functions
16 #include "bfu/dialog.h"
17 #include "cache/cache.h"
18 #include "document/document.h"
19 #include "document/html/frames.h"
20 #include "document/options.h"
21 #include "document/refresh.h"
22 #include "document/renderer.h"
23 #include "document/view.h"
24 #include "dialogs/status.h" /* print_screen_status() */
25 #include "intl/charsets.h"
26 #include "intl/gettext/libintl.h"
27 #include "protocol/uri.h"
28 #include "session/location.h"
29 #include "session/session.h"
30 #include "terminal/draw.h"
31 #include "terminal/image.h"
32 #include "terminal/tab.h"
33 #include "terminal/terminal.h"
34 #include "util/error.h"
35 #include "util/lists.h"
36 #include "util/memory.h"
37 #include "util/string.h"
38 #include "viewer/text/draw.h"
39 #include "viewer/text/form.h"
40 #include "viewer/text/link.h"
41 #include "viewer/text/search.h"
42 #include "viewer/text/view.h" /* current_frame() */
43 #include "viewer/text/vs.h"
47 check_document_fragment(struct session
*ses
, struct document_view
*doc_view
)
49 struct document
*document
= doc_view
->document
;
50 struct uri
*uri
= doc_view
->vs
->uri
;
52 struct string fragment
;
54 assert(uri
->fragmentlen
);
56 if (!init_string(&fragment
)) return -2;
57 if (!add_uri_to_string(&fragment
, uri
, URI_FRAGMENT
)) {
58 done_string(&fragment
);
61 decode_uri_string(&fragment
);
62 assert(fragment
.length
);
63 assert(*fragment
.source
);
65 /* Omit the leading '#' when calling find_tag. */
66 vy
= find_tag(document
, fragment
.source
+ 1, fragment
.length
- 1);
68 struct cache_entry
*cached
= document
->cached
;
71 if (cached
->incomplete
|| cached
->cache_id
!= document
->cache_id
) {
72 done_string(&fragment
);
76 if (get_opt_bool("document.browse.links.missing_fragment",
78 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
79 N_("Missing fragment"), ALIGN_CENTER
,
80 msg_text(ses
->tab
->term
, N_("The requested fragment "
81 "\"%s\" doesn't exist."),
85 int_bounds(&vy
, 0, document
->height
- 1);
88 done_string(&fragment
);
94 draw_frame_lines(struct terminal
*term
, struct frameset_desc
*frameset_desc
,
95 int xp
, int yp
, struct color_pair
*colors
)
99 assert(term
&& frameset_desc
&& frameset_desc
->frame_desc
);
100 if_assert_failed
return;
103 for (j
= 0; j
< frameset_desc
->box
.height
; j
++) {
105 int height
= frameset_desc
->frame_desc
[j
* frameset_desc
->box
.width
].height
;
108 for (i
= 0; i
< frameset_desc
->box
.width
; i
++) {
109 int width
= frameset_desc
->frame_desc
[i
].width
;
114 set_box(&box
, x
, y
+ 1, 1, height
);
115 draw_box(term
, &box
, BORDER_SVLINE
, SCREEN_ATTR_FRAME
, colors
);
117 if (j
== frameset_desc
->box
.height
- 1)
118 draw_border_cross(term
, x
, y
+ height
+ 1,
119 BORDER_X_UP
, colors
);
122 draw_border_cross(term
, x
, y
,
123 BORDER_X_RIGHT
, colors
);
129 set_box(&box
, x
+ 1, y
, width
, 1);
130 draw_box(term
, &box
, BORDER_SHLINE
, SCREEN_ATTR_FRAME
, colors
);
132 if (i
== frameset_desc
->box
.width
- 1
133 && x
+ width
+ 1 < term
->width
)
134 draw_border_cross(term
, x
+ width
+ 1, y
,
135 BORDER_X_LEFT
, colors
);
137 draw_border_cross(term
, x
, y
, BORDER_X_DOWN
, colors
);
141 draw_border_char(term
, x
, y
, BORDER_SCROSS
, colors
);
149 for (j
= 0; j
< frameset_desc
->box
.height
; j
++) {
151 int pj
= j
* frameset_desc
->box
.width
;
152 int height
= frameset_desc
->frame_desc
[pj
].height
;
155 for (i
= 0; i
< frameset_desc
->box
.width
; i
++) {
156 int width
= frameset_desc
->frame_desc
[i
].width
;
159 if (frameset_desc
->frame_desc
[p
].subframe
) {
160 draw_frame_lines(term
, frameset_desc
->frame_desc
[p
].subframe
,
161 x
+ 1, y
+ 1, colors
);
170 draw_images(struct session
*ses
, struct document_view
*doc_view
, struct view_state
*vs
, struct box
*win_box
)
172 struct document
*document
= doc_view
->document
;
173 struct terminal
*term
= ses
->tab
->term
;
176 /* @vs_pixbox is a bounding box representing the viewport in
177 * the document (coordinates relative to document top left), while
178 * @win_pixbox is a bounding box representing the screen tab window.
179 * "Pixbox" means that the dimensions aren't characters but pixels. */
180 struct box win_pixbox
;
181 struct box vs_pixbox
;
183 assert(document
&& term
);
184 if_assert_failed
return;
186 /* Character width, height in pixels. */
187 chw
= term
->xres
/ term
->width
;
188 chh
= term
->yres
/ term
->height
;
189 //printf("RES %d/%d->%d %d/%d->%d\n", term->xres, term->width, chw, term->yres, term->height, chh);
191 set_box(&win_pixbox
, win_box
->x
* chw
, win_box
->y
* chh
, win_box
->width
* chw
, win_box
->height
* chh
);
192 set_box(&vs_pixbox
, vs
->x
* chw
, vs
->y
* chh
, win_pixbox
.width
, win_pixbox
.height
);
194 //printf("ni %d\n", document->nimages);
196 for (i
= 0; i
< document
->nimages
; i
++) {
197 struct image
*img
= &document
->images
[i
];
198 struct cache_entry
*cached
;
199 struct box img_pixbox
;
201 /* Fetch cache entry. */
202 cached
= get_redirected_cache_entry(img
->uri
);
203 //printf("ce %p %d\n", cached, cached ? cached->incomplete : -1);
204 if (!cached
|| cached
->incomplete
)
207 /* Get image dimensions, if unknown. */
208 if (img
->w
< 0 || img
->h
< 0) {
211 get_image_size(cached
, &w
, &h
);
212 if (img
->w
< 0) img
->w
= w
;
213 if (img
->h
< 0) img
->h
= h
;
216 set_box(&img_pixbox
, img
->pos
.x
* chw
, img
->pos
.y
* chh
, img
->w
, img
->h
);
218 if (is_in_box(&vs_pixbox
, img_pixbox
.x
, img_pixbox
.y
)) {
219 /* Arrange the image in the tab window bounding box. */
220 img_pixbox
.x
= win_pixbox
.x
+ img_pixbox
.x
- vs_pixbox
.x
;
221 img_pixbox
.y
= win_pixbox
.y
+ img_pixbox
.y
- vs_pixbox
.y
;
222 img_pixbox
.width
= int_min(img_pixbox
.width
, win_pixbox
.width
- img_pixbox
.x
);
223 img_pixbox
.height
= int_min(img_pixbox
.height
, win_pixbox
.height
- img_pixbox
.y
);
225 show_image(term
, cnt
++, &img_pixbox
, cached
);
233 draw_view_status(struct session
*ses
, struct document_view
*doc_view
, int active
)
235 struct terminal
*term
= ses
->tab
->term
;
237 draw_forms(term
, doc_view
);
239 draw_searched(term
, doc_view
);
240 draw_current_link(ses
, doc_view
);
244 /** Checks if there is a link under the cursor so it can become the current
245 * highlighted link. */
247 check_link_under_cursor(struct session
*ses
, struct document_view
*doc_view
)
251 struct box
*box
= &doc_view
->box
;
254 link
= get_link_at_coordinates(doc_view
, x
- box
->x
, y
- box
->y
);
255 if (link
&& link
!= get_current_link(doc_view
)) {
256 doc_view
->vs
->current_link
= link
- doc_view
->document
->links
;
260 /** Puts the formatted document on the given terminal's screen.
261 * @a active indicates whether the document is focused -- i.e.,
262 * whether it is displayed in the selected frame or document. */
264 draw_doc(struct session
*ses
, struct document_view
*doc_view
, int active
)
266 struct color_pair color
;
267 struct view_state
*vs
;
268 struct terminal
*term
;
273 assert(ses
&& ses
->tab
&& ses
->tab
->term
&& doc_view
);
274 if_assert_failed
return;
276 box
= &doc_view
->box
;
277 term
= ses
->tab
->term
;
279 /* The code in this function assumes that both width and height are
280 * bigger than 1 so we have to bail out here. */
281 if (box
->width
< 2 || box
->height
< 2) return;
284 /* When redrawing the document after things like link menu we
285 * have to reset the cursor routing state. */
286 if (ses
->navigate_mode
== NAVIGATE_CURSOR_ROUTING
) {
287 set_cursor(term
, ses
->tab
->x
, ses
->tab
->y
, 0);
289 set_cursor(term
, box
->x
+ box
->width
- 1, box
->y
+ box
->height
- 1, 1);
290 set_window_ptr(ses
->tab
, box
->x
, box
->y
);
294 color
.foreground
= get_opt_color("document.colors.text", ses
);
295 color
.background
= doc_view
->document
->height
296 ? doc_view
->document
->color
.background
297 : get_opt_color("document.colors.background", ses
);
301 draw_box(term
, box
, ' ', 0, &color
);
305 if (document_has_frames(doc_view
->document
)) {
306 draw_box(term
, box
, ' ', 0, &color
);
307 draw_frame_lines(term
, doc_view
->document
->frame_desc
, box
->x
, box
->y
, &color
);
308 if (vs
->current_link
== -1)
309 vs
->current_link
= 0;
313 if (ses
->navigate_mode
== NAVIGATE_LINKWISE
) {
317 check_link_under_cursor(ses
, doc_view
);
320 if (!vs
->did_fragment
) {
321 vy
= check_document_fragment(ses
, doc_view
);
323 if (vy
!= -2) vs
->did_fragment
= 1;
325 doc_view
->vs
->y
= vy
;
331 if (doc_view
->last_x
!= -1
332 && doc_view
->last_x
== vx
333 && doc_view
->last_y
== vy
334 && !has_search_word(doc_view
)) {
335 clear_link(term
, doc_view
);
336 draw_view_status(ses
, doc_view
, active
);
339 doc_view
->last_x
= vx
;
340 doc_view
->last_y
= vy
;
341 draw_box(term
, box
, ' ', 0, &color
);
342 if (!doc_view
->document
->height
) return;
344 while (vs
->y
>= doc_view
->document
->height
) vs
->y
-= box
->height
;
345 int_lower_bound(&vs
->y
, 0);
348 if (ses
->navigate_mode
== NAVIGATE_LINKWISE
)
351 for (y
= int_max(vy
, 0);
352 y
< int_min(doc_view
->document
->height
, box
->height
+ vy
);
354 int st
= int_max(vx
, 0);
355 int en
= int_min(doc_view
->document
->data
[y
].length
,
358 if (en
- st
<= 0) continue;
359 draw_line(term
, box
->x
+ st
- vx
, box
->y
+ y
- vy
, en
- st
,
360 &doc_view
->document
->data
[y
].chars
[st
]);
362 draw_images(ses
, doc_view
, vs
, box
);
363 draw_view_status(ses
, doc_view
, active
);
364 if (has_search_word(doc_view
))
365 doc_view
->last_x
= doc_view
->last_y
= -1;
369 draw_frames(struct session
*ses
)
371 struct document_view
*doc_view
, *current_doc_view
;
375 assert(ses
&& ses
->doc_view
&& ses
->doc_view
->document
);
376 if_assert_failed
return;
378 if (!document_has_frames(ses
->doc_view
->document
)) return;
381 foreach (doc_view
, ses
->scrn_frames
) {
382 doc_view
->last_x
= doc_view
->last_y
= -1;
385 l
= &cur_loc(ses
)->vs
.current_link
;
386 *l
= int_max(*l
, 0) % int_max(n
, 1);
388 current_doc_view
= current_frame(ses
);
393 foreach (doc_view
, ses
->scrn_frames
) {
394 if (doc_view
->depth
== d
)
395 draw_doc(ses
, doc_view
, doc_view
== current_doc_view
);
396 else if (doc_view
->depth
> d
)
405 /** @todo @a rerender is ridiciously wound-up. */
407 draw_formatted(struct session
*ses
, int rerender
)
409 assert(ses
&& ses
->tab
);
410 if_assert_failed
return;
413 rerender
--; /* Mind this when analyzing @rerender. */
414 if (!(rerender
& 2) && session_is_loading(ses
))
416 render_document_frames(ses
, rerender
);
418 /* Rerendering kills the document refreshing so restart it. */
419 start_document_refreshes(ses
);
422 if (ses
->tab
!= get_current_tab(ses
->tab
->term
))
425 if (!ses
->doc_view
|| !ses
->doc_view
->document
) {
426 /*INTERNAL("document not formatted");*/
430 ses
->tab
->term
->width
,
431 ses
->tab
->term
->height
- 2);
432 draw_box(ses
->tab
->term
, &box
, ' ', 0, NULL
);
436 if (!ses
->doc_view
->vs
&& have_location(ses
))
437 ses
->doc_view
->vs
= &cur_loc(ses
)->vs
;
438 ses
->doc_view
->last_x
= ses
->doc_view
->last_y
= -1;
440 refresh_view(ses
, ses
->doc_view
, 1);
444 refresh_view(struct session
*ses
, struct document_view
*doc_view
, int frames
)
446 /* If refresh_view() is being called because the value of a
447 * form field has changed, @ses might not be in the current
448 * tab: consider SELECT pop-ups behind which -remote loads
449 * another tab, or setTimeout in ECMAScript. */
450 if (ses
->tab
== get_current_tab(ses
->tab
->term
)) {
451 draw_doc(ses
, doc_view
, 1);
452 if (frames
) draw_frames(ses
);
454 print_screen_status(ses
);