imported irssi.
[silc.git] / apps / irssi / src / fe-text / statusbar.c
diff --git a/apps/irssi/src/fe-text/statusbar.c b/apps/irssi/src/fe-text/statusbar.c
new file mode 100644 (file)
index 0000000..8e99c4f
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ 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)
+{
+        WINDOW_REC *old_active_win;
+       GSList *tmp;
+       int xpos, rxpos;
+
+       old_active_win = active_win;
+        if (bar->window != NULL)
+               active_win = bar->window->active;
+
+       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);
+               }
+       }
+
+       active_win = old_active_win;
+}
+
+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->height;
+}
+
+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);
+}