1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00
elinks/src/dialogs/status.c

579 lines
15 KiB
C
Raw Normal View History

/* Sessions status management */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include "elinks.h"
#include "bfu/dialog.h"
#include "cache/cache.h"
#include "config/options.h"
#include "dialogs/progress.h"
#include "dialogs/status.h"
#include "document/document.h"
#include "document/renderer.h"
#include "document/view.h"
#include "intl/libintl.h"
#include "network/connection.h"
#include "network/progress.h"
#include "network/state.h"
#include "protocol/bittorrent/dialogs.h"
#include "protocol/protocol.h"
#include "protocol/uri.h"
#include "session/download.h"
#include "session/session.h"
#include "terminal/draw.h"
#include "terminal/screen.h"
#include "terminal/tab.h"
#include "terminal/terminal.h"
#include "terminal/window.h"
#include "util/color.h"
#include "util/conv.h"
#include "util/error.h"
#include "util/memory.h"
#include "util/snprintf.h"
#include "util/string.h"
#include "viewer/text/form.h"
#include "viewer/text/link.h"
#include "viewer/text/view.h"
char *
get_download_msg(struct download *download, struct terminal *term,
2022-02-17 15:14:36 -05:00
int wide, int full, const char *separator)
{
if (!download_is_progressing(download)) {
/* DBG("%d -> %s", download->state, _(get_err_msg(download->state), term)); */
return stracpy(get_state_message(download->state, term));
}
#ifdef CONFIG_BITTORRENT
if (download->conn
&& download->conn->uri->protocol == PROTOCOL_BITTORRENT)
return get_bittorrent_message(download, term, wide, full, separator);
#endif
if (download->conn && download->conn->http_upload_progress)
return get_upload_progress_msg(download->conn->http_upload_progress,
term, wide, full, separator);
return get_progress_msg(download->progress, term, wide, full, separator);
}
#define show_tabs(option, tabs) (((option) > 0) && !((option) == 1 && (tabs) < 2))
void
update_status(void)
{
int show_title_bar = get_opt_bool("ui.show_title_bar", NULL);
int show_status_bar = get_opt_bool("ui.show_status_bar", NULL);
int show_tabs_bar = get_opt_int("ui.tabs.show_bar", NULL);
int show_tabs_bar_at_top = get_opt_bool("ui.tabs.top", NULL);
#ifdef CONFIG_LEDS
int show_leds = get_opt_bool("ui.leds.enable", NULL);
#endif
int set_window_title = get_opt_bool("ui.window_title", NULL);
int insert_mode = get_opt_bool("document.browse.forms.insert_mode",
NULL);
struct session *ses;
int tabs_count = 1;
struct terminal *term = NULL;
foreach (ses, sessions) {
struct session_status *status = &ses->status;
int dirty = 0;
/* Try to descrease the number of tab calculation using that
* tab sessions share the same term. */
if (ses->tab->term != term) {
term = ses->tab->term;
tabs_count = number_of_tabs(term);
}
if (status->force_show_title_bar >= 0)
show_title_bar = status->force_show_title_bar;
if (status->show_title_bar != show_title_bar) {
status->show_title_bar = show_title_bar;
dirty = 1;
}
if (status->force_show_status_bar >= 0)
show_status_bar = status->force_show_status_bar;
if (status->show_status_bar != show_status_bar) {
status->show_status_bar = show_status_bar;
dirty = 1;
}
if (show_tabs(show_tabs_bar, tabs_count) != status->show_tabs_bar) {
status->show_tabs_bar = show_tabs(show_tabs_bar, tabs_count);
dirty = 1;
}
if (status->show_tabs_bar) {
if (status->show_tabs_bar_at_top != show_tabs_bar_at_top) {
status->show_tabs_bar_at_top = show_tabs_bar_at_top;
dirty = 1;
}
}
#ifdef CONFIG_LEDS
if (status->show_leds != show_leds) {
status->show_leds = show_leds;
}
#endif
status->set_window_title = set_window_title;
/* This more belongs to the current browsing state but ... */
if (!insert_mode)
ses->insert_mode = INSERT_MODE_LESS;
else if (ses->insert_mode == INSERT_MODE_LESS)
ses->insert_mode = INSERT_MODE_OFF;
if (!dirty) continue;
/* Force the current document to be rerendered so the
* document view and document height is updated to fit
* into the new dimensions. Related to bug 87. */
render_document_frames(ses, 1);
set_screen_dirty(term->screen, 0, term->height);
}
}
static char *
get_current_link_info_and_title(struct session *ses,
struct document_view *doc_view)
{
char *link_info, *link_title, *ret = NULL;
link_info = get_current_link_info(ses, doc_view);
if (!link_info) return NULL;
link_title = get_current_link_title(doc_view);
if (link_title) {
assert(*link_title);
ret = straconcat(link_info, " - ", link_title,
(char *) NULL);
mem_free(link_info);
mem_free(link_title);
}
if (!ret) ret = link_info;
return ret;
}
static inline void
display_status_bar(struct session *ses, struct terminal *term, int tabs_count)
{
char *msg = NULL;
unsigned int tab_info_len = 0;
struct download *download = get_current_download(ses);
struct session_status *status = &ses->status;
2022-03-18 14:22:38 -04:00
unsigned int text_color_node = 0;
int msglen;
struct el_box box;
#ifdef CONFIG_MARKS
if (ses->kbdprefix.mark != KP_MARK_NOTHING) {
switch (ses->kbdprefix.mark) {
case KP_MARK_NOTHING:
assert(0);
break;
case KP_MARK_SET:
msg = msg_text(term, N_("Enter a mark to set"));
break;
case KP_MARK_GOTO:
msg = msg_text(term, N_("Enter a mark"
" to which to jump"));
break;
}
} else
#endif
if (ses->kbdprefix.repeat_count) {
msg = msg_text(term, N_("Keyboard prefix: %d"),
ses->kbdprefix.repeat_count);
}
else if (ses->status.window_status) {
msg = stracpy(ses->status.window_status);
}
else if (download) {
struct document_view *doc_view = current_frame(ses);
/* Show S_INTERRUPTED message *once* but then show links
* again as usual. */
/* doc_view->vs may be NULL here in the short interval between
* ses_forward() with @loading_in_frame set, disconnecting the
* doc_view from vs, and render_document_frames(), detaching
* the doc_view. */
if (doc_view && doc_view->vs) {
static int last_current_link;
int ncl = doc_view->vs->current_link;
if (is_in_state(download->state, S_INTERRUPTED)
&& ncl != last_current_link)
download->state = connection_state(S_OK);
last_current_link = ncl;
if (is_in_state(download->state, S_OK)) {
if (get_current_link(doc_view)) {
msg = get_current_link_info_and_title(ses, doc_view);
} else if (ses->navigate_mode == NAVIGATE_CURSOR_ROUTING) {
msg = msg_text(term, N_("Cursor position: %dx%d"),
ses->tab->x + 1, ses->tab->y + 1);
}
}
}
if (!msg) {
int full = term->width > 130;
int wide = term->width > 80;
msg = get_download_msg(download, term, wide, full, ", ");
}
}
set_box(&box, 0, term->height - 1, term->width, 1);
2022-03-18 14:22:38 -04:00
draw_box_node(term, &box, ' ', 0, get_bfu_color_node(term, "status.status-bar"));
if (!status->show_tabs_bar && tabs_count > 1) {
char tab_info[8];
tab_info[tab_info_len++] = '[';
ulongcat(tab_info, &tab_info_len, term->current_tab + 1, 4, 0);
tab_info[tab_info_len++] = ']';
tab_info[tab_info_len++] = ' ';
tab_info[tab_info_len] = '\0';
2022-03-18 14:22:38 -04:00
text_color_node = get_bfu_color_node(term, "status.status-text");
draw_text_node(term, 0, term->height - 1, tab_info, tab_info_len,
0, text_color_node);
}
if (!msg) return;
2022-03-18 14:22:38 -04:00
if (!text_color_node)
text_color_node = get_bfu_color_node(term, "status.status-text");
msglen = strlen(msg);
2022-03-18 14:22:38 -04:00
draw_text_node(term, 0 + tab_info_len, term->height - 1,
msg, msglen, 0, text_color_node);
mem_free(msg);
if (download_is_progressing(download) && download->progress->size > 0) {
int xend = term->width - 1;
int width;
#ifdef CONFIG_LEDS
if (ses->status.show_leds)
xend -= term->leds_length;
#endif
width = int_max(0, xend - msglen - tab_info_len - 1);
if (width < 6) return;
int_upper_bound(&width, 20);
2022-03-18 14:22:38 -04:00
draw_progress_bar_node(download->progress, term,
xend - width, term->height - 1, width,
2022-03-18 14:22:38 -04:00
NULL, 0);
}
}
static inline void
display_tab_bar(struct session *ses, struct terminal *term, int tabs_count)
{
2022-03-18 14:22:38 -04:00
unsigned int normal_color_node = get_bfu_color_node(term, "tabs.normal");
unsigned int selected_color_node = get_bfu_color_node(term, "tabs.selected");
unsigned int loading_color_node = get_bfu_color_node(term, "tabs.loading");
unsigned int fresh_color_node = get_bfu_color_node(term, "tabs.unvisited");
unsigned int tabsep_color_node = get_bfu_color_node(term, "tabs.separator");
struct session_status *status = &ses->status;
int tab_width = int_max(1, term->width / tabs_count);
int tab_total_width = tab_width * tabs_count;
int tab_remain_width = int_max(0, term->width - tab_total_width);
int tab_add = int_max(1, (tab_remain_width / tabs_count));
int tab_num;
struct el_box box;
if (status->show_tabs_bar_at_top) set_box(&box, 0, status->show_title_bar, term->width, 1);
else set_box(&box, 0, term->height - (status->show_status_bar ? 2 : 1), 0, 1);
for (tab_num = 0; tab_num < tabs_count; tab_num++) {
struct download *download = NULL;
2022-03-18 14:22:38 -04:00
unsigned int color_node = normal_color_node;
struct window *tab = get_tab_by_number(term, tab_num);
struct document_view *doc_view;
2022-01-25 11:39:39 -05:00
struct session *tab_ses = (struct session *)tab->data;
int actual_tab_width = tab_width - 1;
char *msg;
/* Adjust tab size to use full term width. */
if (tab_remain_width) {
actual_tab_width += tab_add;
tab_remain_width -= tab_add;
}
doc_view = tab_ses ? current_frame(tab_ses) : NULL;
if (doc_view) {
if (doc_view->document->title
&& *(doc_view->document->title))
msg = doc_view->document->title;
else
msg = _("Untitled", term);
} else {
msg = _("No document", term);
}
if (tab_num) {
2022-03-18 14:22:38 -04:00
draw_char_node(term, box.x, box.y, BORDER_SVLINE,
SCREEN_ATTR_FRAME, tabsep_color_node);
box.x++;
}
if (tab_num == term->current_tab) {
2022-03-18 14:22:38 -04:00
color_node = selected_color_node;
} else {
download = get_current_download(tab_ses);
if (download && !is_in_state(download->state, S_OK)) {
2022-03-18 14:22:38 -04:00
color_node = loading_color_node;
} else if (!tab_ses || !tab_ses->status.visited) {
2022-03-18 14:22:38 -04:00
color_node = fresh_color_node;
}
if (!download_is_progressing(download)
|| download->progress->size <= 0)
download = NULL;
}
box.width = actual_tab_width + 1;
2022-03-18 14:22:38 -04:00
draw_box_node(term, &box, ' ', 0, color_node);
if (download) {
2022-03-18 14:22:38 -04:00
draw_progress_bar_node(download->progress, term,
box.x, box.y, actual_tab_width,
2022-03-18 14:22:38 -04:00
msg, 0);
} else {
int msglen;
#ifdef CONFIG_UTF8
if (term->utf8_cp) {
msglen = utf8_step_forward(msg, NULL,
actual_tab_width,
UTF8_STEP_CELLS_FEWER,
NULL) - msg;
} else
#endif /* CONFIG_UTF8 */
msglen = int_min(strlen(msg), actual_tab_width);
2022-03-18 14:22:38 -04:00
draw_text_node(term, box.x, box.y, msg, msglen, 0, color_node);
}
tab->xpos = box.x;
tab->width = actual_tab_width;
if (tab_num == tabs_count - 1) {
/* This is the last tab, and is therefore followed
* by a space, not a separator; increment tab->width
* to count that space as part of the tab.
* -- Miciah */
tab->width++;
}
box.x += actual_tab_width;
}
}
/* Print page's title and numbering at window top. */
static inline void
display_title_bar(struct session *ses, struct terminal *term)
{
struct document_view *doc_view;
struct document *document;
struct string title;
char buf[40];
int buflen = 0;
int height;
/* Clear the old title */
if (!get_opt_bool("ui.show_menu_bar_always", NULL)) {
struct el_box box;
set_box(&box, 0, 0, term->width, 1);
2022-03-18 14:22:38 -04:00
draw_box_node(term, &box, ' ', 0, get_bfu_color_node(term, "title.title-bar"));
}
doc_view = current_frame(ses);
if (!doc_view || !doc_view->document) return;
if (!init_string(&title)) return;
document = doc_view->document;
/* Set up the document page info string: '(' %page '/' %pages ')' */
height = doc_view->box.height;
if (height < document->height && doc_view->vs) {
int pos = doc_view->vs->y + height;
int page = 1;
int pages = height ? (document->height + height - 1) / height : 1;
/* Check if at the end else calculate the page. */
if (pos >= document->height) {
page = pages;
} else if (height) {
page = int_min((pos - height / 2) / height + 1, pages);
}
buflen = snprintf(buf, sizeof(buf), " (%d/%d)", page, pages);
if (buflen < 0) buflen = 0;
}
if (document->title) {
int maxlen = int_max(term->width - 4 - buflen, 0);
int titlelen, titlewidth;
#ifdef CONFIG_UTF8
if (term->utf8_cp) {
titlewidth = utf8_ptr2cells(document->title, NULL);
titlewidth = int_min(titlewidth, maxlen);
titlelen = utf8_cells2bytes(document->title,
titlewidth, NULL);
} else
#endif /* CONFIG_UTF8 */
{
titlewidth = int_min(strlen(document->title), maxlen);
titlelen = titlewidth;
}
add_bytes_to_string(&title, document->title, titlelen);
if (titlewidth == maxlen)
add_bytes_to_string(&title, "...", 3);
}
if (buflen > 0)
add_bytes_to_string(&title, buf, buflen);
if (title.length) {
int x;
#ifdef CONFIG_UTF8
if (term->utf8_cp) {
2006-07-27 03:51:10 -04:00
x = int_max(term->width - 1
- utf8_ptr2cells(title.source,
title.source
+ title.length), 0);
} else
#endif /* CONFIG_UTF8 */
x = int_max(term->width - 1 - title.length, 0);
2022-03-18 14:22:38 -04:00
draw_text_node(term, x, 0, title.source, title.length, 0,
get_bfu_color_node(term, "title.title-text"));
}
done_string(&title);
}
static inline void
display_window_title(struct session *ses, struct terminal *term)
{
static struct session *last_ses;
struct session_status *status = &ses->status;
char *doc_title = NULL;
char *title;
int titlelen;
if (ses->doc_view
&& ses->doc_view->document
&& ses->doc_view->document->title
&& ses->doc_view->document->title[0])
doc_title = ses->doc_view->document->title;
title = doc_title ? straconcat(doc_title, " - ELinks",
(char *) NULL)
: stracpy("ELinks");
if (!title) return;
titlelen = strlen(title);
Bug 885: Proper charset support in xterm window title When ELinks runs in an X11 terminal emulator (e.g. xterm), or in GNU Screen, it tries to update the title of the window to match the title of the current document. To do this, ELinks sends an "OSC 1 ; Pt BEL" sequence to the terminal. Unfortunately, xterm expects the Pt string to be in the ISO-8859-1 charset, making it impossible to display e.g. Cyrillic characters. In xterm patch #210 (2006-03-12) however, there is a menu item and a resource that can make xterm take the Pt string in UTF-8 instead, allowing characters from all around the world. The downside is that ELinks apparently cannot ask xterm whether the setting is on or off; so add a terminal._template_.latin1_title option to ELinks and let the user edit that instead. Complete list of changes: - Add the terminal._template_.latin1_title option. But do not add that to the terminal options window because it's already rather crowded there. - In set_window_title(), take a new codepage argument. Use it to decode the title into Unicode characters, and remove only actual control characters. For example, CP437 has graphical characters in the 0x80...0x9F range, so don't remove those, even though ISO-8859-1 has control characters in the same range. Likewise, don't misinterpret single bytes of UTF-8 characters as control characters. - In set_window_title(), do not truncate the title to the width of the window. The font is likely to be different and proportional anyway. But do truncate before 1024 bytes, an xterm limit. - In struct itrm, add a title_codepage member to remember which charset the master said it was going to use in the terminal window title. Initialize title_codepage in handle_trm(), update it in dispatch_special() if the master sends the new request TERM_FN_TITLE_CODEPAGE, and use it in most set_window_title() calls; but not in the one that sets $TERM as the title, because that string was not received from the master and should consist of ASCII characters only. - In set_terminal_title(), convert the caller-provided title to ISO-8859-1 or UTF-8 if appropriate, and report the codepage to the slave with the new TERM_FN_TITLE_CODEPAGE request. The conversion can run out of memory, so return a success/error flag, rather than void. In display_window_title(), check this result and don't update caches on error. - Add a NEWS entry for all of this.
2008-12-28 20:09:53 -05:00
if ((last_ses != ses
|| !status->last_title
|| strlen(status->last_title) != titlelen
|| memcmp(status->last_title, title, titlelen))
&& set_terminal_title(term, title) >= 0) {
mem_free_set(&status->last_title, title);
last_ses = ses;
} else {
mem_free(title);
}
}
#ifdef CONFIG_LEDS
static inline void
display_leds(struct session *ses, struct session_status *status)
{
if (ses->doc_view && ses->doc_view->document) {
struct cache_entry *cached = ses->doc_view->document->cached;
if (cached) {
if (cached->ssl_info)
set_led_value(status->ssl_led, 'S');
else
unset_led_value(status->ssl_led);
} else {
/* FIXME: We should do this thing better. */
set_led_value(status->ssl_led, '?');
}
}
if (ses->insert_mode == INSERT_MODE_LESS) {
set_led_value(status->insert_mode_led, 'i');
} else {
if (ses->insert_mode == INSERT_MODE_ON)
set_led_value(status->insert_mode_led, 'I');
else
unset_led_value(status->insert_mode_led);
}
draw_leds(ses);
}
#endif /* CONFIG_LEDS */
/* Print statusbar and titlebar, set terminal title. */
void
print_screen_status(struct session *ses)
{
struct terminal *term = ses->tab->term;
struct session_status *status = &ses->status;
int tabs_count = number_of_tabs(term);
int ses_tab_is_current = (ses->tab == get_current_tab(term));
if (ses_tab_is_current) {
if (status->set_window_title)
display_window_title(ses, term);
if (status->show_title_bar)
display_title_bar(ses, term);
if (status->show_status_bar)
display_status_bar(ses, term, tabs_count);
#ifdef CONFIG_LEDS
if (status->show_leds)
display_leds(ses, status);
#endif
if (!ses->status.visited)
ses->status.visited = 1;
}
if (status->show_tabs_bar) {
display_tab_bar(ses, term, tabs_count);
}
redraw_windows(REDRAW_IN_FRONT_OF_WINDOW, ses->tab);
}