/* 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 "themes.h" #include "statusbar.h" #include "gui-windows.h" static int backs[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* FIXME: should be in some more generic place.. */ void statusbar_items_init(void); void statusbar_items_deinit(void); static GSList *statusbars; static int sbar_uppest, sbar_lowest, sbars_up, sbars_down; static void statusbar_item_destroy(SBAR_ITEM_REC *rec) { rec->bar->items = g_slist_remove(rec->bar->items, rec); g_free(rec); } static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2) { return item1->priority == item2->priority ? 0 : item1->priority < item2->priority ? -1 : 1; } 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; } 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+1; /* +1 == the marginal */ rec->size = 0; } else { /* shrink the item */ rec->size -= size-max_width; break; } } } static void statusbar_get_sizes(STATUSBAR_REC *bar, int max_width) { GSList *tmp, *prior_sorted; int width; /* first give items their max. size */ prior_sorted = NULL; width = -1; /* -1 because of the marginals */ 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) { /* +1 == marginal between items */ width += rec->max_size+1; 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_line(STATUSBAR_REC *bar) { GSList *tmp; int xpos, rxpos; statusbar_get_sizes(bar, COLS-2); xpos = 1; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; if (!rec->right_justify && rec->size > 0) { rec->xpos = xpos; xpos += rec->size+1; rec->func(rec, FALSE); } } rxpos = COLS-1; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; if (rec->right_justify && rec->size > 0) { rxpos -= rec->size+1; rec->xpos = rxpos+1; rec->func(rec, FALSE); } } } static void statusbar_redraw_all(void) { screen_refresh_freeze(); g_slist_foreach(statusbars, (GFunc) statusbar_redraw, NULL); screen_refresh_thaw(); } 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) { if (bar == NULL) { statusbar_redraw_all(); return; } set_bg(stdscr, backs[bar->color] << 4); move(bar->ypos, 0); clrtoeol(); set_bg(stdscr, 0); statusbar_redraw_line(bar); screen_refresh(NULL); } void statusbar_item_redraw(SBAR_ITEM_REC *item) { g_return_if_fail(item != NULL); item->func(item, TRUE); if (item->max_size != item->size) statusbar_redraw(item->bar); else { item->func(item, FALSE); screen_refresh(NULL); } } static int get_last_bg(const char *str) { int last = -1; while (*str != '\0') { if (*str == '%' && str[1] != '\0') { str++; if (*str >= '0' && *str <= '7') last = *str-'0'; } str++; } return last; } /* ypos is used only when pos == STATUSBAR_POS_MIDDLE */ STATUSBAR_REC *statusbar_create(int pos, int ypos) { STATUSBAR_REC *rec; char *str; rec = g_new0(STATUSBAR_REC, 1); statusbars = g_slist_append(statusbars, rec); rec->pos = pos; rec->line = pos == STATUSBAR_POS_MIDDLE ? ypos : mainwindows_reserve_lines(1, pos == STATUSBAR_POS_UP); rec->ypos = pos == STATUSBAR_POS_MIDDLE ? ypos : pos == STATUSBAR_POS_UP ? rec->line : LINES-1-rec->line; /* get background color from sb_background abstract */ str = theme_format_expand(current_theme, "{sb_background}"); if (str == NULL) str = g_strdup("%n%8"); rec->color_string = g_strconcat("%n", str, NULL); g_free(str); rec->color = get_last_bg(rec->color_string); if (rec->color < 0) rec->color = current_theme->default_real_color; if (pos == STATUSBAR_POS_UP) { if (sbars_up == 0) sbar_uppest = rec->line; sbars_up++; rec->line -= sbar_uppest; } else if (pos == STATUSBAR_POS_DOWN) { if (sbars_down == 0) sbar_lowest = rec->line; sbars_down++; rec->line -= sbar_lowest; } set_bg(stdscr, backs[rec->color] << 4); move(rec->ypos, 0); clrtoeol(); set_bg(stdscr, 0); return rec; } static void statusbars_pack(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) { rec->line--; rec->ypos += (pos == STATUSBAR_POS_UP ? -1 : 1); } } } void statusbar_destroy(STATUSBAR_REC *bar) { g_return_if_fail(bar != NULL); if (bar->pos != STATUSBAR_POS_MIDDLE) mainwindows_reserve_lines(-1, bar->pos == STATUSBAR_POS_UP); if (bar->pos == STATUSBAR_POS_UP) sbars_up--; if (bar->pos == STATUSBAR_POS_DOWN) sbars_down--; statusbars = g_slist_remove(statusbars, bar); while (bar->items != NULL) statusbar_item_destroy(bar->items->data); if (bar->pos != STATUSBAR_POS_MIDDLE) statusbars_pack(bar->pos, bar->pos); g_free(bar->color_string); g_free(bar); if (!quitting) statusbar_redraw_all(); } SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar, int priority, int right_justify, STATUSBAR_FUNC func) { SBAR_ITEM_REC *rec; g_return_val_if_fail(bar != NULL, NULL); g_return_val_if_fail(func != NULL, NULL); rec = g_new0(SBAR_ITEM_REC, 1); rec->bar = bar; bar->items = g_slist_append(bar->items, rec); rec->priority = priority; rec->right_justify = right_justify; rec->func = func; return rec; } void statusbar_item_remove(SBAR_ITEM_REC *item) { g_return_if_fail(item != NULL); statusbar_item_destroy(item); if (!quitting) statusbar_redraw_all(); } static void sig_mainwindow_resized(MAIN_WINDOW_REC *window) { STATUSBAR_REC *rec; rec = window->statusbar; rec->ypos = window->first_line+window->lines; } void statusbar_init(void) { statusbars = NULL; sbars_up = sbars_down = 0; statusbar_items_init(); signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); } void statusbar_deinit(void) { statusbar_items_deinit(); while (statusbars != NULL) statusbar_destroy(statusbars->data); signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized); signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized); }