1
0
mirror of https://github.com/irssi/irssi.git synced 2024-12-04 14:46:39 -05:00
irssi/src/fe-text/statusbar.c

373 lines
8.8 KiB
C
Raw Normal View History

/*
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, screen_width-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 = screen_width-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;
}
screen_set_bg(screen_root, backs[bar->color] << 4);
screen_move(screen_root, 0, bar->ypos);
screen_clrtoeol(screen_root);
screen_set_bg(screen_root, 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 :
screen_height-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;
}
screen_set_bg(screen_root, backs[rec->color] << 4);
screen_move(screen_root, 0, rec->ypos);
screen_clrtoeol(screen_root);
screen_set_bg(screen_root, 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);
}