[wip] draw_images(): w, h, spos -> box screen_img; img_box -> pixel_box
[elinks/images.git] / src / viewer / text / draw.c
blobe6b2b2821c41bb30cdcfc636aa626884fae66ca2
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/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"
46 static inline int
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;
51 int vy;
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);
59 return -2;
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);
67 if (vy == -1) {
68 struct cache_entry *cached = document->cached;
70 assert(cached);
71 if (cached->incomplete || cached->cache_id != document->cache_id) {
72 done_string(&fragment);
73 return -2;
76 if (get_opt_bool("document.browse.links.missing_fragment",
77 ses)) {
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."),
82 fragment.source));
84 } else {
85 int_bounds(&vy, 0, document->height - 1);
88 done_string(&fragment);
90 return vy;
93 static void
94 draw_frame_lines(struct terminal *term, struct frameset_desc *frameset_desc,
95 int xp, int yp, struct color_pair *colors)
97 int y, j;
99 assert(term && frameset_desc && frameset_desc->frame_desc);
100 if_assert_failed return;
102 y = yp - 1;
103 for (j = 0; j < frameset_desc->box.height; j++) {
104 int x, i;
105 int height = frameset_desc->frame_desc[j * frameset_desc->box.width].height;
107 x = xp - 1;
108 for (i = 0; i < frameset_desc->box.width; i++) {
109 int width = frameset_desc->frame_desc[i].width;
111 if (i) {
112 struct box box;
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);
120 } else if (j) {
121 if (x >= 0)
122 draw_border_cross(term, x, y,
123 BORDER_X_RIGHT, colors);
126 if (j) {
127 struct box box;
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);
136 } else if (i) {
137 draw_border_cross(term, x, y, BORDER_X_DOWN, colors);
140 if (i && j)
141 draw_border_char(term, x, y, BORDER_SCROSS, colors);
143 x += width + 1;
145 y += height + 1;
148 y = yp - 1;
149 for (j = 0; j < frameset_desc->box.height; j++) {
150 int x, i;
151 int pj = j * frameset_desc->box.width;
152 int height = frameset_desc->frame_desc[pj].height;
154 x = xp - 1;
155 for (i = 0; i < frameset_desc->box.width; i++) {
156 int width = frameset_desc->frame_desc[i].width;
157 int p = pj + i;
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);
163 x += width + 1;
165 y += height + 1;
169 static void
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;
174 int chw, chh;
175 int i, cnt = 1;
176 struct box vs_box;
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);
190 vs_box.x = vs->x;
191 vs_box.y = vs->y;
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,
201 .width = img->w,
202 .height = img->h
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)
215 continue;
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);
226 /* Create box. */
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);
233 sync_images(term);
236 static void
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);
242 if (active) {
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. */
250 static void
251 check_link_under_cursor(struct session *ses, struct document_view *doc_view)
253 int x = ses->tab->x;
254 int y = ses->tab->y;
255 struct box *box = &doc_view->box;
256 struct link *link;
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. */
267 static void
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;
273 struct box *box;
274 int vx, vy;
275 int y;
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;
287 if (active) {
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);
292 } else {
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);
303 vs = doc_view->vs;
304 if (!vs) {
305 draw_box(term, box, ' ', 0, &color);
306 return;
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;
314 return;
317 if (ses->navigate_mode == NAVIGATE_LINKWISE) {
318 check_vs(doc_view);
320 } else {
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;
328 if (vy >= 0) {
329 doc_view->vs->y = vy;
330 set_link(doc_view);
333 vx = vs->x;
334 vy = vs->y;
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);
341 return;
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);
350 if (vy != vs->y) {
351 vy = vs->y;
352 if (ses->navigate_mode == NAVIGATE_LINKWISE)
353 check_vs(doc_view);
355 for (y = int_max(vy, 0);
356 y < int_min(doc_view->document->height, box->height + vy);
357 y++) {
358 int st = int_max(vx, 0);
359 int en = int_min(doc_view->document->data[y].length,
360 box->width + vx);
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;
372 static void
373 draw_frames(struct session *ses)
375 struct document_view *doc_view, *current_doc_view;
376 int *l;
377 int n, d;
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;
384 n = 0;
385 foreach (doc_view, ses->scrn_frames) {
386 doc_view->last_x = doc_view->last_y = -1;
387 n++;
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);
393 d = 0;
394 while (1) {
395 int more = 0;
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)
401 more = 1;
404 if (!more) break;
405 d++;
409 /** @todo @a rerender is ridiciously wound-up. */
410 void
411 draw_formatted(struct session *ses, int rerender)
413 assert(ses && ses->tab);
414 if_assert_failed return;
416 if (rerender) {
417 rerender--; /* Mind this when analyzing @rerender. */
418 if (!(rerender & 2) && session_is_loading(ses))
419 rerender |= 2;
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))
427 return;
429 if (!ses->doc_view || !ses->doc_view->document) {
430 /*INTERNAL("document not formatted");*/
431 struct box box;
433 set_box(&box, 0, 1,
434 ses->tab->term->width,
435 ses->tab->term->height - 2);
436 draw_box(ses->tab->term, &box, ' ', 0, NULL);
437 return;
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);
447 void
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);