Off by one, also 200 instead of 30
[elinks.git] / src / viewer / text / draw.c
blobe633c24b3fe1c026f70e038fbd6500b69eb26411
1 /** Text mode drawing functions
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <stdlib.h>
9 #include <string.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
14 #include "elinks.h"
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"
45 static inline int
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;
50 int vy;
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);
58 return -2;
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);
66 if (vy == -1) {
67 struct cache_entry *cached = document->cached;
69 assert(cached);
70 if (cached->incomplete || cached->cache_id != document->cache_id) {
71 done_string(&fragment);
72 return -2;
75 if (get_opt_bool("document.browse.links.missing_fragment",
76 ses)) {
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."),
81 fragment.source));
83 } else {
84 int_bounds(&vy, 0, document->height - 1);
87 done_string(&fragment);
89 return vy;
92 static void
93 draw_frame_lines(struct terminal *term, struct frameset_desc *frameset_desc,
94 int xp, int yp, struct color_pair *colors)
96 int y, j;
98 assert(term && frameset_desc && frameset_desc->frame_desc);
99 if_assert_failed return;
101 y = yp - 1;
102 for (j = 0; j < frameset_desc->box.height; j++) {
103 int x, i;
104 int height = frameset_desc->frame_desc[j * frameset_desc->box.width].height;
106 x = xp - 1;
107 for (i = 0; i < frameset_desc->box.width; i++) {
108 int width = frameset_desc->frame_desc[i].width;
110 if (i) {
111 struct box box;
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);
119 } else if (j) {
120 if (x >= 0)
121 draw_border_cross(term, x, y,
122 BORDER_X_RIGHT, colors);
125 if (j) {
126 struct box box;
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);
135 } else if (i) {
136 draw_border_cross(term, x, y, BORDER_X_DOWN, colors);
139 if (i && j)
140 draw_border_char(term, x, y, BORDER_SCROSS, colors);
142 x += width + 1;
144 y += height + 1;
147 y = yp - 1;
148 for (j = 0; j < frameset_desc->box.height; j++) {
149 int x, i;
150 int pj = j * frameset_desc->box.width;
151 int height = frameset_desc->frame_desc[pj].height;
153 x = xp - 1;
154 for (i = 0; i < frameset_desc->box.width; i++) {
155 int width = frameset_desc->frame_desc[i].width;
156 int p = pj + i;
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);
162 x += width + 1;
164 y += height + 1;
168 static void
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);
174 if (active) {
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. */
182 static void
183 check_link_under_cursor(struct session *ses, struct document_view *doc_view)
185 int x = ses->tab->x;
186 int y = ses->tab->y;
187 struct box *box = &doc_view->box;
188 struct link *link;
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. */
199 static void
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;
205 struct box *box;
206 struct screen_char *last = NULL;
208 int vx, vy;
209 int y;
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;
221 if (active) {
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);
226 } else {
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);
237 vs = doc_view->vs;
238 if (!vs) {
239 draw_box(term, box, ' ', 0, &color);
240 return;
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;
248 return;
251 if (ses->navigate_mode == NAVIGATE_LINKWISE) {
252 check_vs(doc_view);
254 } else {
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;
262 if (vy >= 0) {
263 doc_view->vs->y = vy;
264 set_link(doc_view);
267 vx = vs->x;
268 vy = vs->y;
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);
275 return;
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);
284 if (vy != vs->y) {
285 vy = vs->y;
286 if (ses->navigate_mode == NAVIGATE_LINKWISE)
287 check_vs(doc_view);
289 for (y = int_max(vy, 0);
290 y < int_min(doc_view->document->height, box->height + vy);
291 y++) {
292 struct screen_char *first = NULL;
293 int i, j;
294 int st = int_max(vx, 0);
295 int en = int_min(doc_view->document->data[y].length,
296 box->width + vx);
297 int max = int_min(en, st + 200);
299 if (en - st > 0) {
300 draw_line(term, box->x + st - vx, box->y + y - vy,
301 en - st,
302 &doc_view->document->data[y].chars[st]);
303 last = &doc_view->document->data[y].chars[en - 1];
305 for (i = st; i < max; i++) {
306 if (doc_view->document->data[y].chars[i].data != ' ') {
307 first = &doc_view->document->data[y].chars[i];
308 break;
312 for (j = st; j < i; j++) {
313 draw_space(term, box->x + j - vx, box->y + y - vy,
314 first);
316 for (i = en ? en : 0; i < box->width + vx; i++) {
317 draw_space(term, box->x + i - vx, box->y + y - vy,
318 last);
321 draw_view_status(ses, doc_view, active);
322 if (has_search_word(doc_view))
323 doc_view->last_x = doc_view->last_y = -1;
326 static void
327 draw_frames(struct session *ses)
329 struct document_view *doc_view, *current_doc_view;
330 int *l;
331 int n, d;
333 assert(ses && ses->doc_view && ses->doc_view->document);
334 if_assert_failed return;
336 if (!document_has_frames(ses->doc_view->document)) return;
338 n = 0;
339 foreach (doc_view, ses->scrn_frames) {
340 doc_view->last_x = doc_view->last_y = -1;
341 n++;
343 l = &cur_loc(ses)->vs.current_link;
344 *l = int_max(*l, 0) % int_max(n, 1);
346 current_doc_view = current_frame(ses);
347 d = 0;
348 while (1) {
349 int more = 0;
351 foreach (doc_view, ses->scrn_frames) {
352 if (doc_view->depth == d)
353 draw_doc(ses, doc_view, doc_view == current_doc_view);
354 else if (doc_view->depth > d)
355 more = 1;
358 if (!more) break;
359 d++;
363 /** @todo @a rerender is ridiciously wound-up. */
364 void
365 draw_formatted(struct session *ses, int rerender)
367 assert(ses && ses->tab);
368 if_assert_failed return;
370 if (rerender) {
371 rerender--; /* Mind this when analyzing @rerender. */
372 if (!(rerender & 2) && session_is_loading(ses))
373 rerender |= 2;
374 render_document_frames(ses, rerender);
376 /* Rerendering kills the document refreshing so restart it. */
377 start_document_refreshes(ses);
380 if (ses->tab != get_current_tab(ses->tab->term))
381 return;
383 if (!ses->doc_view || !ses->doc_view->document) {
384 /*INTERNAL("document not formatted");*/
385 struct box box;
387 set_box(&box, 0, 1,
388 ses->tab->term->width,
389 ses->tab->term->height - 2);
390 draw_box(ses->tab->term, &box, ' ', 0, NULL);
391 return;
394 if (!ses->doc_view->vs && have_location(ses))
395 ses->doc_view->vs = &cur_loc(ses)->vs;
396 ses->doc_view->last_x = ses->doc_view->last_y = -1;
398 refresh_view(ses, ses->doc_view, 1);
401 void
402 refresh_view(struct session *ses, struct document_view *doc_view, int frames)
404 /* If refresh_view() is being called because the value of a
405 * form field has changed, @ses might not be in the current
406 * tab: consider SELECT pop-ups behind which -remote loads
407 * another tab, or setTimeout in ECMAScript. */
408 if (ses->tab == get_current_tab(ses->tab->term)) {
409 draw_doc(ses, doc_view, 1);
410 if (frames) draw_frames(ses);
412 print_screen_status(ses);