diff options
Diffstat (limited to '')
-rw-r--r-- | src/fe-text/statusbar.c | 1196 |
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(); +} |