include libgen.h for basename
[elinks.git] / src / dialogs / status.c
bloba9f6aec46fbcf8b0a194165e21a100559c0b639c
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
58 if (download->conn && download->conn->http_upload_progress)
59 return get_upload_progress_msg(download->conn->http_upload_progress,
60 term, wide, full, separator);
62 return get_progress_msg(download->progress, term, wide, full, separator);
65 #define show_tabs(option, tabs) (((option) > 0) && !((option) == 1 && (tabs) < 2))
67 void
68 update_status(void)
70 int show_title_bar = get_opt_bool("ui.show_title_bar", NULL);
71 int show_status_bar = get_opt_bool("ui.show_status_bar", NULL);
72 int show_tabs_bar = get_opt_int("ui.tabs.show_bar", NULL);
73 int show_tabs_bar_at_top = get_opt_bool("ui.tabs.top", NULL);
74 #ifdef CONFIG_LEDS
75 int show_leds = get_opt_bool("ui.leds.enable", NULL);
76 #endif
77 int set_window_title = get_opt_bool("ui.window_title", NULL);
78 int insert_mode = get_opt_bool("document.browse.forms.insert_mode",
79 NULL);
80 struct session *ses;
81 int tabs_count = 1;
82 struct terminal *term = NULL;
84 foreach (ses, sessions) {
85 struct session_status *status = &ses->status;
86 int dirty = 0;
88 /* Try to descrease the number of tab calculation using that
89 * tab sessions share the same term. */
90 if (ses->tab->term != term) {
91 term = ses->tab->term;
92 tabs_count = number_of_tabs(term);
95 if (status->force_show_title_bar >= 0)
96 show_title_bar = status->force_show_title_bar;
97 if (status->show_title_bar != show_title_bar) {
98 status->show_title_bar = show_title_bar;
99 dirty = 1;
102 if (status->force_show_status_bar >= 0)
103 show_status_bar = status->force_show_status_bar;
104 if (status->show_status_bar != show_status_bar) {
105 status->show_status_bar = show_status_bar;
106 dirty = 1;
109 if (show_tabs(show_tabs_bar, tabs_count) != status->show_tabs_bar) {
110 status->show_tabs_bar = show_tabs(show_tabs_bar, tabs_count);
111 dirty = 1;
114 if (status->show_tabs_bar) {
115 if (status->show_tabs_bar_at_top != show_tabs_bar_at_top) {
116 status->show_tabs_bar_at_top = show_tabs_bar_at_top;
117 dirty = 1;
120 #ifdef CONFIG_LEDS
121 if (status->show_leds != show_leds) {
122 status->show_leds = show_leds;
124 #endif
126 status->set_window_title = set_window_title;
128 /* This more belongs to the current browsing state but ... */
129 if (!insert_mode)
130 ses->insert_mode = INSERT_MODE_LESS;
131 else if (ses->insert_mode == INSERT_MODE_LESS)
132 ses->insert_mode = INSERT_MODE_OFF;
134 if (!dirty) continue;
136 /* Force the current document to be rerendered so the
137 * document view and document height is updated to fit
138 * into the new dimensions. Related to bug 87. */
139 render_document_frames(ses, 1);
141 set_screen_dirty(term->screen, 0, term->height);
145 static unsigned char *
146 get_current_link_info_and_title(struct session *ses,
147 struct document_view *doc_view)
149 unsigned char *link_info, *link_title, *ret = NULL;
151 link_info = get_current_link_info(ses, doc_view);
152 if (!link_info) return NULL;
154 link_title = get_current_link_title(doc_view);
155 if (link_title) {
156 assert(*link_title);
158 ret = straconcat(link_info, " - ", link_title,
159 (unsigned char *) NULL);
160 mem_free(link_info);
161 mem_free(link_title);
164 if (!ret) ret = link_info;
166 return ret;
169 static inline void
170 display_status_bar(struct session *ses, struct terminal *term, int tabs_count)
172 unsigned char *msg = NULL;
173 unsigned int tab_info_len = 0;
174 struct download *download = get_current_download(ses);
175 struct session_status *status = &ses->status;
176 struct color_pair *text_color = NULL;
177 int msglen;
178 struct box box;
180 #ifdef CONFIG_MARKS
181 if (ses->kbdprefix.mark != KP_MARK_NOTHING) {
182 switch (ses->kbdprefix.mark) {
183 case KP_MARK_NOTHING:
184 assert(0);
185 break;
187 case KP_MARK_SET:
188 msg = msg_text(term, N_("Enter a mark to set"));
189 break;
191 case KP_MARK_GOTO:
192 msg = msg_text(term, N_("Enter a mark"
193 " to which to jump"));
194 break;
196 } else
197 #endif
198 if (ses->kbdprefix.repeat_count) {
199 msg = msg_text(term, N_("Keyboard prefix: %d"),
200 ses->kbdprefix.repeat_count);
202 #ifdef CONFIG_ECMASCRIPT
203 else if (ses->status.window_status) {
204 msg = stracpy(ses->status.window_status);
206 #endif
207 else if (download) {
208 struct document_view *doc_view = current_frame(ses);
210 /* Show S_INTERRUPTED message *once* but then show links
211 * again as usual. */
212 /* doc_view->vs may be NULL here in the short interval between
213 * ses_forward() with @loading_in_frame set, disconnecting the
214 * doc_view from vs, and render_document_frames(), detaching
215 * the doc_view. */
216 if (doc_view && doc_view->vs) {
217 static int last_current_link;
218 int ncl = doc_view->vs->current_link;
220 if (is_in_state(download->state, S_INTERRUPTED)
221 && ncl != last_current_link)
222 download->state = connection_state(S_OK);
223 last_current_link = ncl;
225 if (is_in_state(download->state, S_OK)) {
226 if (get_current_link(doc_view)) {
227 msg = get_current_link_info_and_title(ses, doc_view);
228 } else if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
229 msg = msg_text(term, N_("Cursor position: %dx%d"),
230 ses->tab->x + 1, ses->tab->y + 1);
236 if (!msg) {
237 int full = term->width > 130;
238 int wide = term->width > 80;
240 msg = get_download_msg(download, term, wide, full, ", ");
244 set_box(&box, 0, term->height - 1, term->width, 1);
245 draw_box(term, &box, ' ', 0, get_bfu_color(term, "status.status-bar"));
247 if (!status->show_tabs_bar && tabs_count > 1) {
248 unsigned char tab_info[8];
250 tab_info[tab_info_len++] = '[';
251 ulongcat(tab_info, &tab_info_len, term->current_tab + 1, 4, 0);
252 tab_info[tab_info_len++] = ']';
253 tab_info[tab_info_len++] = ' ';
254 tab_info[tab_info_len] = '\0';
256 text_color = get_bfu_color(term, "status.status-text");
257 draw_text(term, 0, term->height - 1, tab_info, tab_info_len,
258 0, text_color);
261 if (!msg) return;
263 if (!text_color)
264 text_color = get_bfu_color(term, "status.status-text");
266 msglen = strlen(msg);
267 draw_text(term, 0 + tab_info_len, term->height - 1,
268 msg, msglen, 0, text_color);
269 mem_free(msg);
271 if (download_is_progressing(download) && download->progress->size > 0) {
272 int xend = term->width - 1;
273 int width;
275 #ifdef CONFIG_LEDS
276 if (ses->status.show_leds)
277 xend -= term->leds_length;
278 #endif
280 width = int_max(0, xend - msglen - tab_info_len - 1);
281 if (width < 6) return;
282 int_upper_bound(&width, 20);
283 draw_progress_bar(download->progress, term,
284 xend - width, term->height - 1, width,
285 NULL, NULL);
289 static inline void
290 display_tab_bar(struct session *ses, struct terminal *term, int tabs_count)
292 struct color_pair *normal_color = get_bfu_color(term, "tabs.normal");
293 struct color_pair *selected_color = get_bfu_color(term, "tabs.selected");
294 struct color_pair *loading_color = get_bfu_color(term, "tabs.loading");
295 struct color_pair *fresh_color = get_bfu_color(term, "tabs.unvisited");
296 struct color_pair *tabsep_color = get_bfu_color(term, "tabs.separator");
297 struct session_status *status = &ses->status;
298 int tab_width = int_max(1, term->width / tabs_count);
299 int tab_total_width = tab_width * tabs_count;
300 int tab_remain_width = int_max(0, term->width - tab_total_width);
301 int tab_add = int_max(1, (tab_remain_width / tabs_count));
302 int tab_num;
303 struct box box;
305 if (status->show_tabs_bar_at_top) set_box(&box, 0, status->show_title_bar, term->width, 1);
306 else set_box(&box, 0, term->height - (status->show_status_bar ? 2 : 1), 0, 1);
308 for (tab_num = 0; tab_num < tabs_count; tab_num++) {
309 struct download *download = NULL;
310 struct color_pair *color = normal_color;
311 struct window *tab = get_tab_by_number(term, tab_num);
312 struct document_view *doc_view;
313 struct session *tab_ses = tab->data;
314 int actual_tab_width = tab_width - 1;
315 unsigned char *msg;
317 /* Adjust tab size to use full term width. */
318 if (tab_remain_width) {
319 actual_tab_width += tab_add;
320 tab_remain_width -= tab_add;
323 doc_view = tab_ses ? current_frame(tab_ses) : NULL;
325 if (doc_view) {
326 if (doc_view->document->title
327 && *(doc_view->document->title))
328 msg = doc_view->document->title;
329 else
330 msg = _("Untitled", term);
331 } else {
332 msg = _("No document", term);
335 if (tab_num) {
336 draw_char(term, box.x, box.y, BORDER_SVLINE,
337 SCREEN_ATTR_FRAME, tabsep_color);
338 box.x++;
341 if (tab_num == term->current_tab) {
342 color = selected_color;
344 } else {
345 download = get_current_download(tab_ses);
347 if (download && !is_in_state(download->state, S_OK)) {
348 color = loading_color;
349 } else if (!tab_ses || !tab_ses->status.visited) {
350 color = fresh_color;
353 if (!download_is_progressing(download)
354 || download->progress->size <= 0)
355 download = NULL;
358 box.width = actual_tab_width + 1;
359 draw_box(term, &box, ' ', 0, color);
361 if (download) {
362 draw_progress_bar(download->progress, term,
363 box.x, box.y, actual_tab_width,
364 msg, NULL);
365 } else {
366 int msglen;
367 #ifdef CONFIG_UTF8
368 if (term->utf8_cp) {
369 msglen = utf8_step_forward(msg, NULL,
370 actual_tab_width,
371 UTF8_STEP_CELLS_FEWER,
372 NULL) - msg;
373 } else
374 #endif /* CONFIG_UTF8 */
375 msglen = int_min(strlen(msg), actual_tab_width);
377 draw_text(term, box.x, box.y, msg, msglen, 0, color);
380 tab->xpos = box.x;
381 tab->width = actual_tab_width;
382 if (tab_num == tabs_count - 1) {
383 /* This is the last tab, and is therefore followed
384 * by a space, not a separator; increment tab->width
385 * to count that space as part of the tab.
386 * -- Miciah */
387 tab->width++;
390 box.x += actual_tab_width;
394 /* Print page's title and numbering at window top. */
395 static inline void
396 display_title_bar(struct session *ses, struct terminal *term)
398 struct document_view *doc_view;
399 struct document *document;
400 struct string title;
401 unsigned char buf[40];
402 int buflen = 0;
403 int height;
405 /* Clear the old title */
406 if (!get_opt_bool("ui.show_menu_bar_always", NULL)) {
407 struct box box;
409 set_box(&box, 0, 0, term->width, 1);
410 draw_box(term, &box, ' ', 0, get_bfu_color(term, "title.title-bar"));
413 doc_view = current_frame(ses);
414 if (!doc_view || !doc_view->document) return;
416 if (!init_string(&title)) return;
418 document = doc_view->document;
420 /* Set up the document page info string: '(' %page '/' %pages ')' */
421 height = doc_view->box.height;
422 if (height < document->height && doc_view->vs) {
423 int pos = doc_view->vs->y + height;
424 int page = 1;
425 int pages = height ? (document->height + height - 1) / height : 1;
427 /* Check if at the end else calculate the page. */
428 if (pos >= document->height) {
429 page = pages;
430 } else if (height) {
431 page = int_min((pos - height / 2) / height + 1, pages);
434 buflen = snprintf(buf, sizeof(buf), " (%d/%d)", page, pages);
435 if (buflen < 0) buflen = 0;
438 if (document->title) {
439 int maxlen = int_max(term->width - 4 - buflen, 0);
440 int titlelen, titlewidth;
442 #ifdef CONFIG_UTF8
443 if (term->utf8_cp) {
444 titlewidth = utf8_ptr2cells(document->title, NULL);
445 titlewidth = int_min(titlewidth, maxlen);
447 titlelen = utf8_cells2bytes(document->title,
448 titlewidth, NULL);
449 } else
450 #endif /* CONFIG_UTF8 */
452 titlewidth = int_min(strlen(document->title), maxlen);
453 titlelen = titlewidth;
456 add_bytes_to_string(&title, document->title, titlelen);
458 if (titlewidth == maxlen)
459 add_bytes_to_string(&title, "...", 3);
462 if (buflen > 0)
463 add_bytes_to_string(&title, buf, buflen);
465 if (title.length) {
466 int x;
467 #ifdef CONFIG_UTF8
468 if (term->utf8_cp) {
469 x = int_max(term->width - 1
470 - utf8_ptr2cells(title.source,
471 title.source
472 + title.length), 0);
473 } else
474 #endif /* CONFIG_UTF8 */
475 x = int_max(term->width - 1 - title.length, 0);
477 draw_text(term, x, 0, title.source, title.length, 0,
478 get_bfu_color(term, "title.title-text"));
481 done_string(&title);
484 static inline void
485 display_window_title(struct session *ses, struct terminal *term)
487 static struct session *last_ses;
488 struct session_status *status = &ses->status;
489 unsigned char *doc_title = NULL;
490 unsigned char *title;
491 int titlelen;
493 if (ses->doc_view
494 && ses->doc_view->document
495 && ses->doc_view->document->title
496 && ses->doc_view->document->title[0])
497 doc_title = ses->doc_view->document->title;
499 title = doc_title ? straconcat(doc_title, " - ELinks",
500 (unsigned char *) NULL)
501 : stracpy("ELinks");
502 if (!title) return;
504 titlelen = strlen(title);
505 if ((last_ses != ses
506 || !status->last_title
507 || strlen(status->last_title) != titlelen
508 || memcmp(status->last_title, title, titlelen))
509 && set_terminal_title(term, title) >= 0) {
510 mem_free_set(&status->last_title, title);
511 last_ses = ses;
512 } else {
513 mem_free(title);
517 #ifdef CONFIG_LEDS
518 static inline void
519 display_leds(struct session *ses, struct session_status *status)
521 if (ses->doc_view && ses->doc_view->document) {
522 struct cache_entry *cached = ses->doc_view->document->cached;
524 if (cached) {
525 if (cached->ssl_info)
526 set_led_value(status->ssl_led, 'S');
527 else
528 unset_led_value(status->ssl_led);
529 } else {
530 /* FIXME: We should do this thing better. */
531 set_led_value(status->ssl_led, '?');
535 if (ses->insert_mode == INSERT_MODE_LESS) {
536 set_led_value(status->insert_mode_led, 'i');
537 } else {
538 if (ses->insert_mode == INSERT_MODE_ON)
539 set_led_value(status->insert_mode_led, 'I');
540 else
541 unset_led_value(status->insert_mode_led);
544 draw_leds(ses);
546 #endif /* CONFIG_LEDS */
548 /* Print statusbar and titlebar, set terminal title. */
549 void
550 print_screen_status(struct session *ses)
552 struct terminal *term = ses->tab->term;
553 struct session_status *status = &ses->status;
554 int tabs_count = number_of_tabs(term);
555 int ses_tab_is_current = (ses->tab == get_current_tab(term));
557 if (ses_tab_is_current) {
558 if (status->set_window_title)
559 display_window_title(ses, term);
561 if (status->show_title_bar)
562 display_title_bar(ses, term);
564 if (status->show_status_bar)
565 display_status_bar(ses, term, tabs_count);
566 #ifdef CONFIG_LEDS
567 if (status->show_leds)
568 display_leds(ses, status);
569 #endif
571 if (!ses->status.visited)
572 ses->status.visited = 1;
575 if (status->show_tabs_bar) {
576 display_tab_bar(ses, term, tabs_count);
579 redraw_windows(REDRAW_IN_FRONT_OF_WINDOW, ses->tab);