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
;
178 assert(document
&& term
);
179 if_assert_failed
return;
181 /* Character width, height in pixels. */
182 chw
= term
->xres
/ term
->width
;
183 chh
= term
->yres
/ term
->height
;
184 //printf("RES %d/%d->%d %d/%d->%d\n", term->xres, term->width, chw, term->yres, term->height, chh);
186 /* @vs_box is a bounding box representing the viewport in the document
187 * (coordinates relative to document top left), while
188 * @win_box is a bounding box representing the screen tab window. */
189 copy_box(&vs_box
, win_box
);
193 //printf("ni %d\n", document->nimages);
195 for (i
= 0; i
< document
->nimages
; i
++) {
196 struct image
*img
= &document
->images
[i
];
197 /* XXX: x,y in chars, width,height in pixels */
198 struct box screen_img
= {
199 .x
= img
->pos
.x
- vs_box
.x
,
200 .y
= img
->pos
.y
- vs_box
.y
,
205 //printf("imgpos %d %d,%d\n", i, img->pos.x, img->pos.y);
207 if (is_in_box(&vs_box
, img
->pos
.x
, img
->pos
.y
)) {
208 struct cache_entry
*cached
;
209 struct box pixel_box
;
211 /* Fetch cache entry. */
212 cached
= get_redirected_cache_entry(img
->uri
);
213 //printf("ce %p %d\n", cached, cached ? cached->incomplete : -1);
214 if (!cached
|| cached
->incomplete
)
217 /* Get image dimensions, if unknown. */
218 if (screen_img
.width
< 0 || screen_img
.height
< 0) {
219 get_image_size(cached
, &screen_img
.width
, &screen_img
.height
);
220 if (img
->w
< 0) img
->w
= screen_img
.width
;
221 if (img
->h
< 0) img
->h
= screen_img
.height
;
223 /* Trim image by bounding box. */
224 screen_img
.width
= int_min(screen_img
.width
, (win_box
->width
- screen_img
.x
) * chw
);
225 screen_img
.height
= int_min(screen_img
.height
, (win_box
->height
- screen_img
.y
) * chh
);
227 set_box(&pixel_box
, (win_box
->x
+ screen_img
.x
) * chw
, (win_box
->y
+ screen_img
.y
) * chh
, screen_img
.width
, screen_img
.height
);
229 show_image(term
, cnt
++, &pixel_box
, cached
);
237 draw_view_status(struct session
*ses
, struct document_view
*doc_view
, int active
)
239 struct terminal
*term
= ses
->tab
->term
;
241 draw_forms(term
, doc_view
);
243 draw_searched(term
, doc_view
);
244 draw_current_link(ses
, doc_view
);
248 /** Checks if there is a link under the cursor so it can become the current
249 * highlighted link. */
251 check_link_under_cursor(struct session
*ses
, struct document_view
*doc_view
)
255 struct box
*box
= &doc_view
->box
;
258 link
= get_link_at_coordinates(doc_view
, x
- box
->x
, y
- box
->y
);
259 if (link
&& link
!= get_current_link(doc_view
)) {
260 doc_view
->vs
->current_link
= link
- doc_view
->document
->links
;
264 /** Puts the formatted document on the given terminal's screen.
265 * @a active indicates whether the document is focused -- i.e.,
266 * whether it is displayed in the selected frame or document. */
268 draw_doc(struct session
*ses
, struct document_view
*doc_view
, int active
)
270 struct color_pair color
;
271 struct view_state
*vs
;
272 struct terminal
*term
;
277 assert(ses
&& ses
->tab
&& ses
->tab
->term
&& doc_view
);
278 if_assert_failed
return;
280 box
= &doc_view
->box
;
281 term
= ses
->tab
->term
;
283 /* The code in this function assumes that both width and height are
284 * bigger than 1 so we have to bail out here. */
285 if (box
->width
< 2 || box
->height
< 2) return;
288 /* When redrawing the document after things like link menu we
289 * have to reset the cursor routing state. */
290 if (ses
->navigate_mode
== NAVIGATE_CURSOR_ROUTING
) {
291 set_cursor(term
, ses
->tab
->x
, ses
->tab
->y
, 0);
293 set_cursor(term
, box
->x
+ box
->width
- 1, box
->y
+ box
->height
- 1, 1);
294 set_window_ptr(ses
->tab
, box
->x
, box
->y
);
298 color
.foreground
= get_opt_color("document.colors.text", ses
);
299 color
.background
= doc_view
->document
->height
300 ? doc_view
->document
->color
.background
301 : get_opt_color("document.colors.background", ses
);
305 draw_box(term
, box
, ' ', 0, &color
);
309 if (document_has_frames(doc_view
->document
)) {
310 draw_box(term
, box
, ' ', 0, &color
);
311 draw_frame_lines(term
, doc_view
->document
->frame_desc
, box
->x
, box
->y
, &color
);
312 if (vs
->current_link
== -1)
313 vs
->current_link
= 0;
317 if (ses
->navigate_mode
== NAVIGATE_LINKWISE
) {
321 check_link_under_cursor(ses
, doc_view
);
324 if (!vs
->did_fragment
) {
325 vy
= check_document_fragment(ses
, doc_view
);
327 if (vy
!= -2) vs
->did_fragment
= 1;
329 doc_view
->vs
->y
= vy
;
335 if (doc_view
->last_x
!= -1
336 && doc_view
->last_x
== vx
337 && doc_view
->last_y
== vy
338 && !has_search_word(doc_view
)) {
339 clear_link(term
, doc_view
);
340 draw_view_status(ses
, doc_view
, active
);
343 doc_view
->last_x
= vx
;
344 doc_view
->last_y
= vy
;
345 draw_box(term
, box
, ' ', 0, &color
);
346 if (!doc_view
->document
->height
) return;
348 while (vs
->y
>= doc_view
->document
->height
) vs
->y
-= box
->height
;
349 int_lower_bound(&vs
->y
, 0);
352 if (ses
->navigate_mode
== NAVIGATE_LINKWISE
)
355 for (y
= int_max(vy
, 0);
356 y
< int_min(doc_view
->document
->height
, box
->height
+ vy
);
358 int st
= int_max(vx
, 0);
359 int en
= int_min(doc_view
->document
->data
[y
].length
,
362 if (en
- st
<= 0) continue;
363 draw_line(term
, box
->x
+ st
- vx
, box
->y
+ y
- vy
, en
- st
,
364 &doc_view
->document
->data
[y
].chars
[st
]);
366 draw_images(ses
, doc_view
, vs
, box
);
367 draw_view_status(ses
, doc_view
, active
);
368 if (has_search_word(doc_view
))
369 doc_view
->last_x
= doc_view
->last_y
= -1;
373 draw_frames(struct session
*ses
)
375 struct document_view
*doc_view
, *current_doc_view
;
379 assert(ses
&& ses
->doc_view
&& ses
->doc_view
->document
);
380 if_assert_failed
return;
382 if (!document_has_frames(ses
->doc_view
->document
)) return;
385 foreach (doc_view
, ses
->scrn_frames
) {
386 doc_view
->last_x
= doc_view
->last_y
= -1;
389 l
= &cur_loc(ses
)->vs
.current_link
;
390 *l
= int_max(*l
, 0) % int_max(n
, 1);
392 current_doc_view
= current_frame(ses
);
397 foreach (doc_view
, ses
->scrn_frames
) {
398 if (doc_view
->depth
== d
)
399 draw_doc(ses
, doc_view
, doc_view
== current_doc_view
);
400 else if (doc_view
->depth
> d
)
409 /** @todo @a rerender is ridiciously wound-up. */
411 draw_formatted(struct session
*ses
, int rerender
)
413 assert(ses
&& ses
->tab
);
414 if_assert_failed
return;
417 rerender
--; /* Mind this when analyzing @rerender. */
418 if (!(rerender
& 2) && session_is_loading(ses
))
420 render_document_frames(ses
, rerender
);
422 /* Rerendering kills the document refreshing so restart it. */
423 start_document_refreshes(ses
);
426 if (ses
->tab
!= get_current_tab(ses
->tab
->term
))
429 if (!ses
->doc_view
|| !ses
->doc_view
->document
) {
430 /*INTERNAL("document not formatted");*/
434 ses
->tab
->term
->width
,
435 ses
->tab
->term
->height
- 2);
436 draw_box(ses
->tab
->term
, &box
, ' ', 0, NULL
);
440 if (!ses
->doc_view
->vs
&& have_location(ses
))
441 ses
->doc_view
->vs
= &cur_loc(ses
)->vs
;
442 ses
->doc_view
->last_x
= ses
->doc_view
->last_y
= -1;
444 refresh_view(ses
, ses
->doc_view
, 1);
448 refresh_view(struct session
*ses
, struct document_view
*doc_view
, int frames
)
450 /* If refresh_view() is being called because the value of a
451 * form field has changed, @ses might not be in the current
452 * tab: consider SELECT pop-ups behind which -remote loads
453 * another tab, or setTimeout in ECMAScript. */
454 if (ses
->tab
== get_current_tab(ses
->tab
->term
)) {
455 draw_doc(ses
, doc_view
, 1);
456 if (frames
) draw_frames(ses
);
458 print_screen_status(ses
);