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/tab.h"
32 #include "terminal/terminal.h"
33 #include "util/error.h"
34 #include "util/lists.h"
35 #include "util/memory.h"
36 #include "util/string.h"
37 #include "viewer/text/draw.h"
38 #include "viewer/text/form.h"
39 #include "viewer/text/link.h"
40 #include "viewer/text/search.h"
41 #include "viewer/text/view.h" /* current_frame() */
42 #include "viewer/text/vs.h"
46 check_document_fragment(struct session
*ses
, struct document_view
*doc_view
)
48 struct document
*document
= doc_view
->document
;
49 struct uri
*uri
= doc_view
->vs
->uri
;
51 struct string fragment
;
53 assert(uri
->fragmentlen
);
55 if (!init_string(&fragment
)) return -2;
56 if (!add_uri_to_string(&fragment
, uri
, URI_FRAGMENT
)) {
57 done_string(&fragment
);
60 decode_uri_string(&fragment
);
61 assert(fragment
.length
);
62 assert(*fragment
.source
);
64 /* Omit the leading '#' when calling find_tag. */
65 vy
= find_tag(document
, fragment
.source
+ 1, fragment
.length
- 1);
67 struct cache_entry
*cached
= document
->cached
;
70 if (cached
->incomplete
|| cached
->cache_id
!= document
->cache_id
) {
71 done_string(&fragment
);
75 if (get_opt_bool("document.browse.links.missing_fragment",
77 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
78 N_("Missing fragment"), ALIGN_CENTER
,
79 msg_text(ses
->tab
->term
, N_("The requested fragment "
80 "\"%s\" doesn't exist."),
84 int_bounds(&vy
, 0, document
->height
- 1);
87 done_string(&fragment
);
93 draw_frame_lines(struct terminal
*term
, struct frameset_desc
*frameset_desc
,
94 int xp
, int yp
, struct color_pair
*colors
)
98 assert(term
&& frameset_desc
&& frameset_desc
->frame_desc
);
99 if_assert_failed
return;
102 for (j
= 0; j
< frameset_desc
->box
.height
; j
++) {
104 int height
= frameset_desc
->frame_desc
[j
* frameset_desc
->box
.width
].height
;
107 for (i
= 0; i
< frameset_desc
->box
.width
; i
++) {
108 int width
= frameset_desc
->frame_desc
[i
].width
;
113 set_box(&box
, x
, y
+ 1, 1, height
);
114 draw_box(term
, &box
, BORDER_SVLINE
, SCREEN_ATTR_FRAME
, colors
);
116 if (j
== frameset_desc
->box
.height
- 1)
117 draw_border_cross(term
, x
, y
+ height
+ 1,
118 BORDER_X_UP
, colors
);
121 draw_border_cross(term
, x
, y
,
122 BORDER_X_RIGHT
, colors
);
128 set_box(&box
, x
+ 1, y
, width
, 1);
129 draw_box(term
, &box
, BORDER_SHLINE
, SCREEN_ATTR_FRAME
, colors
);
131 if (i
== frameset_desc
->box
.width
- 1
132 && x
+ width
+ 1 < term
->width
)
133 draw_border_cross(term
, x
+ width
+ 1, y
,
134 BORDER_X_LEFT
, colors
);
136 draw_border_cross(term
, x
, y
, BORDER_X_DOWN
, colors
);
140 draw_border_char(term
, x
, y
, BORDER_SCROSS
, colors
);
148 for (j
= 0; j
< frameset_desc
->box
.height
; j
++) {
150 int pj
= j
* frameset_desc
->box
.width
;
151 int height
= frameset_desc
->frame_desc
[pj
].height
;
154 for (i
= 0; i
< frameset_desc
->box
.width
; i
++) {
155 int width
= frameset_desc
->frame_desc
[i
].width
;
158 if (frameset_desc
->frame_desc
[p
].subframe
) {
159 draw_frame_lines(term
, frameset_desc
->frame_desc
[p
].subframe
,
160 x
+ 1, y
+ 1, colors
);
169 draw_view_status(struct session
*ses
, struct document_view
*doc_view
, int active
)
171 struct terminal
*term
= ses
->tab
->term
;
173 draw_forms(term
, doc_view
);
175 draw_searched(term
, doc_view
);
176 draw_current_link(ses
, doc_view
);
180 /** Checks if there is a link under the cursor so it can become the current
181 * highlighted link. */
183 check_link_under_cursor(struct session
*ses
, struct document_view
*doc_view
)
187 struct box
*box
= &doc_view
->box
;
190 link
= get_link_at_coordinates(doc_view
, x
- box
->x
, y
- box
->y
);
191 if (link
&& link
!= get_current_link(doc_view
)) {
192 doc_view
->vs
->current_link
= link
- doc_view
->document
->links
;
196 /** Puts the formatted document on the given terminal's screen.
197 * @a active indicates whether the document is focused -- i.e.,
198 * whether it is displayed in the selected frame or document. */
200 draw_doc(struct session
*ses
, struct document_view
*doc_view
, int active
)
202 struct color_pair color
;
203 struct view_state
*vs
;
204 struct terminal
*term
;
206 struct screen_char
*last
= NULL
;
211 assert(ses
&& ses
->tab
&& ses
->tab
->term
&& doc_view
);
212 if_assert_failed
return;
214 box
= &doc_view
->box
;
215 term
= ses
->tab
->term
;
217 /* The code in this function assumes that both width and height are
218 * bigger than 1 so we have to bail out here. */
219 if (box
->width
< 2 || box
->height
< 2) return;
222 /* When redrawing the document after things like link menu we
223 * have to reset the cursor routing state. */
224 if (ses
->navigate_mode
== NAVIGATE_CURSOR_ROUTING
) {
225 set_cursor(term
, ses
->tab
->x
, ses
->tab
->y
, 0);
227 set_cursor(term
, box
->x
+ box
->width
- 1, box
->y
+ box
->height
- 1, 1);
228 set_window_ptr(ses
->tab
, box
->x
, box
->y
);
232 color
.foreground
= get_opt_color("document.colors.text", ses
);
233 color
.background
= doc_view
->document
->height
234 ? doc_view
->document
->color
.background
235 : get_opt_color("document.colors.background", ses
);
239 draw_box(term
, box
, ' ', 0, &color
);
243 if (document_has_frames(doc_view
->document
)) {
244 draw_box(term
, box
, ' ', 0, &color
);
245 draw_frame_lines(term
, doc_view
->document
->frame_desc
, box
->x
, box
->y
, &color
);
246 if (vs
->current_link
== -1)
247 vs
->current_link
= 0;
251 if (ses
->navigate_mode
== NAVIGATE_LINKWISE
) {
255 check_link_under_cursor(ses
, doc_view
);
258 if (!vs
->did_fragment
) {
259 vy
= check_document_fragment(ses
, doc_view
);
261 if (vy
!= -2) vs
->did_fragment
= 1;
263 doc_view
->vs
->y
= vy
;
269 if (doc_view
->last_x
!= -1
270 && doc_view
->last_x
== vx
271 && doc_view
->last_y
== vy
272 && !has_search_word(doc_view
)) {
273 clear_link(term
, doc_view
);
274 draw_view_status(ses
, doc_view
, active
);
277 doc_view
->last_x
= vx
;
278 doc_view
->last_y
= vy
;
279 draw_box(term
, box
, ' ', 0, &color
);
280 if (!doc_view
->document
->height
) return;
282 while (vs
->y
>= doc_view
->document
->height
) vs
->y
-= box
->height
;
283 int_lower_bound(&vs
->y
, 0);
286 if (ses
->navigate_mode
== NAVIGATE_LINKWISE
)
289 for (y
= int_max(vy
, 0);
290 y
< int_min(doc_view
->document
->height
, box
->height
+ vy
);
292 struct screen_char
*first
= NULL
;
295 int st
= int_max(vx
, 0);
296 int en
= int_min(doc_view
->document
->data
[y
].length
,
298 int max
= int_min(en
, st
+ 200);
301 draw_line(term
, box
->x
+ st
- vx
, box
->y
+ y
- vy
,
303 &doc_view
->document
->data
[y
].chars
[st
]);
305 for (i
= en
- 1; i
> 0; --i
) {
306 if (doc_view
->document
->data
[y
].chars
[i
].data
!= ' ') {
307 last
= &doc_view
->document
->data
[y
].chars
[i
];
313 for (i
= st
; i
< max
; i
++) {
314 if (doc_view
->document
->data
[y
].chars
[i
].data
!= ' ') {
315 first
= &doc_view
->document
->data
[y
].chars
[i
];
320 for (j
= st
; j
< i
; j
++) {
321 draw_space(term
, box
->x
+ j
- vx
, box
->y
+ y
- vy
,
325 for (i
= last_index
; i
< box
->width
+ vx
; i
++) {
326 draw_space(term
, box
->x
+ i
- vx
, box
->y
+ y
- vy
,
330 draw_view_status(ses
, doc_view
, active
);
331 if (has_search_word(doc_view
))
332 doc_view
->last_x
= doc_view
->last_y
= -1;
336 draw_frames(struct session
*ses
)
338 struct document_view
*doc_view
, *current_doc_view
;
342 assert(ses
&& ses
->doc_view
&& ses
->doc_view
->document
);
343 if_assert_failed
return;
345 if (!document_has_frames(ses
->doc_view
->document
)) return;
348 foreach (doc_view
, ses
->scrn_frames
) {
349 doc_view
->last_x
= doc_view
->last_y
= -1;
352 l
= &cur_loc(ses
)->vs
.current_link
;
353 *l
= int_max(*l
, 0) % int_max(n
, 1);
355 current_doc_view
= current_frame(ses
);
360 foreach (doc_view
, ses
->scrn_frames
) {
361 if (doc_view
->depth
== d
)
362 draw_doc(ses
, doc_view
, doc_view
== current_doc_view
);
363 else if (doc_view
->depth
> d
)
372 /** @todo @a rerender is ridiciously wound-up. */
374 draw_formatted(struct session
*ses
, int rerender
)
376 assert(ses
&& ses
->tab
);
377 if_assert_failed
return;
380 rerender
--; /* Mind this when analyzing @rerender. */
381 if (!(rerender
& 2) && session_is_loading(ses
))
383 render_document_frames(ses
, rerender
);
385 /* Rerendering kills the document refreshing so restart it. */
386 start_document_refreshes(ses
);
389 if (ses
->tab
!= get_current_tab(ses
->tab
->term
))
392 if (!ses
->doc_view
|| !ses
->doc_view
->document
) {
393 /*INTERNAL("document not formatted");*/
397 ses
->tab
->term
->width
,
398 ses
->tab
->term
->height
- 2);
399 draw_box(ses
->tab
->term
, &box
, ' ', 0, NULL
);
403 if (!ses
->doc_view
->vs
&& have_location(ses
))
404 ses
->doc_view
->vs
= &cur_loc(ses
)->vs
;
405 ses
->doc_view
->last_x
= ses
->doc_view
->last_y
= -1;
407 refresh_view(ses
, ses
->doc_view
, 1);
411 refresh_view(struct session
*ses
, struct document_view
*doc_view
, int frames
)
413 /* If refresh_view() is being called because the value of a
414 * form field has changed, @ses might not be in the current
415 * tab: consider SELECT pop-ups behind which -remote loads
416 * another tab, or setTimeout in ECMAScript. */
417 if (ses
->tab
== get_current_tab(ses
->tab
->term
)) {
418 draw_doc(ses
, doc_view
, 1);
419 if (frames
) draw_frames(ses
);
421 print_screen_status(ses
);