irssi/src/fe-text/statusbar.c
Timo Sirainen 76c6e9ab83 Dependencies finally work correctly in perl libraries (they're each compiled
separately now). Added statusbar code to available to perl, it's now
possible to create new statusbar items with perl scripts.

statusbar_items_redraw(char *name) can now be used to easily redraw all
named statusbar items in screen.

Probably several other changes I've already forgotten :)


git-svn-id: http://svn.irssi.org/repos/irssi/trunk@1861 dbcabf3a-b0e7-0310-adc4-f8d773084564
2001-10-20 19:52:07 +00:00

1043 lines
27 KiB
C

/*
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "module.h"
#include "signals.h"
#include "expandos.h"
#include "special-vars.h"
#include "themes.h"
#include "statusbar.h"
#include "statusbar-config.h"
#include "statusbar-items.h"
#include "gui-windows.h"
#include "gui-printtext.h"
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;
void statusbar_item_register(const char *name, const char *value,
STATUSBAR_FUNC func)
{
gpointer hkey, hvalue;
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), func);
}
}
}
void statusbar_item_unregister(const char *name)
{
gpointer key, value;
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);
}
}
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 (strcmp(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);
}
static void statusbar_redraw_items(STATUSBAR_REC *bar)
{
WINDOW_REC *old_active_win;
GSList *tmp;
int xpos, rxpos;
old_active_win = active_win;
if (bar->parent_window != NULL)
active_win = bar->parent_window->active;
statusbar_resize_items(bar, screen_width);
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->xpos = xpos;
xpos += rec->size;
rec->func(rec, FALSE);
}
}
rxpos = screen_width;
for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
SBAR_ITEM_REC *rec = tmp->data;
if (rec->config->right_alignment && rec->size > 0) {
rxpos -= rec->size;
rec->xpos = rxpos;
rec->func(rec, FALSE);
}
}
active_win = old_active_win;
}
/*STATUSBAR_REC *statusbar_find(int pos, int line)
{
GSList *tmp;
for (tmp = statusbars; tmp != NULL; tmp = tmp->next) {
STATUSBAR_REC *rec = tmp->data;
if (rec->pos == pos && rec->line == line)
return rec;
}
return NULL;
}*/
void statusbar_redraw(STATUSBAR_REC *bar)
{
char *str;
if (bar == NULL) {
if (active_statusbar_group != NULL) {
screen_refresh_freeze();
g_slist_foreach(active_statusbar_group->bars,
(GFunc) statusbar_redraw, NULL);
screen_refresh_thaw();
}
return;
}
str = g_strconcat(bar->color, "%>", NULL);
gui_printtext(0, bar->real_ypos, str);
g_free(str);
statusbar_redraw_items(bar);
screen_refresh(NULL);
}
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);
if (item->max_size != item->size) {
/* item wants a new size - we'll need to redraw
the statusbar to see if this is allowed */
/*FIXME:fprintf(stderr, "%s resizes & redraws whole statusbar", item->config->name);*/
statusbar_redraw(item->bar);
} else {
/*FIXME:fprintf(stderr, "%s redrawing", item->config->name);*/
item->func(item, FALSE);
screen_refresh(NULL);
}
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 :
screen_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);
}
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)
{
mainwindow_recalc_ypos(window, STATUSBAR_TOP);
mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM);
}
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;
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..) */
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);
}
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 &&
strcmp(rec->config->name, name) == 0)
return rec;
}
return NULL;
}
static char *update_statusbar_bg(const char *str, const char *color)
{
GString *out;
char *ret;
out = g_string_new(color);
while (*str != '\0') {
if (*str == '%' && str[1] == 'n') {
g_string_append(out, color);
str += 2;
continue;
}
g_string_append_c(out, *str);
str++;
}
ret = out->str;
g_string_free(out, FALSE);
return ret;
}
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;
}
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;
int len;
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;
wiitem = active_win->active;
}
/* expand $variables */
tmpstr = parse_special_string(str, server, wiitem, data, NULL,
(escape_vars ? PARSE_FLAG_ESCAPE_VARS : 0 ) |
PARSE_FLAG_ESCAPE_THEME);
/* expand templates */
str = tmpstr;
tmpstr2 = theme_format_expand_data(current_theme, &str,
'n', 'n',
NULL, NULL,
EXPAND_FLAG_ROOT |
EXPAND_FLAG_IGNORE_REPLACES |
EXPAND_FLAG_IGNORE_EMPTY);
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 {
if (item->size < item->min_size) {
/* they're forcing us smaller than minimum size.. */
len = format_real_length(tmpstr, item->size);
tmpstr[len] = '\0';
}
tmpstr2 = update_statusbar_bg(tmpstr, item->bar->color);
gui_printtext(item->xpos, item->bar->real_ypos, tmpstr2);
g_free(tmpstr2);
}
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_to_id(MODULE_NAME, 1, *pos, func);
}
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 = 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);
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);
signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_server);
signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window);
signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window_item);
}
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);
}
#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);
}
}
}
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;
mainwindow_resize_freeze(rec);
statusbars_remove_unvisible(rec);
statusbars_add_visible(rec);
mainwindow_resize_thaw(rec);
}
}
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);
}
static void sig_setup_reload(void)
{
/* statusbar-config.c recreates root statusbars,
we need to create window-statusbars */
g_slist_foreach(mainwindows, (GFunc) statusbars_add_visible, NULL);
}
void statusbar_init(void)
{
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("window changed", (SIGNAL_FUNC) sig_window_changed);
signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
signal_add_last("setup reread", (SIGNAL_FUNC) sig_setup_reload);
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("window changed", (SIGNAL_FUNC) sig_window_changed);
signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
signal_remove("setup reread", (SIGNAL_FUNC) sig_setup_reload);
statusbar_items_deinit();
statusbar_config_deinit();
}