summaryrefslogtreecommitdiffstats
path: root/src/fe-text/statusbar.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/fe-text/statusbar.c1196
1 files changed, 1196 insertions, 0 deletions
diff --git a/src/fe-text/statusbar.c b/src/fe-text/statusbar.c
new file mode 100644
index 0000000..2439d10
--- /dev/null
+++ b/src/fe-text/statusbar.c
@@ -0,0 +1,1196 @@
+/*
+ statusbar.c : irssi
+
+ Copyright (C) 1999-2001 Timo Sirainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "module.h"
+#include <irssi/src/core/signals.h>
+#include <irssi/src/core/expandos.h>
+#include <irssi/src/core/special-vars.h>
+
+#include <irssi/src/fe-common/core/themes.h>
+
+#include <irssi/src/fe-text/statusbar.h>
+#include <irssi/src/fe-text/statusbar-config.h>
+#include <irssi/src/fe-text/gui-windows.h>
+#include <irssi/src/fe-text/gui-printtext.h>
+
+void statusbar_items_init(void);
+void statusbar_items_deinit(void);
+
+GSList *statusbar_groups;
+STATUSBAR_GROUP_REC *active_statusbar_group;
+
+/*
+ sbar_item_defs: char *name => char *value
+ sbar_item_funcs: char *name => STATUSBAR_FUNC func
+ sbar_signal_items: int signal_id => GSList *(SBAR_ITEM_REC *items)
+ sbar_item_signals: SBAR_ITEM_REC *item => GSList *(int *signal_ids)
+ named_sbar_items: const char *name => GSList *(SBAR_ITEM_REC *items)
+*/
+static GHashTable *sbar_item_defs, *sbar_item_funcs;
+static GHashTable *sbar_signal_items, *sbar_item_signals;
+static GHashTable *named_sbar_items;
+static int statusbar_need_recreate_items;
+
+void statusbar_item_register(const char *name, const char *value,
+ STATUSBAR_FUNC func)
+{
+ gpointer hkey, hvalue;
+
+ statusbar_need_recreate_items = TRUE;
+ if (value != NULL) {
+ if (g_hash_table_lookup_extended(sbar_item_defs,
+ name, &hkey, &hvalue)) {
+ g_hash_table_remove(sbar_item_defs, name);
+ g_free(hkey);
+ g_free(hvalue);
+ }
+ g_hash_table_insert(sbar_item_defs,
+ g_strdup(name), g_strdup(value));
+ }
+
+ if (func != NULL) {
+ if (g_hash_table_lookup(sbar_item_funcs, name) == NULL) {
+ g_hash_table_insert(sbar_item_funcs,
+ g_strdup(name), (void *) func);
+ }
+ }
+}
+
+void statusbar_item_unregister(const char *name)
+{
+ gpointer key, value;
+
+ statusbar_need_recreate_items = TRUE;
+ if (g_hash_table_lookup_extended(sbar_item_defs,
+ name, &key, &value)) {
+ g_hash_table_remove(sbar_item_defs, key);
+ g_free(key);
+ g_free(value);
+ }
+
+ if (g_hash_table_lookup_extended(sbar_item_funcs,
+ name, &key, &value)) {
+ g_hash_table_remove(sbar_item_funcs, key);
+ g_free(key);
+ }
+}
+
+void statusbar_item_set_size(struct SBAR_ITEM_REC *item, int min_size, int max_size)
+{
+ item->min_size = min_size;
+ item->max_size = max_size;
+}
+
+STATUSBAR_GROUP_REC *statusbar_group_create(const char *name)
+{
+ STATUSBAR_GROUP_REC *rec;
+
+ rec = g_new0(STATUSBAR_GROUP_REC, 1);
+ rec->name = g_strdup(name);
+
+ statusbar_groups = g_slist_append(statusbar_groups, rec);
+ return rec;
+}
+
+void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec)
+{
+ statusbar_groups = g_slist_remove(statusbar_groups, rec);
+
+ while (rec->bars != NULL)
+ statusbar_destroy(rec->bars->data);
+ while (rec->config_bars != NULL)
+ statusbar_config_destroy(rec, rec->config_bars->data);
+
+ g_free(rec->name);
+ g_free(rec);
+}
+
+STATUSBAR_GROUP_REC *statusbar_group_find(const char *name)
+{
+ GSList *tmp;
+
+ for (tmp = statusbar_groups; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_GROUP_REC *rec = tmp->data;
+
+ if (g_strcmp0(rec->name, name) == 0)
+ return rec;
+ }
+
+ return NULL;
+}
+
+static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2)
+{
+ return item1->config->priority == item2->config->priority ? 0 :
+ item1->config->priority < item2->config->priority ? -1 : 1;
+}
+
+static int sbar_cmp_position(STATUSBAR_REC *bar1, STATUSBAR_REC *bar2)
+{
+ return bar1->config->position < bar2->config->position ? -1 : 1;
+}
+
+/* Shink all items in statusbar to their minimum requested size.
+ The items list should be sorted by priority, highest first. */
+static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
+{
+ GSList *tmp;
+
+ for (tmp = items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ size -= (rec->max_size-rec->min_size);
+ rec->size = rec->min_size;
+
+ if (size <= max_width) {
+ rec->size += max_width-size;
+ break;
+ }
+
+ if (rec->size == 0) {
+ /* min_size was 0, item removed.
+ remove the marginal too */
+ size--;
+ }
+ }
+
+ return size;
+}
+
+/* shink the items in statusbar, even if their size gets smaller than
+ their minimum requested size. The items list should be sorted by
+ priority, highest first. */
+static void statusbar_shrink_forced(GSList *items, int size, int max_width)
+{
+ GSList *tmp;
+
+ for (tmp = items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ if (size-rec->size > max_width) {
+ /* remove the whole item */
+ size -= rec->size;
+ rec->size = 0;
+ } else {
+ /* shrink the item */
+ rec->size -= size-max_width;
+ break;
+ }
+ }
+}
+
+static void statusbar_resize_items(STATUSBAR_REC *bar, int max_width)
+{
+ GSList *tmp, *prior_sorted;
+ int width;
+
+ /* first give items their max. size */
+ prior_sorted = NULL;
+ width = 0;
+ for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ rec->func(rec, TRUE);
+ rec->size = rec->max_size;
+
+ if (rec->size > 0) {
+ width += rec->max_size;
+
+ prior_sorted = g_slist_insert_sorted(prior_sorted, rec,
+ (GCompareFunc)
+ sbar_item_cmp);
+ }
+ }
+
+ if (width > max_width) {
+ /* too big, start shrinking from items with lowest priority
+ and shrink until everything fits or until we've shrinked
+ all items. */
+ width = statusbar_shrink_to_min(prior_sorted, width,
+ max_width);
+ if (width > max_width) {
+ /* still need to shrink, remove the items with lowest
+ priority until everything fits to screen */
+ statusbar_shrink_forced(prior_sorted, width,
+ max_width);
+ }
+ }
+
+ g_slist_free(prior_sorted);
+}
+
+#define SBAR_ITEM_REDRAW_NEEDED(_bar, _item, _xpos) \
+ (((_bar)->dirty_xpos != -1 && (_xpos) >= (_bar)->dirty_xpos) || \
+ (_item)->xpos != (_xpos) || (_item)->current_size != (_item)->size)
+
+static void statusbar_calc_item_positions(STATUSBAR_REC *bar)
+{
+ WINDOW_REC *window;
+ WINDOW_REC *old_active_win;
+ GSList *tmp, *right_items;
+ int xpos, rxpos;
+ int max_width;
+
+ old_active_win = active_win;
+ if (bar->parent_window != NULL)
+ active_win = bar->parent_window->active;
+
+ window = bar->parent_window != NULL
+ ? bar->parent_window->active
+ : NULL;
+
+ max_width = window != NULL ? window->width : term_width;
+ statusbar_resize_items(bar, max_width);
+
+ /* left-aligned items */
+ xpos = 0;
+ for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ if (!rec->config->right_alignment &&
+ (rec->size > 0 || rec->current_size > 0)) {
+ if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, xpos)) {
+ /* redraw the item */
+ rec->dirty = TRUE;
+ if (bar->dirty_xpos == -1 ||
+ xpos < bar->dirty_xpos) {
+ irssi_set_dirty();
+ bar->dirty = TRUE;
+ bar->dirty_xpos = xpos;
+ }
+
+ rec->xpos = xpos;
+ }
+ xpos += rec->size;
+ }
+ }
+
+ /* right-aligned items - first copy them to a new list backwards,
+ easier to draw them in right order */
+ right_items = NULL;
+ for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ if (rec->config->right_alignment) {
+ if (rec->size > 0)
+ right_items = g_slist_prepend(right_items, rec);
+ else if (rec->current_size > 0 &&
+ (bar->dirty_xpos == -1 ||
+ rec->xpos < bar->dirty_xpos)) {
+ /* item was hidden - set the dirty position
+ to begin from the item's old xpos */
+ irssi_set_dirty();
+ bar->dirty = TRUE;
+ bar->dirty_xpos = rec->xpos;
+ }
+ }
+ }
+
+ rxpos = max_width;
+ for (tmp = right_items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ rxpos -= rec->size;
+ if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, rxpos)) {
+ rec->dirty = TRUE;
+ if (bar->dirty_xpos == -1 ||
+ rxpos < bar->dirty_xpos) {
+ irssi_set_dirty();
+ bar->dirty = TRUE;
+ bar->dirty_xpos = rxpos;
+ }
+ rec->xpos = rxpos;
+ }
+ }
+ g_slist_free(right_items);
+
+ active_win = old_active_win;
+}
+
+void statusbar_redraw(STATUSBAR_REC *bar, int force)
+{
+ if (statusbar_need_recreate_items)
+ return; /* don't bother yet */
+
+ if (bar != NULL) {
+ if (force) {
+ irssi_set_dirty();
+ bar->dirty = TRUE;
+ bar->dirty_xpos = 0;
+ }
+ statusbar_calc_item_positions(bar);
+ } else if (active_statusbar_group != NULL) {
+ g_slist_foreach(active_statusbar_group->bars,
+ (GFunc) statusbar_redraw,
+ GINT_TO_POINTER(force));
+ }
+}
+
+void statusbar_item_redraw(SBAR_ITEM_REC *item)
+{
+ WINDOW_REC *old_active_win;
+
+ g_return_if_fail(item != NULL);
+
+ old_active_win = active_win;
+ if (item->bar->parent_window != NULL)
+ active_win = item->bar->parent_window->active;
+
+ item->func(item, TRUE);
+
+ item->dirty = TRUE;
+ item->bar->dirty = TRUE;
+ irssi_set_dirty();
+
+ if (item->max_size != item->size) {
+ /* item wants a new size - we'll need to redraw
+ the statusbar to see if this is allowed */
+ statusbar_redraw(item->bar, item->config->right_alignment);
+ }
+
+ active_win = old_active_win;
+}
+
+void statusbar_items_redraw(const char *name)
+{
+ g_slist_foreach(g_hash_table_lookup(named_sbar_items, name),
+ (GFunc) statusbar_item_redraw, NULL);
+}
+
+static void statusbars_recalc_ypos(STATUSBAR_REC *bar)
+{
+ GSList *tmp, *bar_group;
+ int ypos;
+
+ /* get list of statusbars with same type and placement,
+ sorted by position */
+ bar_group = NULL;
+ tmp = bar->config->type == STATUSBAR_TYPE_ROOT ? bar->group->bars :
+ bar->parent_window->statusbars;
+
+ for (; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_REC *rec = tmp->data;
+
+ if (rec->config->type == bar->config->type &&
+ rec->config->placement == bar->config->placement) {
+ bar_group = g_slist_insert_sorted(bar_group, rec,
+ (GCompareFunc)
+ sbar_cmp_position);
+ }
+ }
+
+ if (bar_group == NULL) {
+ /* we just destroyed the last statusbar in this
+ type/placement group */
+ return;
+ }
+
+ /* get the Y-position for the first statusbar */
+ if (bar->config->type == STATUSBAR_TYPE_ROOT) {
+ ypos = bar->config->placement == STATUSBAR_TOP ? 0 :
+ term_height - g_slist_length(bar_group);
+ } else {
+ ypos = bar->config->placement == STATUSBAR_TOP ?
+ bar->parent_window->first_line :
+ bar->parent_window->last_line -
+ (g_slist_length(bar_group)-1);
+ }
+
+ /* set the Y-positions */
+ while (bar_group != NULL) {
+ bar = bar_group->data;
+
+ if (bar->real_ypos != ypos) {
+ bar->real_ypos = ypos;
+ statusbar_redraw(bar, TRUE);
+ }
+
+ ypos++;
+ bar_group = g_slist_remove(bar_group, bar_group->data);
+ }
+}
+
+static void sig_terminal_resized(void)
+{
+ GSList *tmp;
+
+ for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_REC *bar = tmp->data;
+
+ if (bar->config->type == STATUSBAR_TYPE_ROOT &&
+ bar->config->placement == STATUSBAR_BOTTOM) {
+ statusbars_recalc_ypos(bar);
+ break;
+ }
+ }
+}
+
+static void mainwindow_recalc_ypos(MAIN_WINDOW_REC *window, int placement)
+{
+ GSList *tmp;
+
+ for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_REC *bar = tmp->data;
+
+ if (bar->config->placement == placement) {
+ statusbars_recalc_ypos(bar);
+ break;
+ }
+ }
+}
+
+static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
+{
+ GSList *tmp;
+ mainwindow_recalc_ypos(window, STATUSBAR_TOP);
+ mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM);
+ for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_REC *bar = tmp->data;
+ statusbar_redraw(bar, TRUE);
+ }
+}
+
+STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group,
+ STATUSBAR_CONFIG_REC *config,
+ MAIN_WINDOW_REC *parent_window)
+{
+ STATUSBAR_REC *bar;
+ THEME_REC *theme;
+ GSList *tmp;
+ char *name, *value;
+
+ g_return_val_if_fail(group != NULL, NULL);
+ g_return_val_if_fail(config != NULL, NULL);
+ g_return_val_if_fail(config->type != STATUSBAR_TYPE_WINDOW ||
+ parent_window != NULL, NULL);
+
+ bar = g_new0(STATUSBAR_REC, 1);
+ group->bars = g_slist_append(group->bars, bar);
+
+ bar->group = group;
+
+ bar->config = config;
+ bar->parent_window = parent_window;
+
+ irssi_set_dirty();
+ bar->dirty = TRUE;
+ bar->dirty_xpos = 0;
+
+ signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+ signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+
+ if (config->type == STATUSBAR_TYPE_ROOT) {
+ /* top/bottom of the screen */
+ mainwindows_reserve_lines(config->placement == STATUSBAR_TOP,
+ config->placement == STATUSBAR_BOTTOM);
+ theme = current_theme;
+ } else {
+ /* top/bottom of the window */
+ parent_window->statusbars =
+ g_slist_append(parent_window->statusbars, bar);
+ mainwindow_set_statusbar_lines(parent_window,
+ config->placement == STATUSBAR_TOP,
+ config->placement == STATUSBAR_BOTTOM);
+ theme = parent_window != NULL && parent_window->active != NULL &&
+ parent_window->active->theme != NULL ?
+ parent_window->active->theme : current_theme;
+ }
+
+ signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+ signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+
+ /* get background color from sb_background abstract */
+ name = g_strdup_printf("{sb_%s_bg}", config->name);
+ value = theme_format_expand(theme, name);
+ g_free(name);
+
+ if (*value == '\0') {
+ /* try with the statusbar group name */
+ g_free(value);
+
+ name = g_strdup_printf("{sb_%s_bg}", group->name);
+ value = theme_format_expand(theme, name);
+ g_free(name);
+
+ if (*value == '\0') {
+ /* fallback to default statusbar background
+ (also provides backwards compatibility..) */
+ g_free(value);
+ value = theme_format_expand(theme, "{sb_background}");
+ }
+ }
+
+ if (*value == '\0') {
+ g_free(value);
+ value = g_strdup("%8");
+ }
+ bar->color = g_strconcat("%n", value, NULL);
+ g_free(value);
+
+ statusbars_recalc_ypos(bar);
+ signal_emit("statusbar created", 1, bar);
+
+ /* create the items to statusbar */
+ for (tmp = config->items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_CONFIG_REC *rec = tmp->data;
+
+ statusbar_item_create(bar, rec);
+ }
+ return bar;
+}
+
+void statusbar_destroy(STATUSBAR_REC *bar)
+{
+ int top;
+
+ g_return_if_fail(bar != NULL);
+
+ bar->group->bars = g_slist_remove(bar->group->bars, bar);
+ if (bar->parent_window != NULL) {
+ bar->parent_window->statusbars =
+ g_slist_remove(bar->parent_window->statusbars, bar);
+ }
+
+ signal_emit("statusbar destroyed", 1, bar);
+
+ while (bar->items != NULL)
+ statusbar_item_destroy(bar->items->data);
+
+ g_free(bar->color);
+
+ if (bar->config->type != STATUSBAR_TYPE_WINDOW ||
+ bar->parent_window != NULL)
+ statusbars_recalc_ypos(bar);
+
+ top = bar->config->placement == STATUSBAR_TOP;
+ if (bar->config->type == STATUSBAR_TYPE_ROOT) {
+ /* top/bottom of the screen */
+ mainwindows_reserve_lines(top ? -1 : 0, !top ? -1 : 0);
+ } else if (bar->parent_window != NULL) {
+ /* top/bottom of the window */
+ mainwindow_set_statusbar_lines(bar->parent_window,
+ top ? -1 : 0, !top ? -1 : 0);
+ }
+
+ g_free(bar);
+}
+
+void statusbar_recreate_items(STATUSBAR_REC *bar)
+{
+ GSList *tmp;
+
+ /* destroy */
+ while (bar->items != NULL)
+ statusbar_item_destroy(bar->items->data);
+
+ /* create */
+ for (tmp = bar->config->items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_CONFIG_REC *rec = tmp->data;
+
+ statusbar_item_create(bar, rec);
+ }
+
+ statusbar_redraw(bar, TRUE);
+}
+
+void statusbars_recreate_items(void)
+{
+ if (active_statusbar_group != NULL) {
+ g_slist_foreach(active_statusbar_group->bars,
+ (GFunc) statusbar_recreate_items, NULL);
+ }
+}
+
+STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
+ MAIN_WINDOW_REC *window)
+{
+ GSList *tmp;
+
+ for (tmp = group->bars; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_REC *rec = tmp->data;
+
+ if (rec->parent_window == window &&
+ g_strcmp0(rec->config->name, name) == 0)
+ return rec;
+ }
+
+ return NULL;
+}
+
+static const char *statusbar_item_get_value(SBAR_ITEM_REC *item)
+{
+ const char *value;
+
+ value = item->config->value;
+ if (value == NULL) {
+ value = g_hash_table_lookup(sbar_item_defs,
+ item->config->name);
+ }
+
+ return value;
+}
+
+static GString *finalize_string(const char *str, const char *color)
+{
+ GString *out;
+
+ out = g_string_new(color);
+
+ while (*str != '\0') {
+ if ((unsigned char) *str < 32 ||
+ (term_type == TERM_TYPE_8BIT &&
+ (unsigned char) (*str & 0x7f) < 32)) {
+ /* control char */
+ g_string_append_printf(out, "%%8%c%%8",
+ 'A'-1 + (*str & 0x7f));
+ } else if (*str == '%' && str[1] == 'n') {
+ g_string_append(out, color);
+ str++;
+ } else {
+ g_string_append_c(out, *str);
+ }
+
+ str++;
+ }
+
+ return out;
+}
+
+void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
+ const char *str, const char *data,
+ int escape_vars)
+{
+ SERVER_REC *server;
+ WI_ITEM_REC *wiitem;
+ char *tmpstr, *tmpstr2;
+ theme_rm_col reset;
+ int len;
+ strcpy(reset.m, "n");
+
+ if (str == NULL)
+ str = statusbar_item_get_value(item);
+ if (str == NULL || *str == '\0') {
+ item->min_size = item->max_size = 0;
+ return;
+ }
+
+ if (active_win == NULL) {
+ server = NULL;
+ wiitem = NULL;
+ } else {
+ server = active_win->active_server != NULL ?
+ active_win->active_server : active_win->connect_server;
+ wiitem = active_win->active;
+ }
+
+ /* expand templates */
+ tmpstr = theme_format_expand_data(current_theme, &str,
+ reset, reset,
+ NULL, NULL,
+ EXPAND_FLAG_ROOT |
+ EXPAND_FLAG_IGNORE_REPLACES |
+ EXPAND_FLAG_IGNORE_EMPTY);
+ /* expand $variables */
+ tmpstr2 = parse_special_string(tmpstr, server, wiitem, data, NULL,
+ (escape_vars ? PARSE_FLAG_ESCAPE_VARS : 0 ));
+ g_free(tmpstr);
+
+ /* remove color codes (not %formats) */
+ tmpstr = strip_codes(tmpstr2);
+ g_free(tmpstr2);
+
+ if (get_size_only) {
+ item->min_size = item->max_size = format_get_length(tmpstr);
+ } else {
+ GString *out;
+
+ if (item->size < item->min_size) {
+ /* they're forcing us smaller than minimum size.. */
+ len = format_real_length(tmpstr, item->size);
+ tmpstr[len] = '\0';
+ }
+ out = finalize_string(tmpstr, item->bar->color);
+ /* make sure the str is big enough to fill the
+ requested size, so it won't corrupt screen */
+ len = format_get_length(tmpstr);
+ if (len < item->size) {
+ int i;
+
+ len = item->size-len;
+ for (i = 0; i < len; i++)
+ g_string_append_c(out, ' ');
+ }
+
+ gui_printtext(ITEM_WINDOW_REAL_XPOS(item), item->bar->real_ypos, out->str);
+ g_string_free(out, TRUE);
+ }
+ g_free(tmpstr);
+}
+
+static void statusbar_item_default_func(SBAR_ITEM_REC *item, int get_size_only)
+{
+ statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE);
+}
+
+static void statusbar_update_item(void)
+{
+ GSList *items;
+
+ items = g_hash_table_lookup(sbar_signal_items,
+ GINT_TO_POINTER(signal_get_emitted_id()));
+ while (items != NULL) {
+ SBAR_ITEM_REC *item = items->data;
+
+ statusbar_item_redraw(item);
+ items = items->next;
+ }
+}
+
+static void statusbar_update_server(SERVER_REC *server)
+{
+ SERVER_REC *item_server;
+ GSList *items;
+
+ items = g_hash_table_lookup(sbar_signal_items,
+ GINT_TO_POINTER(signal_get_emitted_id()));
+ while (items != NULL) {
+ SBAR_ITEM_REC *item = items->data;
+
+ item_server = item->bar->parent_window != NULL ?
+ item->bar->parent_window->active->active_server :
+ active_win->active_server;
+
+ if (item_server == server)
+ statusbar_item_redraw(item);
+
+ items = items->next;
+ }
+}
+
+static void statusbar_update_window(WINDOW_REC *window)
+{
+ WINDOW_REC *item_window;
+ GSList *items;
+
+ items = g_hash_table_lookup(sbar_signal_items,
+ GINT_TO_POINTER(signal_get_emitted_id()));
+ while (items != NULL) {
+ SBAR_ITEM_REC *item = items->data;
+
+ item_window = item->bar->parent_window != NULL ?
+ item->bar->parent_window->active : active_win;
+
+ if (item_window == window)
+ statusbar_item_redraw(item);
+
+ items = items->next;
+ }
+}
+
+static void statusbar_update_window_item(WI_ITEM_REC *wiitem)
+{
+ WI_ITEM_REC *item_wi;
+ GSList *items;
+
+ items = g_hash_table_lookup(sbar_signal_items,
+ GINT_TO_POINTER(signal_get_emitted_id()));
+ while (items != NULL) {
+ SBAR_ITEM_REC *item = items->data;
+
+ item_wi = item->bar->parent_window != NULL ?
+ item->bar->parent_window->active->active :
+ active_win->active;
+
+ if (item_wi == wiitem)
+ statusbar_item_redraw(item);
+
+ items = items->next;
+ }
+}
+
+static void statusbar_item_default_signals(SBAR_ITEM_REC *item)
+{
+ SIGNAL_FUNC func;
+ GSList *list;
+ const char *value;
+ void *signal_id;
+ int *signals, *pos;
+
+ value = statusbar_item_get_value(item);
+ if (value == NULL)
+ return;
+
+ signals = special_vars_get_signals(value);
+ if (signals == NULL)
+ return;
+
+ for (pos = signals; *pos != -1; pos += 2) {
+ /* update signal -> item mappings */
+ signal_id = GINT_TO_POINTER(*pos);
+ list = g_hash_table_lookup(sbar_signal_items, signal_id);
+ if (list == NULL) {
+ switch (pos[1]) {
+ case EXPANDO_ARG_NONE:
+ func = (SIGNAL_FUNC) statusbar_update_item;
+ break;
+ case EXPANDO_ARG_SERVER:
+ func = (SIGNAL_FUNC) statusbar_update_server;
+ break;
+ case EXPANDO_ARG_WINDOW:
+ func = (SIGNAL_FUNC) statusbar_update_window;
+ break;
+ case EXPANDO_ARG_WINDOW_ITEM:
+ func = (SIGNAL_FUNC) statusbar_update_window_item;
+ break;
+ default:
+ func = NULL;
+ break;
+ }
+ if (func != NULL) {
+ signal_add_full_id(MODULE_NAME,
+ SIGNAL_PRIORITY_DEFAULT,
+ *pos, func, NULL);
+ }
+ }
+
+ if (g_slist_find(list, item) == NULL)
+ list = g_slist_append(list, item);
+ g_hash_table_insert(sbar_signal_items, signal_id, list);
+
+ /* update item -> signal mappings */
+ list = g_hash_table_lookup(sbar_item_signals, item);
+ if (g_slist_find(list, signal_id) == NULL)
+ list = g_slist_append(list, signal_id);
+ g_hash_table_insert(sbar_item_signals, item, list);
+ }
+ g_free(signals);
+}
+
+SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
+ SBAR_ITEM_CONFIG_REC *config)
+{
+ SBAR_ITEM_REC *rec;
+ GSList *items;
+
+ g_return_val_if_fail(bar != NULL, NULL);
+ g_return_val_if_fail(config != NULL, NULL);
+
+ rec = g_new0(SBAR_ITEM_REC, 1);
+ bar->items = g_slist_append(bar->items, rec);
+
+ rec->bar = bar;
+ rec->config = config;
+
+ rec->func = (STATUSBAR_FUNC) g_hash_table_lookup(sbar_item_funcs,
+ config->name);
+ if (rec->func == NULL)
+ rec->func = statusbar_item_default_func;
+ statusbar_item_default_signals(rec);
+
+ items = g_hash_table_lookup(named_sbar_items, config->name);
+ items = g_slist_append(items, rec);
+ g_hash_table_insert(named_sbar_items, config->name, items);
+
+ irssi_set_dirty();
+ rec->dirty = TRUE;
+ bar->dirty = TRUE;
+
+ signal_emit("statusbar item created", 1, rec);
+ return rec;
+}
+
+static void statusbar_signal_remove(int signal_id)
+{
+ signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_item, NULL);
+ signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_server, NULL);
+ signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window, NULL);
+ signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window_item, NULL);
+}
+
+static void statusbar_item_remove_signal(SBAR_ITEM_REC *item, int signal_id)
+{
+ GSList *list;
+
+ /* update signal -> item hash */
+ list = g_hash_table_lookup(sbar_signal_items,
+ GINT_TO_POINTER(signal_id));
+ list = g_slist_remove(list, item);
+ if (list != NULL) {
+ g_hash_table_insert(sbar_signal_items,
+ GINT_TO_POINTER(signal_id), list);
+ } else {
+ g_hash_table_remove(sbar_signal_items,
+ GINT_TO_POINTER(signal_id));
+ statusbar_signal_remove(signal_id);
+ }
+}
+
+void statusbar_item_destroy(SBAR_ITEM_REC *item)
+{
+ GSList *list;
+
+ g_return_if_fail(item != NULL);
+
+ item->bar->items = g_slist_remove(item->bar->items, item);
+
+ list = g_hash_table_lookup(named_sbar_items, item->config->name);
+ list = g_slist_remove(list, item);
+ if (list == NULL)
+ g_hash_table_remove(named_sbar_items, item->config->name);
+ else
+ g_hash_table_insert(named_sbar_items, item->config->name, list);
+
+ signal_emit("statusbar item destroyed", 1, item);
+
+ list = g_hash_table_lookup(sbar_item_signals, item);
+ g_hash_table_remove(sbar_item_signals, item);
+
+ while (list != NULL) {
+ statusbar_item_remove_signal(item, GPOINTER_TO_INT(list->data));
+ list = g_slist_remove(list, list->data);
+ }
+
+ g_free(item);
+}
+
+static MAIN_WINDOW_BORDER_REC *set_border_info(STATUSBAR_REC *bar)
+{
+ MAIN_WINDOW_BORDER_REC *orig_border, *new_border;
+ orig_border = clrtoeol_info;
+ new_border = g_new0(MAIN_WINDOW_BORDER_REC, 1);
+ new_border->window = bar->parent_window != NULL ? bar->parent_window->screen_win : NULL;
+ new_border->color = bar->color;
+ clrtoeol_info = new_border;
+ return orig_border;
+}
+
+static void restore_border_info(MAIN_WINDOW_BORDER_REC *border_info)
+{
+ MAIN_WINDOW_BORDER_REC *old_border;
+ old_border = clrtoeol_info;
+ clrtoeol_info = border_info;
+ g_free(old_border);
+}
+
+static void statusbar_redraw_needed_items(STATUSBAR_REC *bar)
+{
+ WINDOW_REC *old_active_win;
+ GSList *tmp;
+ char *str;
+
+ old_active_win = active_win;
+ if (bar->parent_window != NULL)
+ active_win = bar->parent_window->active;
+
+ if (bar->dirty_xpos >= 0) {
+ MAIN_WINDOW_BORDER_REC *orig_border;
+ orig_border = set_border_info(bar);
+ str = g_strconcat(bar->color, "%>", NULL);
+ gui_printtext(BAR_WINDOW_REAL_DIRTY_XPOS(bar), bar->real_ypos, str);
+ g_free(str);
+ restore_border_info(orig_border);
+ }
+
+ for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
+ SBAR_ITEM_REC *rec = tmp->data;
+
+ if (rec->dirty ||
+ (bar->dirty_xpos != -1 &&
+ rec->xpos >= bar->dirty_xpos)) {
+ rec->current_size = rec->size;
+ rec->func(rec, FALSE);
+ rec->dirty = FALSE;
+ }
+ }
+
+ active_win = old_active_win;
+}
+
+void statusbar_redraw_dirty(void)
+{
+ GSList *tmp;
+
+ if (statusbar_need_recreate_items) {
+ statusbar_need_recreate_items = FALSE;
+ statusbars_recreate_items();
+ }
+
+ for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_REC *rec = tmp->data;
+
+ if (rec->dirty) {
+ statusbar_redraw_needed_items(rec);
+ rec->dirty = FALSE;
+ rec->dirty_xpos = -1;
+ }
+ }
+}
+
+#define STATUSBAR_IS_VISIBLE(bar, window) \
+ ((bar)->visible == STATUSBAR_VISIBLE_ALWAYS || \
+ (active_mainwin == (window) && \
+ (bar)->visible == STATUSBAR_VISIBLE_ACTIVE) || \
+ (active_mainwin != (window) && \
+ (bar)->visible == STATUSBAR_VISIBLE_INACTIVE))
+
+static void statusbars_remove_unvisible(MAIN_WINDOW_REC *window)
+{
+ GSList *tmp, *next;
+
+ for (tmp = window->statusbars; tmp != NULL; tmp = next) {
+ STATUSBAR_REC *bar = tmp->data;
+
+ next = tmp->next;
+ if (!STATUSBAR_IS_VISIBLE(bar->config, window))
+ statusbar_destroy(bar);
+ }
+}
+
+static void statusbars_add_visible(MAIN_WINDOW_REC *window)
+{
+ STATUSBAR_GROUP_REC *group;
+ STATUSBAR_REC *bar;
+ GSList *tmp;
+
+ group = active_statusbar_group;
+ for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
+ STATUSBAR_CONFIG_REC *config = tmp->data;
+
+ if (config->type == STATUSBAR_TYPE_WINDOW &&
+ STATUSBAR_IS_VISIBLE(config, window) &&
+ statusbar_find(group, config->name, window) == NULL) {
+ bar = statusbar_create(group, config, window);
+ statusbar_redraw(bar, TRUE);
+ }
+ }
+}
+
+static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window)
+{
+ while (window->statusbars != NULL) {
+ STATUSBAR_REC *bar = window->statusbars->data;
+
+ bar->parent_window->statusbars =
+ g_slist_remove(bar->parent_window->statusbars, bar);
+ bar->parent_window = NULL;
+ statusbar_destroy(bar);
+ }
+}
+
+static void sig_window_changed(void)
+{
+ GSList *tmp;
+
+ for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
+ MAIN_WINDOW_REC *rec = tmp->data;
+
+ statusbars_remove_unvisible(rec);
+ statusbars_add_visible(rec);
+ }
+}
+
+static void sig_gui_window_created(WINDOW_REC *window)
+{
+ statusbars_add_visible(WINDOW_MAIN(window));
+}
+
+static void statusbar_item_def_destroy(void *key, void *value)
+{
+ g_free(key);
+ g_free(value);
+}
+
+static void statusbar_signal_item_destroy(void *key, GSList *value)
+{
+ while (value != NULL) {
+ statusbar_signal_remove(GPOINTER_TO_INT(value->data));
+ value->data = g_slist_remove(value, value->data);
+ }
+}
+
+static void statusbar_item_signal_destroy(void *key, GSList *value)
+{
+ g_slist_free(value);
+}
+
+void statusbars_create_window_bars(void)
+{
+ g_slist_foreach(mainwindows, (GFunc) statusbars_add_visible, NULL);
+}
+
+void statusbar_init(void)
+{
+ statusbar_need_recreate_items = FALSE;
+ statusbar_groups = NULL;
+ active_statusbar_group = NULL;
+ sbar_item_defs = g_hash_table_new((GHashFunc) g_str_hash,
+ (GCompareFunc) g_str_equal);
+ sbar_item_funcs = g_hash_table_new((GHashFunc) g_str_hash,
+ (GCompareFunc) g_str_equal);
+ sbar_signal_items = g_hash_table_new((GHashFunc) g_direct_hash,
+ (GCompareFunc) g_direct_equal);
+ sbar_item_signals = g_hash_table_new((GHashFunc) g_direct_hash,
+ (GCompareFunc) g_direct_equal);
+ named_sbar_items = g_hash_table_new((GHashFunc) g_str_hash,
+ (GCompareFunc) g_str_equal);
+
+ signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+ signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_add("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
+ signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
+ signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
+
+ statusbar_items_init();
+ statusbar_config_init(); /* signals need to be before this call */
+}
+
+void statusbar_deinit(void)
+{
+ while (statusbar_groups != NULL)
+ statusbar_group_destroy(statusbar_groups->data);
+
+ g_hash_table_foreach(sbar_item_defs,
+ (GHFunc) statusbar_item_def_destroy, NULL);
+ g_hash_table_destroy(sbar_item_defs);
+
+ g_hash_table_foreach(sbar_item_funcs, (GHFunc) g_free, NULL);
+ g_hash_table_destroy(sbar_item_funcs);
+
+ g_hash_table_foreach(sbar_signal_items,
+ (GHFunc) statusbar_signal_item_destroy, NULL);
+ g_hash_table_destroy(sbar_signal_items);
+ g_hash_table_foreach(sbar_item_signals,
+ (GHFunc) statusbar_item_signal_destroy, NULL);
+ g_hash_table_destroy(sbar_item_signals);
+ g_hash_table_destroy(named_sbar_items);
+
+ signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
+ signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
+ signal_remove("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
+ signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
+ signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
+
+ statusbar_items_deinit();
+ statusbar_config_deinit();
+}