Merge with git+ssh://pasky/srv/git/elinks.git
[elinks.git] / src / dialogs / status.c
blob0eaa948a9c70ad1ee5072222babc8bfbeee15066
1 /* Sessions status management */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <string.h>
9 #include "elinks.h"
11 #include "bfu/dialog.h"
12 #include "cache/cache.h"
13 #include "config/options.h"
14 #include "dialogs/progress.h"
15 #include "dialogs/status.h"
16 #include "document/document.h"
17 #include "document/renderer.h"
18 #include "document/view.h"
19 #include "intl/gettext/libintl.h"
20 #include "network/connection.h"
21 #include "network/progress.h"
22 #include "network/state.h"
23 #include "protocol/bittorrent/dialogs.h"
24 #include "protocol/protocol.h"
25 #include "protocol/uri.h"
26 #include "session/download.h"
27 #include "session/session.h"
28 #include "terminal/draw.h"
29 #include "terminal/screen.h"
30 #include "terminal/tab.h"
31 #include "terminal/terminal.h"
32 #include "terminal/window.h"
33 #include "util/color.h"
34 #include "util/conv.h"
35 #include "util/error.h"
36 #include "util/memory.h"
37 #include "util/snprintf.h"
38 #include "util/string.h"
39 #include "viewer/text/form.h"
40 #include "viewer/text/link.h"
41 #include "viewer/text/view.h"
44 unsigned char *
45 get_download_msg(struct download *download, struct terminal *term,
46 int wide, int full, unsigned char *separator)
48 if (!download_is_progressing(download)) {
49 /* DBG("%d -> %s", download->state, _(get_err_msg(download->state), term)); */
50 return stracpy(get_state_message(download->state, term));
53 #ifdef CONFIG_BITTORRENT
54 if (download->conn
55 && download->conn->uri->protocol == PROTOCOL_BITTORRENT)
56 return get_bittorrent_message(download, term, wide, full, separator);
57 #endif
59 return get_progress_msg(download->progress, term, wide, full, separator);
62 #define show_tabs(option, tabs) (((option) > 0) && !((option) == 1 && (tabs) < 2))
64 void
65 update_status(void)
67 int show_title_bar = get_opt_bool("ui.show_title_bar");
68 int show_status_bar = get_opt_bool("ui.show_status_bar");
69 int show_tabs_bar = get_opt_int("ui.tabs.show_bar");
70 #ifdef CONFIG_LEDS
71 int show_leds = get_opt_bool("ui.leds.enable");
72 #endif
73 int set_window_title = get_opt_bool("ui.window_title");
74 int insert_mode = get_opt_bool("document.browse.forms.insert_mode");
75 struct session *ses;
76 int tabs = 1;
77 struct terminal *term = NULL;
79 foreach (ses, sessions) {
80 struct session_status *status = &ses->status;
81 int dirty = 0;
83 /* Try to descrease the number of tab calculation using that
84 * tab sessions share the same term. */
85 if (ses->tab->term != term) {
86 term = ses->tab->term;
87 tabs = number_of_tabs(term);
90 if (status->force_show_title_bar >= 0)
91 show_title_bar = status->force_show_title_bar;
92 if (status->show_title_bar != show_title_bar) {
93 status->show_title_bar = show_title_bar;
94 dirty = 1;
97 if (status->force_show_status_bar >= 0)
98 show_status_bar = status->force_show_status_bar;
99 if (status->show_status_bar != show_status_bar) {
100 status->show_status_bar = show_status_bar;
101 dirty = 1;
104 if (show_tabs(show_tabs_bar, tabs) != status->show_tabs_bar) {
105 status->show_tabs_bar = show_tabs(show_tabs_bar, tabs);
106 dirty = 1;
108 #ifdef CONFIG_LEDS
109 if (status->show_leds != show_leds) {
110 status->show_leds = show_leds;
111 dirty = 1;
113 #endif
115 status->set_window_title = set_window_title;
117 /* This more belongs to the current browsing state but ... */
118 if (!insert_mode)
119 ses->insert_mode = INSERT_MODE_LESS;
120 else if (ses->insert_mode == INSERT_MODE_LESS)
121 ses->insert_mode = INSERT_MODE_OFF;
123 if (!dirty) continue;
125 /* Force the current document to be rerendered so the
126 * document view and document height is updated to fit
127 * into the new dimensions. Related to bug 87. */
128 render_document_frames(ses, 1);
130 set_screen_dirty(term->screen, 0, term->height);
134 static unsigned char *
135 get_current_link_info_and_title(struct session *ses,
136 struct document_view *doc_view)
138 unsigned char *link_info, *link_title, *ret = NULL;
140 link_info = get_current_link_info(ses, doc_view);
141 if (!link_info) return NULL;
143 link_title = get_current_link_title(doc_view);
144 if (link_title) {
145 unsigned char *src;
147 assert(*link_title);
149 /* Remove illicit chars. */
150 for (src = link_title; *src; src++)
151 if (!isprint(*src) || iscntrl(*src))
152 *src = '*';
154 ret = straconcat(link_info, " - ", link_title, NULL);
155 mem_free(link_info);
156 mem_free(link_title);
159 if (!ret) ret = link_info;
161 return ret;
164 static inline void
165 display_status_bar(struct session *ses, struct terminal *term, int tabs_count)
167 unsigned char *msg = NULL;
168 unsigned int tab_info_len = 0;
169 struct download *download = get_current_download(ses);
170 struct session_status *status = &ses->status;
171 struct color_pair *text_color = NULL;
172 int msglen;
173 struct box box;
175 #ifdef CONFIG_MARKS
176 if (ses->kbdprefix.mark != KP_MARK_NOTHING) {
177 switch (ses->kbdprefix.mark) {
178 case KP_MARK_NOTHING:
179 assert(0);
180 break;
182 case KP_MARK_SET:
183 msg = msg_text(term, N_("Enter a mark to set"));
184 break;
186 case KP_MARK_GOTO:
187 msg = msg_text(term, N_("Enter a mark"
188 " to which to jump"));
189 break;
191 } else
192 #endif
193 if (ses->kbdprefix.repeat_count) {
194 msg = msg_text(term, N_("Keyboard prefix: %d"),
195 ses->kbdprefix.repeat_count);
196 } else if (download) {
197 struct document_view *doc_view = current_frame(ses);
199 /* Show S_INTERRUPTED message *once* but then show links
200 * again as usual. */
201 /* doc_view->vs may be NULL here in the short interval between
202 * ses_forward() with @loading_in_frame set, disconnecting the
203 * doc_view from vs, and render_document_frames(), detaching
204 * the doc_view. */
205 if (doc_view && doc_view->vs) {
206 static int last_current_link;
207 int ncl = doc_view->vs->current_link;
209 if (download->state == S_INTERRUPTED
210 && ncl != last_current_link)
211 download->state = S_OK;
212 last_current_link = ncl;
214 if (download->state == S_OK) {
215 if (get_current_link(doc_view)) {
216 msg = get_current_link_info_and_title(ses, doc_view);
217 } else if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
218 msg = msg_text(term, N_("Cursor position: %dx%d"),
219 ses->tab->x + 1, ses->tab->y + 1);
224 if (!msg) {
225 int full = term->width > 130;
226 int wide = term->width > 80;
228 msg = get_download_msg(download, term, wide, full, ", ");
232 set_box(&box, 0, term->height - 1, term->width, 1);
233 draw_box(term, &box, ' ', 0, get_bfu_color(term, "status.status-bar"));
235 if (!status->show_tabs_bar && tabs_count > 1) {
236 unsigned char tab_info[8];
238 tab_info[tab_info_len++] = '[';
239 ulongcat(tab_info, &tab_info_len, term->current_tab + 1, 4, 0);
240 tab_info[tab_info_len++] = ']';
241 tab_info[tab_info_len++] = ' ';
242 tab_info[tab_info_len] = '\0';
244 text_color = get_bfu_color(term, "status.status-text");
245 draw_text(term, 0, term->height - 1, tab_info, tab_info_len,
246 0, text_color);
249 if (!msg) return;
251 if (!text_color)
252 text_color = get_bfu_color(term, "status.status-text");
254 msglen = strlen(msg);
255 draw_text(term, 0 + tab_info_len, term->height - 1,
256 msg, msglen, 0, text_color);
257 mem_free(msg);
259 if (download_is_progressing(download) && download->progress->size > 0) {
260 int xend = term->width - 1;
261 int width;
263 #ifdef CONFIG_LEDS
264 if (ses->status.show_leds)
265 xend -= term->leds_length;
266 #endif
268 width = int_max(0, xend - msglen - tab_info_len - 1);
269 if (width < 6) return;
270 int_upper_bound(&width, 20);
271 draw_progress_bar(download->progress, term,
272 xend - width, term->height - 1, width,
273 NULL, NULL);
277 static inline void
278 display_tab_bar(struct session *ses, struct terminal *term, int tabs_count)
280 struct color_pair *normal_color = get_bfu_color(term, "tabs.normal");
281 struct color_pair *selected_color = get_bfu_color(term, "tabs.selected");
282 struct color_pair *loading_color = get_bfu_color(term, "tabs.loading");
283 struct color_pair *fresh_color = get_bfu_color(term, "tabs.unvisited");
284 struct color_pair *tabsep_color = get_bfu_color(term, "tabs.separator");
285 struct session_status *status = &ses->status;
286 int tab_width = int_max(1, term->width / tabs_count);
287 int tab_total_width = tab_width * tabs_count;
288 int tab_remain_width = int_max(0, term->width - tab_total_width);
289 int tab_add = int_max(1, (tab_remain_width / tabs_count));
290 int tab_num;
291 struct box box;
293 set_box(&box, 0, term->height - (status->show_status_bar ? 2 : 1), 0, 1);
295 for (tab_num = 0; tab_num < tabs_count; tab_num++) {
296 struct download *download = NULL;
297 struct color_pair *color = normal_color;
298 struct window *tab = get_tab_by_number(term, tab_num);
299 struct document_view *doc_view;
300 struct session *tab_ses = tab->data;
301 int actual_tab_width = tab_width - 1;
302 unsigned char *msg;
304 /* Adjust tab size to use full term width. */
305 if (tab_remain_width) {
306 actual_tab_width += tab_add;
307 tab_remain_width -= tab_add;
310 doc_view = tab_ses ? current_frame(tab_ses) : NULL;
312 if (doc_view) {
313 if (doc_view->document->title
314 && *(doc_view->document->title))
315 msg = doc_view->document->title;
316 else
317 msg = _("Untitled", term);
318 } else {
319 msg = _("No document", term);
322 if (tab_num) {
323 draw_char(term, box.x, box.y, BORDER_SVLINE,
324 SCREEN_ATTR_FRAME, tabsep_color);
325 box.x++;
328 if (tab_num == term->current_tab) {
329 color = selected_color;
331 } else {
332 download = get_current_download(tab_ses);
334 if (download && download->state != S_OK) {
335 color = loading_color;
336 } else if (!tab_ses || !tab_ses->status.visited) {
337 color = fresh_color;
340 if (!download_is_progressing(download)
341 || download->progress->size <= 0)
342 download = NULL;
345 box.width = actual_tab_width + 1;
346 draw_box(term, &box, ' ', 0, color);
348 if (download) {
349 draw_progress_bar(download->progress, term,
350 box.x, box.y, actual_tab_width,
351 msg, NULL);
352 } else {
353 int msglen = int_min(strlen(msg), actual_tab_width);
355 draw_text(term, box.x, box.y, msg, msglen, 0, color);
358 tab->xpos = box.x;
359 tab->width = actual_tab_width;
360 if (tab_num == tabs_count - 1) {
361 /* This is the last tab, and is therefore followed
362 * by a space, not a separator; increment tab->width
363 * to count that space as part of the tab.
364 * -- Miciah */
365 tab->width++;
368 box.x += actual_tab_width;
372 /* Print page's title and numbering at window top. */
373 static inline void
374 display_title_bar(struct session *ses, struct terminal *term)
376 struct document_view *doc_view;
377 struct document *document;
378 struct string title;
379 unsigned char buf[40];
380 int buflen = 0;
381 int height;
382 struct box box;
384 /* Clear the old title */
385 set_box(&box, 0, 0, term->width, 1);
386 draw_box(term, &box, ' ', 0, get_bfu_color(term, "title.title-bar"));
388 doc_view = current_frame(ses);
389 if (!doc_view || !doc_view->document) return;
391 if (!init_string(&title)) return;
393 document = doc_view->document;
395 /* Set up the document page info string: '(' %page '/' %pages ')' */
396 height = doc_view->box.height;
397 if (height < document->height && doc_view->vs) {
398 int pos = doc_view->vs->y + height;
399 int page = 1;
400 int pages = height ? (document->height + height - 1) / height : 1;
402 /* Check if at the end else calculate the page. */
403 if (pos >= document->height) {
404 page = pages;
405 } else if (height) {
406 page = int_min((pos - height / 2) / height + 1, pages);
409 buflen = snprintf(buf, sizeof(buf), " (%d/%d)", page, pages);
410 if (buflen < 0) buflen = 0;
413 if (document->title) {
414 int maxlen = int_max(term->width - 4 - buflen, 0);
415 int titlelen = int_min(strlen(document->title), maxlen);
417 add_bytes_to_string(&title, document->title, titlelen);
419 if (titlelen == maxlen)
420 add_bytes_to_string(&title, "...", 3);
423 if (buflen > 0)
424 add_bytes_to_string(&title, buf, buflen);
426 if (title.length) {
427 int x = int_max(term->width - 1 - title.length, 0);
429 draw_text(term, x, 0, title.source, title.length, 0,
430 get_bfu_color(term, "title.title-text"));
433 done_string(&title);
436 static inline void
437 display_window_title(struct session *ses, struct terminal *term)
439 static struct session *last_ses;
440 struct session_status *status = &ses->status;
441 unsigned char *doc_title = NULL;
442 unsigned char *title;
443 int titlelen;
445 if (ses->doc_view
446 && ses->doc_view->document
447 && ses->doc_view->document->title
448 && ses->doc_view->document->title[0])
449 doc_title = ses->doc_view->document->title;
451 title = doc_title ? straconcat(doc_title, " - ELinks", NULL)
452 : stracpy("ELinks");
453 if (!title) return;
455 titlelen = strlen(title);
456 if (last_ses != ses
457 || !status->last_title
458 || strlen(status->last_title) != titlelen
459 || memcmp(status->last_title, title, titlelen)) {
460 mem_free_set(&status->last_title, title);
461 set_terminal_title(term, title);
462 last_ses = ses;
463 } else {
464 mem_free(title);
468 #ifdef CONFIG_LEDS
469 static inline void
470 display_leds(struct session *ses, struct session_status *status)
472 if (ses->doc_view && ses->doc_view->document
473 && ses->doc_view->document->uri) {
474 struct cache_entry *cached =
475 find_in_cache(ses->doc_view->document->uri);
477 if (cached) {
478 if (cached->ssl_info)
479 set_led_value(status->ssl_led, 'S');
480 else
481 unset_led_value(status->ssl_led);
482 } else {
483 /* FIXME: We should do this thing better. */
484 set_led_value(status->ssl_led, '?');
488 if (ses->insert_mode == INSERT_MODE_LESS) {
489 set_led_value(status->insert_mode_led, 'i');
490 } else {
491 if (ses->insert_mode == INSERT_MODE_ON)
492 set_led_value(status->insert_mode_led, 'I');
493 else
494 unset_led_value(status->insert_mode_led);
497 draw_leds(ses);
499 #endif /* CONFIG_LEDS */
501 /* Print statusbar and titlebar, set terminal title. */
502 void
503 print_screen_status(struct session *ses)
505 struct terminal *term = ses->tab->term;
506 struct session_status *status = &ses->status;
507 int tabs_count = number_of_tabs(term);
508 int ses_tab_is_current = (ses->tab == get_current_tab(term));
510 if (ses_tab_is_current) {
511 if (status->set_window_title)
512 display_window_title(ses, term);
514 if (status->show_title_bar)
515 display_title_bar(ses, term);
517 if (status->show_status_bar)
518 display_status_bar(ses, term, tabs_count);
519 #ifdef CONFIG_LEDS
520 if (status->show_leds)
521 display_leds(ses, status);
522 #endif
524 if (!ses->status.visited)
525 ses->status.visited = 1;
528 if (status->show_tabs_bar) {
529 display_tab_bar(ses, term, tabs_count);
532 redraw_from_window(ses->tab);