1 /*
2 statusbar.c : irssi
3
4 Copyright (C) 1999-2001 Timo Sirainen
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "expandos.h"
24 #include "special-vars.h"
25
26 #include "themes.h"
27
28 #include "statusbar.h"
29 #include "statusbar-config.h"
30 #include "gui-windows.h"
31 #include "gui-printtext.h"
32
33 void statusbar_items_init(void);
34 void statusbar_items_deinit(void);
35
36 GSList *statusbar_groups;
37 STATUSBAR_GROUP_REC *active_statusbar_group;
38
39 /*
40 sbar_item_defs: char *name => char *value
41 sbar_item_funcs: char *name => STATUSBAR_FUNC func
42 sbar_signal_items: int signal_id => GSList *(SBAR_ITEM_REC *items)
43 sbar_item_signals: SBAR_ITEM_REC *item => GSList *(int *signal_ids)
44 named_sbar_items: const char *name => GSList *(SBAR_ITEM_REC *items)
45 */
46 static GHashTable *sbar_item_defs, *sbar_item_funcs;
47 static GHashTable *sbar_signal_items, *sbar_item_signals;
48 static GHashTable *named_sbar_items;
49 static int statusbar_need_recreate_items;
50
statusbar_item_register(const char * name,const char * value,STATUSBAR_FUNC func)51 void statusbar_item_register(const char *name, const char *value,
52 STATUSBAR_FUNC func)
53 {
54 gpointer hkey, hvalue;
55
56 statusbar_need_recreate_items = TRUE;
57 if (value != NULL) {
58 if (g_hash_table_lookup_extended(sbar_item_defs,
59 name, &hkey, &hvalue)) {
60 g_hash_table_remove(sbar_item_defs, name);
61 g_free(hkey);
62 g_free(hvalue);
63 }
64 g_hash_table_insert(sbar_item_defs,
65 g_strdup(name), g_strdup(value));
66 }
67
68 if (func != NULL) {
69 if (g_hash_table_lookup(sbar_item_funcs, name) == NULL) {
70 g_hash_table_insert(sbar_item_funcs,
71 g_strdup(name), (void *) func);
72 }
73 }
74 }
75
statusbar_item_unregister(const char * name)76 void statusbar_item_unregister(const char *name)
77 {
78 gpointer key, value;
79
80 statusbar_need_recreate_items = TRUE;
81 if (g_hash_table_lookup_extended(sbar_item_defs,
82 name, &key, &value)) {
83 g_hash_table_remove(sbar_item_defs, key);
84 g_free(key);
85 g_free(value);
86 }
87
88 if (g_hash_table_lookup_extended(sbar_item_funcs,
89 name, &key, &value)) {
90 g_hash_table_remove(sbar_item_funcs, key);
91 g_free(key);
92 }
93 }
94
statusbar_item_set_size(struct SBAR_ITEM_REC * item,int min_size,int max_size)95 void statusbar_item_set_size(struct SBAR_ITEM_REC *item, int min_size, int max_size)
96 {
97 item->min_size = min_size;
98 item->max_size = max_size;
99 }
100
statusbar_group_create(const char * name)101 STATUSBAR_GROUP_REC *statusbar_group_create(const char *name)
102 {
103 STATUSBAR_GROUP_REC *rec;
104
105 rec = g_new0(STATUSBAR_GROUP_REC, 1);
106 rec->name = g_strdup(name);
107
108 statusbar_groups = g_slist_append(statusbar_groups, rec);
109 return rec;
110 }
111
statusbar_group_destroy(STATUSBAR_GROUP_REC * rec)112 void statusbar_group_destroy(STATUSBAR_GROUP_REC *rec)
113 {
114 statusbar_groups = g_slist_remove(statusbar_groups, rec);
115
116 while (rec->bars != NULL)
117 statusbar_destroy(rec->bars->data);
118 while (rec->config_bars != NULL)
119 statusbar_config_destroy(rec, rec->config_bars->data);
120
121 g_free(rec->name);
122 g_free(rec);
123 }
124
statusbar_group_find(const char * name)125 STATUSBAR_GROUP_REC *statusbar_group_find(const char *name)
126 {
127 GSList *tmp;
128
129 for (tmp = statusbar_groups; tmp != NULL; tmp = tmp->next) {
130 STATUSBAR_GROUP_REC *rec = tmp->data;
131
132 if (g_strcmp0(rec->name, name) == 0)
133 return rec;
134 }
135
136 return NULL;
137 }
138
sbar_item_cmp(SBAR_ITEM_REC * item1,SBAR_ITEM_REC * item2)139 static int sbar_item_cmp(SBAR_ITEM_REC *item1, SBAR_ITEM_REC *item2)
140 {
141 return item1->config->priority == item2->config->priority ? 0 :
142 item1->config->priority < item2->config->priority ? -1 : 1;
143 }
144
sbar_cmp_position(STATUSBAR_REC * bar1,STATUSBAR_REC * bar2)145 static int sbar_cmp_position(STATUSBAR_REC *bar1, STATUSBAR_REC *bar2)
146 {
147 return bar1->config->position < bar2->config->position ? -1 : 1;
148 }
149
150 /* Shink all items in statusbar to their minimum requested size.
151 The items list should be sorted by priority, highest first. */
statusbar_shrink_to_min(GSList * items,int size,int max_width)152 static int statusbar_shrink_to_min(GSList *items, int size, int max_width)
153 {
154 GSList *tmp;
155
156 for (tmp = items; tmp != NULL; tmp = tmp->next) {
157 SBAR_ITEM_REC *rec = tmp->data;
158
159 size -= (rec->max_size-rec->min_size);
160 rec->size = rec->min_size;
161
162 if (size <= max_width) {
163 rec->size += max_width-size;
164 break;
165 }
166
167 if (rec->size == 0) {
168 /* min_size was 0, item removed.
169 remove the marginal too */
170 size--;
171 }
172 }
173
174 return size;
175 }
176
177 /* shink the items in statusbar, even if their size gets smaller than
178 their minimum requested size. The items list should be sorted by
179 priority, highest first. */
statusbar_shrink_forced(GSList * items,int size,int max_width)180 static void statusbar_shrink_forced(GSList *items, int size, int max_width)
181 {
182 GSList *tmp;
183
184 for (tmp = items; tmp != NULL; tmp = tmp->next) {
185 SBAR_ITEM_REC *rec = tmp->data;
186
187 if (size-rec->size > max_width) {
188 /* remove the whole item */
189 size -= rec->size;
190 rec->size = 0;
191 } else {
192 /* shrink the item */
193 rec->size -= size-max_width;
194 break;
195 }
196 }
197 }
198
statusbar_resize_items(STATUSBAR_REC * bar,int max_width)199 static void statusbar_resize_items(STATUSBAR_REC *bar, int max_width)
200 {
201 GSList *tmp, *prior_sorted;
202 int width;
203
204 /* first give items their max. size */
205 prior_sorted = NULL;
206 width = 0;
207 for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
208 SBAR_ITEM_REC *rec = tmp->data;
209
210 rec->func(rec, TRUE);
211 rec->size = rec->max_size;
212
213 if (rec->size > 0) {
214 width += rec->max_size;
215
216 prior_sorted = g_slist_insert_sorted(prior_sorted, rec,
217 (GCompareFunc)
218 sbar_item_cmp);
219 }
220 }
221
222 if (width > max_width) {
223 /* too big, start shrinking from items with lowest priority
224 and shrink until everything fits or until we've shrinked
225 all items. */
226 width = statusbar_shrink_to_min(prior_sorted, width,
227 max_width);
228 if (width > max_width) {
229 /* still need to shrink, remove the items with lowest
230 priority until everything fits to screen */
231 statusbar_shrink_forced(prior_sorted, width,
232 max_width);
233 }
234 }
235
236 g_slist_free(prior_sorted);
237 }
238
239 #define SBAR_ITEM_REDRAW_NEEDED(_bar, _item, _xpos) \
240 (((_bar)->dirty_xpos != -1 && (_xpos) >= (_bar)->dirty_xpos) || \
241 (_item)->xpos != (_xpos) || (_item)->current_size != (_item)->size)
242
statusbar_calc_item_positions(STATUSBAR_REC * bar)243 static void statusbar_calc_item_positions(STATUSBAR_REC *bar)
244 {
245 WINDOW_REC *window;
246 WINDOW_REC *old_active_win;
247 GSList *tmp, *right_items;
248 int xpos, rxpos;
249 int max_width;
250
251 old_active_win = active_win;
252 if (bar->parent_window != NULL)
253 active_win = bar->parent_window->active;
254
255 window = bar->parent_window != NULL
256 ? bar->parent_window->active
257 : NULL;
258
259 max_width = window != NULL ? window->width : term_width;
260 statusbar_resize_items(bar, max_width);
261
262 /* left-aligned items */
263 xpos = 0;
264 for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
265 SBAR_ITEM_REC *rec = tmp->data;
266
267 if (!rec->config->right_alignment &&
268 (rec->size > 0 || rec->current_size > 0)) {
269 if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, xpos)) {
270 /* redraw the item */
271 rec->dirty = TRUE;
272 if (bar->dirty_xpos == -1 ||
273 xpos < bar->dirty_xpos) {
274 irssi_set_dirty();
275 bar->dirty = TRUE;
276 bar->dirty_xpos = xpos;
277 }
278
279 rec->xpos = xpos;
280 }
281 xpos += rec->size;
282 }
283 }
284
285 /* right-aligned items - first copy them to a new list backwards,
286 easier to draw them in right order */
287 right_items = NULL;
288 for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
289 SBAR_ITEM_REC *rec = tmp->data;
290
291 if (rec->config->right_alignment) {
292 if (rec->size > 0)
293 right_items = g_slist_prepend(right_items, rec);
294 else if (rec->current_size > 0 &&
295 (bar->dirty_xpos == -1 ||
296 rec->xpos < bar->dirty_xpos)) {
297 /* item was hidden - set the dirty position
298 to begin from the item's old xpos */
299 irssi_set_dirty();
300 bar->dirty = TRUE;
301 bar->dirty_xpos = rec->xpos;
302 }
303 }
304 }
305
306 rxpos = max_width;
307 for (tmp = right_items; tmp != NULL; tmp = tmp->next) {
308 SBAR_ITEM_REC *rec = tmp->data;
309
310 rxpos -= rec->size;
311 if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, rxpos)) {
312 rec->dirty = TRUE;
313 if (bar->dirty_xpos == -1 ||
314 rxpos < bar->dirty_xpos) {
315 irssi_set_dirty();
316 bar->dirty = TRUE;
317 bar->dirty_xpos = rxpos;
318 }
319 rec->xpos = rxpos;
320 }
321 }
322 g_slist_free(right_items);
323
324 active_win = old_active_win;
325 }
326
statusbar_redraw(STATUSBAR_REC * bar,int force)327 void statusbar_redraw(STATUSBAR_REC *bar, int force)
328 {
329 if (statusbar_need_recreate_items)
330 return; /* don't bother yet */
331
332 if (bar != NULL) {
333 if (force) {
334 irssi_set_dirty();
335 bar->dirty = TRUE;
336 bar->dirty_xpos = 0;
337 }
338 statusbar_calc_item_positions(bar);
339 } else if (active_statusbar_group != NULL) {
340 g_slist_foreach(active_statusbar_group->bars,
341 (GFunc) statusbar_redraw,
342 GINT_TO_POINTER(force));
343 }
344 }
345
statusbar_item_redraw(SBAR_ITEM_REC * item)346 void statusbar_item_redraw(SBAR_ITEM_REC *item)
347 {
348 WINDOW_REC *old_active_win;
349
350 g_return_if_fail(item != NULL);
351
352 old_active_win = active_win;
353 if (item->bar->parent_window != NULL)
354 active_win = item->bar->parent_window->active;
355
356 item->func(item, TRUE);
357
358 item->dirty = TRUE;
359 item->bar->dirty = TRUE;
360 irssi_set_dirty();
361
362 if (item->max_size != item->size) {
363 /* item wants a new size - we'll need to redraw
364 the statusbar to see if this is allowed */
365 statusbar_redraw(item->bar, item->config->right_alignment);
366 }
367
368 active_win = old_active_win;
369 }
370
statusbar_items_redraw(const char * name)371 void statusbar_items_redraw(const char *name)
372 {
373 g_slist_foreach(g_hash_table_lookup(named_sbar_items, name),
374 (GFunc) statusbar_item_redraw, NULL);
375 }
376
statusbars_recalc_ypos(STATUSBAR_REC * bar)377 static void statusbars_recalc_ypos(STATUSBAR_REC *bar)
378 {
379 GSList *tmp, *bar_group;
380 int ypos;
381
382 /* get list of statusbars with same type and placement,
383 sorted by position */
384 bar_group = NULL;
385 tmp = bar->config->type == STATUSBAR_TYPE_ROOT ? bar->group->bars :
386 bar->parent_window->statusbars;
387
388 for (; tmp != NULL; tmp = tmp->next) {
389 STATUSBAR_REC *rec = tmp->data;
390
391 if (rec->config->type == bar->config->type &&
392 rec->config->placement == bar->config->placement) {
393 bar_group = g_slist_insert_sorted(bar_group, rec,
394 (GCompareFunc)
395 sbar_cmp_position);
396 }
397 }
398
399 if (bar_group == NULL) {
400 /* we just destroyed the last statusbar in this
401 type/placement group */
402 return;
403 }
404
405 /* get the Y-position for the first statusbar */
406 if (bar->config->type == STATUSBAR_TYPE_ROOT) {
407 ypos = bar->config->placement == STATUSBAR_TOP ? 0 :
408 term_height - g_slist_length(bar_group);
409 } else {
410 ypos = bar->config->placement == STATUSBAR_TOP ?
411 bar->parent_window->first_line :
412 bar->parent_window->last_line -
413 (g_slist_length(bar_group)-1);
414 }
415
416 /* set the Y-positions */
417 while (bar_group != NULL) {
418 bar = bar_group->data;
419
420 if (bar->real_ypos != ypos) {
421 bar->real_ypos = ypos;
422 statusbar_redraw(bar, TRUE);
423 }
424
425 ypos++;
426 bar_group = g_slist_remove(bar_group, bar_group->data);
427 }
428 }
429
sig_terminal_resized(void)430 static void sig_terminal_resized(void)
431 {
432 GSList *tmp;
433
434 for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
435 STATUSBAR_REC *bar = tmp->data;
436
437 if (bar->config->type == STATUSBAR_TYPE_ROOT &&
438 bar->config->placement == STATUSBAR_BOTTOM) {
439 statusbars_recalc_ypos(bar);
440 break;
441 }
442 }
443 }
444
mainwindow_recalc_ypos(MAIN_WINDOW_REC * window,int placement)445 static void mainwindow_recalc_ypos(MAIN_WINDOW_REC *window, int placement)
446 {
447 GSList *tmp;
448
449 for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) {
450 STATUSBAR_REC *bar = tmp->data;
451
452 if (bar->config->placement == placement) {
453 statusbars_recalc_ypos(bar);
454 break;
455 }
456 }
457 }
458
sig_mainwindow_resized(MAIN_WINDOW_REC * window)459 static void sig_mainwindow_resized(MAIN_WINDOW_REC *window)
460 {
461 GSList *tmp;
462 mainwindow_recalc_ypos(window, STATUSBAR_TOP);
463 mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM);
464 for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) {
465 STATUSBAR_REC *bar = tmp->data;
466 statusbar_redraw(bar, TRUE);
467 }
468 }
469
statusbar_create(STATUSBAR_GROUP_REC * group,STATUSBAR_CONFIG_REC * config,MAIN_WINDOW_REC * parent_window)470 STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group,
471 STATUSBAR_CONFIG_REC *config,
472 MAIN_WINDOW_REC *parent_window)
473 {
474 STATUSBAR_REC *bar;
475 THEME_REC *theme;
476 GSList *tmp;
477 char *name, *value;
478
479 g_return_val_if_fail(group != NULL, NULL);
480 g_return_val_if_fail(config != NULL, NULL);
481 g_return_val_if_fail(config->type != STATUSBAR_TYPE_WINDOW ||
482 parent_window != NULL, NULL);
483
484 bar = g_new0(STATUSBAR_REC, 1);
485 group->bars = g_slist_append(group->bars, bar);
486
487 bar->group = group;
488
489 bar->config = config;
490 bar->parent_window = parent_window;
491
492 irssi_set_dirty();
493 bar->dirty = TRUE;
494 bar->dirty_xpos = 0;
495
496 signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
497 signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
498 signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
499
500 if (config->type == STATUSBAR_TYPE_ROOT) {
501 /* top/bottom of the screen */
502 mainwindows_reserve_lines(config->placement == STATUSBAR_TOP,
503 config->placement == STATUSBAR_BOTTOM);
504 theme = current_theme;
505 } else {
506 /* top/bottom of the window */
507 parent_window->statusbars =
508 g_slist_append(parent_window->statusbars, bar);
509 mainwindow_set_statusbar_lines(parent_window,
510 config->placement == STATUSBAR_TOP,
511 config->placement == STATUSBAR_BOTTOM);
512 theme = parent_window != NULL && parent_window->active != NULL &&
513 parent_window->active->theme != NULL ?
514 parent_window->active->theme : current_theme;
515 }
516
517 signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
518 signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
519 signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
520
521 /* get background color from sb_background abstract */
522 name = g_strdup_printf("{sb_%s_bg}", config->name);
523 value = theme_format_expand(theme, name);
524 g_free(name);
525
526 if (*value == '\0') {
527 /* try with the statusbar group name */
528 g_free(value);
529
530 name = g_strdup_printf("{sb_%s_bg}", group->name);
531 value = theme_format_expand(theme, name);
532 g_free(name);
533
534 if (*value == '\0') {
535 /* fallback to default statusbar background
536 (also provides backwards compatibility..) */
537 g_free(value);
538 value = theme_format_expand(theme, "{sb_background}");
539 }
540 }
541
542 if (*value == '\0') {
543 g_free(value);
544 value = g_strdup("%8");
545 }
546 bar->color = g_strconcat("%n", value, NULL);
547 g_free(value);
548
549 statusbars_recalc_ypos(bar);
550 signal_emit("statusbar created", 1, bar);
551
552 /* create the items to statusbar */
553 for (tmp = config->items; tmp != NULL; tmp = tmp->next) {
554 SBAR_ITEM_CONFIG_REC *rec = tmp->data;
555
556 statusbar_item_create(bar, rec);
557 }
558 return bar;
559 }
560
statusbar_destroy(STATUSBAR_REC * bar)561 void statusbar_destroy(STATUSBAR_REC *bar)
562 {
563 int top;
564
565 g_return_if_fail(bar != NULL);
566
567 bar->group->bars = g_slist_remove(bar->group->bars, bar);
568 if (bar->parent_window != NULL) {
569 bar->parent_window->statusbars =
570 g_slist_remove(bar->parent_window->statusbars, bar);
571 }
572
573 signal_emit("statusbar destroyed", 1, bar);
574
575 while (bar->items != NULL)
576 statusbar_item_destroy(bar->items->data);
577
578 g_free(bar->color);
579
580 if (bar->config->type != STATUSBAR_TYPE_WINDOW ||
581 bar->parent_window != NULL)
582 statusbars_recalc_ypos(bar);
583
584 top = bar->config->placement == STATUSBAR_TOP;
585 if (bar->config->type == STATUSBAR_TYPE_ROOT) {
586 /* top/bottom of the screen */
587 mainwindows_reserve_lines(top ? -1 : 0, !top ? -1 : 0);
588 } else if (bar->parent_window != NULL) {
589 /* top/bottom of the window */
590 mainwindow_set_statusbar_lines(bar->parent_window,
591 top ? -1 : 0, !top ? -1 : 0);
592 }
593
594 g_free(bar);
595 }
596
statusbar_recreate_items(STATUSBAR_REC * bar)597 void statusbar_recreate_items(STATUSBAR_REC *bar)
598 {
599 GSList *tmp;
600
601 /* destroy */
602 while (bar->items != NULL)
603 statusbar_item_destroy(bar->items->data);
604
605 /* create */
606 for (tmp = bar->config->items; tmp != NULL; tmp = tmp->next) {
607 SBAR_ITEM_CONFIG_REC *rec = tmp->data;
608
609 statusbar_item_create(bar, rec);
610 }
611
612 statusbar_redraw(bar, TRUE);
613 }
614
statusbars_recreate_items(void)615 void statusbars_recreate_items(void)
616 {
617 if (active_statusbar_group != NULL) {
618 g_slist_foreach(active_statusbar_group->bars,
619 (GFunc) statusbar_recreate_items, NULL);
620 }
621 }
622
statusbar_find(STATUSBAR_GROUP_REC * group,const char * name,MAIN_WINDOW_REC * window)623 STATUSBAR_REC *statusbar_find(STATUSBAR_GROUP_REC *group, const char *name,
624 MAIN_WINDOW_REC *window)
625 {
626 GSList *tmp;
627
628 for (tmp = group->bars; tmp != NULL; tmp = tmp->next) {
629 STATUSBAR_REC *rec = tmp->data;
630
631 if (rec->parent_window == window &&
632 g_strcmp0(rec->config->name, name) == 0)
633 return rec;
634 }
635
636 return NULL;
637 }
638
statusbar_item_get_value(SBAR_ITEM_REC * item)639 static const char *statusbar_item_get_value(SBAR_ITEM_REC *item)
640 {
641 const char *value;
642
643 value = item->config->value;
644 if (value == NULL) {
645 value = g_hash_table_lookup(sbar_item_defs,
646 item->config->name);
647 }
648
649 return value;
650 }
651
finalize_string(const char * str,const char * color)652 static GString *finalize_string(const char *str, const char *color)
653 {
654 GString *out;
655
656 out = g_string_new(color);
657
658 while (*str != '\0') {
659 if ((unsigned char) *str < 32 ||
660 (term_type == TERM_TYPE_8BIT &&
661 (unsigned char) (*str & 0x7f) < 32)) {
662 /* control char */
663 g_string_append_printf(out, "%%8%c%%8",
664 'A'-1 + (*str & 0x7f));
665 } else if (*str == '%' && str[1] == 'n') {
666 g_string_append(out, color);
667 str++;
668 } else {
669 g_string_append_c(out, *str);
670 }
671
672 str++;
673 }
674
675 return out;
676 }
677
statusbar_item_default_handler(SBAR_ITEM_REC * item,int get_size_only,const char * str,const char * data,int escape_vars)678 void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
679 const char *str, const char *data,
680 int escape_vars)
681 {
682 SERVER_REC *server;
683 WI_ITEM_REC *wiitem;
684 char *tmpstr, *tmpstr2;
685 theme_rm_col reset;
686 int len;
687 strcpy(reset.m, "n");
688
689 if (str == NULL)
690 str = statusbar_item_get_value(item);
691 if (str == NULL || *str == '\0') {
692 item->min_size = item->max_size = 0;
693 return;
694 }
695
696 if (active_win == NULL) {
697 server = NULL;
698 wiitem = NULL;
699 } else {
700 server = active_win->active_server != NULL ?
701 active_win->active_server : active_win->connect_server;
702 wiitem = active_win->active;
703 }
704
705 /* expand templates */
706 tmpstr = theme_format_expand_data(current_theme, &str,
707 reset, reset,
708 NULL, NULL,
709 EXPAND_FLAG_ROOT |
710 EXPAND_FLAG_IGNORE_REPLACES |
711 EXPAND_FLAG_IGNORE_EMPTY);
712 /* expand $variables */
713 tmpstr2 = parse_special_string(tmpstr, server, wiitem, data, NULL,
714 (escape_vars ? PARSE_FLAG_ESCAPE_VARS : 0 ));
715 g_free(tmpstr);
716
717 /* remove color codes (not %formats) */
718 tmpstr = strip_codes(tmpstr2);
719 g_free(tmpstr2);
720
721 if (get_size_only) {
722 item->min_size = item->max_size = format_get_length(tmpstr);
723 } else {
724 GString *out;
725
726 if (item->size < item->min_size) {
727 /* they're forcing us smaller than minimum size.. */
728 len = format_real_length(tmpstr, item->size);
729 tmpstr[len] = '\0';
730 }
731 out = finalize_string(tmpstr, item->bar->color);
732 /* make sure the str is big enough to fill the
733 requested size, so it won't corrupt screen */
734 len = format_get_length(tmpstr);
735 if (len < item->size) {
736 int i;
737
738 len = item->size-len;
739 for (i = 0; i < len; i++)
740 g_string_append_c(out, ' ');
741 }
742
743 gui_printtext(ITEM_WINDOW_REAL_XPOS(item), item->bar->real_ypos, out->str);
744 g_string_free(out, TRUE);
745 }
746 g_free(tmpstr);
747 }
748
statusbar_item_default_func(SBAR_ITEM_REC * item,int get_size_only)749 static void statusbar_item_default_func(SBAR_ITEM_REC *item, int get_size_only)
750 {
751 statusbar_item_default_handler(item, get_size_only, NULL, "", TRUE);
752 }
753
statusbar_update_item(void)754 static void statusbar_update_item(void)
755 {
756 GSList *items;
757
758 items = g_hash_table_lookup(sbar_signal_items,
759 GINT_TO_POINTER(signal_get_emitted_id()));
760 while (items != NULL) {
761 SBAR_ITEM_REC *item = items->data;
762
763 statusbar_item_redraw(item);
764 items = items->next;
765 }
766 }
767
statusbar_update_server(SERVER_REC * server)768 static void statusbar_update_server(SERVER_REC *server)
769 {
770 SERVER_REC *item_server;
771 GSList *items;
772
773 items = g_hash_table_lookup(sbar_signal_items,
774 GINT_TO_POINTER(signal_get_emitted_id()));
775 while (items != NULL) {
776 SBAR_ITEM_REC *item = items->data;
777
778 item_server = item->bar->parent_window != NULL ?
779 item->bar->parent_window->active->active_server :
780 active_win->active_server;
781
782 if (item_server == server)
783 statusbar_item_redraw(item);
784
785 items = items->next;
786 }
787 }
788
statusbar_update_window(WINDOW_REC * window)789 static void statusbar_update_window(WINDOW_REC *window)
790 {
791 WINDOW_REC *item_window;
792 GSList *items;
793
794 items = g_hash_table_lookup(sbar_signal_items,
795 GINT_TO_POINTER(signal_get_emitted_id()));
796 while (items != NULL) {
797 SBAR_ITEM_REC *item = items->data;
798
799 item_window = item->bar->parent_window != NULL ?
800 item->bar->parent_window->active : active_win;
801
802 if (item_window == window)
803 statusbar_item_redraw(item);
804
805 items = items->next;
806 }
807 }
808
statusbar_update_window_item(WI_ITEM_REC * wiitem)809 static void statusbar_update_window_item(WI_ITEM_REC *wiitem)
810 {
811 WI_ITEM_REC *item_wi;
812 GSList *items;
813
814 items = g_hash_table_lookup(sbar_signal_items,
815 GINT_TO_POINTER(signal_get_emitted_id()));
816 while (items != NULL) {
817 SBAR_ITEM_REC *item = items->data;
818
819 item_wi = item->bar->parent_window != NULL ?
820 item->bar->parent_window->active->active :
821 active_win->active;
822
823 if (item_wi == wiitem)
824 statusbar_item_redraw(item);
825
826 items = items->next;
827 }
828 }
829
statusbar_item_default_signals(SBAR_ITEM_REC * item)830 static void statusbar_item_default_signals(SBAR_ITEM_REC *item)
831 {
832 SIGNAL_FUNC func;
833 GSList *list;
834 const char *value;
835 void *signal_id;
836 int *signals, *pos;
837
838 value = statusbar_item_get_value(item);
839 if (value == NULL)
840 return;
841
842 signals = special_vars_get_signals(value);
843 if (signals == NULL)
844 return;
845
846 for (pos = signals; *pos != -1; pos += 2) {
847 /* update signal -> item mappings */
848 signal_id = GINT_TO_POINTER(*pos);
849 list = g_hash_table_lookup(sbar_signal_items, signal_id);
850 if (list == NULL) {
851 switch (pos[1]) {
852 case EXPANDO_ARG_NONE:
853 func = (SIGNAL_FUNC) statusbar_update_item;
854 break;
855 case EXPANDO_ARG_SERVER:
856 func = (SIGNAL_FUNC) statusbar_update_server;
857 break;
858 case EXPANDO_ARG_WINDOW:
859 func = (SIGNAL_FUNC) statusbar_update_window;
860 break;
861 case EXPANDO_ARG_WINDOW_ITEM:
862 func = (SIGNAL_FUNC) statusbar_update_window_item;
863 break;
864 default:
865 func = NULL;
866 break;
867 }
868 if (func != NULL) {
869 signal_add_full_id(MODULE_NAME,
870 SIGNAL_PRIORITY_DEFAULT,
871 *pos, func, NULL);
872 }
873 }
874
875 if (g_slist_find(list, item) == NULL)
876 list = g_slist_append(list, item);
877 g_hash_table_insert(sbar_signal_items, signal_id, list);
878
879 /* update item -> signal mappings */
880 list = g_hash_table_lookup(sbar_item_signals, item);
881 if (g_slist_find(list, signal_id) == NULL)
882 list = g_slist_append(list, signal_id);
883 g_hash_table_insert(sbar_item_signals, item, list);
884 }
885 g_free(signals);
886 }
887
statusbar_item_create(STATUSBAR_REC * bar,SBAR_ITEM_CONFIG_REC * config)888 SBAR_ITEM_REC *statusbar_item_create(STATUSBAR_REC *bar,
889 SBAR_ITEM_CONFIG_REC *config)
890 {
891 SBAR_ITEM_REC *rec;
892 GSList *items;
893
894 g_return_val_if_fail(bar != NULL, NULL);
895 g_return_val_if_fail(config != NULL, NULL);
896
897 rec = g_new0(SBAR_ITEM_REC, 1);
898 bar->items = g_slist_append(bar->items, rec);
899
900 rec->bar = bar;
901 rec->config = config;
902
903 rec->func = (STATUSBAR_FUNC) g_hash_table_lookup(sbar_item_funcs,
904 config->name);
905 if (rec->func == NULL)
906 rec->func = statusbar_item_default_func;
907 statusbar_item_default_signals(rec);
908
909 items = g_hash_table_lookup(named_sbar_items, config->name);
910 items = g_slist_append(items, rec);
911 g_hash_table_insert(named_sbar_items, config->name, items);
912
913 irssi_set_dirty();
914 rec->dirty = TRUE;
915 bar->dirty = TRUE;
916
917 signal_emit("statusbar item created", 1, rec);
918 return rec;
919 }
920
statusbar_signal_remove(int signal_id)921 static void statusbar_signal_remove(int signal_id)
922 {
923 signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_item, NULL);
924 signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_server, NULL);
925 signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window, NULL);
926 signal_remove_id(signal_id, (SIGNAL_FUNC) statusbar_update_window_item, NULL);
927 }
928
statusbar_item_remove_signal(SBAR_ITEM_REC * item,int signal_id)929 static void statusbar_item_remove_signal(SBAR_ITEM_REC *item, int signal_id)
930 {
931 GSList *list;
932
933 /* update signal -> item hash */
934 list = g_hash_table_lookup(sbar_signal_items,
935 GINT_TO_POINTER(signal_id));
936 list = g_slist_remove(list, item);
937 if (list != NULL) {
938 g_hash_table_insert(sbar_signal_items,
939 GINT_TO_POINTER(signal_id), list);
940 } else {
941 g_hash_table_remove(sbar_signal_items,
942 GINT_TO_POINTER(signal_id));
943 statusbar_signal_remove(signal_id);
944 }
945 }
946
statusbar_item_destroy(SBAR_ITEM_REC * item)947 void statusbar_item_destroy(SBAR_ITEM_REC *item)
948 {
949 GSList *list;
950
951 g_return_if_fail(item != NULL);
952
953 item->bar->items = g_slist_remove(item->bar->items, item);
954
955 list = g_hash_table_lookup(named_sbar_items, item->config->name);
956 list = g_slist_remove(list, item);
957 if (list == NULL)
958 g_hash_table_remove(named_sbar_items, item->config->name);
959 else
960 g_hash_table_insert(named_sbar_items, item->config->name, list);
961
962 signal_emit("statusbar item destroyed", 1, item);
963
964 list = g_hash_table_lookup(sbar_item_signals, item);
965 g_hash_table_remove(sbar_item_signals, item);
966
967 while (list != NULL) {
968 statusbar_item_remove_signal(item, GPOINTER_TO_INT(list->data));
969 list = g_slist_remove(list, list->data);
970 }
971
972 g_free(item);
973 }
974
set_border_info(STATUSBAR_REC * bar)975 static MAIN_WINDOW_BORDER_REC *set_border_info(STATUSBAR_REC *bar)
976 {
977 MAIN_WINDOW_BORDER_REC *orig_border, *new_border;
978 orig_border = clrtoeol_info;
979 new_border = g_new0(MAIN_WINDOW_BORDER_REC, 1);
980 new_border->window = bar->parent_window != NULL ? bar->parent_window->screen_win : NULL;
981 new_border->color = bar->color;
982 clrtoeol_info = new_border;
983 return orig_border;
984 }
985
restore_border_info(MAIN_WINDOW_BORDER_REC * border_info)986 static void restore_border_info(MAIN_WINDOW_BORDER_REC *border_info)
987 {
988 MAIN_WINDOW_BORDER_REC *old_border;
989 old_border = clrtoeol_info;
990 clrtoeol_info = border_info;
991 g_free(old_border);
992 }
993
statusbar_redraw_needed_items(STATUSBAR_REC * bar)994 static void statusbar_redraw_needed_items(STATUSBAR_REC *bar)
995 {
996 WINDOW_REC *old_active_win;
997 GSList *tmp;
998 char *str;
999
1000 old_active_win = active_win;
1001 if (bar->parent_window != NULL)
1002 active_win = bar->parent_window->active;
1003
1004 if (bar->dirty_xpos >= 0) {
1005 MAIN_WINDOW_BORDER_REC *orig_border;
1006 orig_border = set_border_info(bar);
1007 str = g_strconcat(bar->color, "%>", NULL);
1008 gui_printtext(BAR_WINDOW_REAL_DIRTY_XPOS(bar), bar->real_ypos, str);
1009 g_free(str);
1010 restore_border_info(orig_border);
1011 }
1012
1013 for (tmp = bar->items; tmp != NULL; tmp = tmp->next) {
1014 SBAR_ITEM_REC *rec = tmp->data;
1015
1016 if (rec->dirty ||
1017 (bar->dirty_xpos != -1 &&
1018 rec->xpos >= bar->dirty_xpos)) {
1019 rec->current_size = rec->size;
1020 rec->func(rec, FALSE);
1021 rec->dirty = FALSE;
1022 }
1023 }
1024
1025 active_win = old_active_win;
1026 }
1027
statusbar_redraw_dirty(void)1028 void statusbar_redraw_dirty(void)
1029 {
1030 GSList *tmp;
1031
1032 if (statusbar_need_recreate_items) {
1033 statusbar_need_recreate_items = FALSE;
1034 statusbars_recreate_items();
1035 }
1036
1037 for (tmp = active_statusbar_group->bars; tmp != NULL; tmp = tmp->next) {
1038 STATUSBAR_REC *rec = tmp->data;
1039
1040 if (rec->dirty) {
1041 statusbar_redraw_needed_items(rec);
1042 rec->dirty = FALSE;
1043 rec->dirty_xpos = -1;
1044 }
1045 }
1046 }
1047
1048 #define STATUSBAR_IS_VISIBLE(bar, window) \
1049 ((bar)->visible == STATUSBAR_VISIBLE_ALWAYS || \
1050 (active_mainwin == (window) && \
1051 (bar)->visible == STATUSBAR_VISIBLE_ACTIVE) || \
1052 (active_mainwin != (window) && \
1053 (bar)->visible == STATUSBAR_VISIBLE_INACTIVE))
1054
statusbars_remove_unvisible(MAIN_WINDOW_REC * window)1055 static void statusbars_remove_unvisible(MAIN_WINDOW_REC *window)
1056 {
1057 GSList *tmp, *next;
1058
1059 for (tmp = window->statusbars; tmp != NULL; tmp = next) {
1060 STATUSBAR_REC *bar = tmp->data;
1061
1062 next = tmp->next;
1063 if (!STATUSBAR_IS_VISIBLE(bar->config, window))
1064 statusbar_destroy(bar);
1065 }
1066 }
1067
statusbars_add_visible(MAIN_WINDOW_REC * window)1068 static void statusbars_add_visible(MAIN_WINDOW_REC *window)
1069 {
1070 STATUSBAR_GROUP_REC *group;
1071 STATUSBAR_REC *bar;
1072 GSList *tmp;
1073
1074 group = active_statusbar_group;
1075 for (tmp = group->config_bars; tmp != NULL; tmp = tmp->next) {
1076 STATUSBAR_CONFIG_REC *config = tmp->data;
1077
1078 if (config->type == STATUSBAR_TYPE_WINDOW &&
1079 STATUSBAR_IS_VISIBLE(config, window) &&
1080 statusbar_find(group, config->name, window) == NULL) {
1081 bar = statusbar_create(group, config, window);
1082 statusbar_redraw(bar, TRUE);
1083 }
1084 }
1085 }
1086
sig_mainwindow_destroyed(MAIN_WINDOW_REC * window)1087 static void sig_mainwindow_destroyed(MAIN_WINDOW_REC *window)
1088 {
1089 while (window->statusbars != NULL) {
1090 STATUSBAR_REC *bar = window->statusbars->data;
1091
1092 bar->parent_window->statusbars =
1093 g_slist_remove(bar->parent_window->statusbars, bar);
1094 bar->parent_window = NULL;
1095 statusbar_destroy(bar);
1096 }
1097 }
1098
sig_window_changed(void)1099 static void sig_window_changed(void)
1100 {
1101 GSList *tmp;
1102
1103 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
1104 MAIN_WINDOW_REC *rec = tmp->data;
1105
1106 statusbars_remove_unvisible(rec);
1107 statusbars_add_visible(rec);
1108 }
1109 }
1110
sig_gui_window_created(WINDOW_REC * window)1111 static void sig_gui_window_created(WINDOW_REC *window)
1112 {
1113 statusbars_add_visible(WINDOW_MAIN(window));
1114 }
1115
statusbar_item_def_destroy(void * key,void * value)1116 static void statusbar_item_def_destroy(void *key, void *value)
1117 {
1118 g_free(key);
1119 g_free(value);
1120 }
1121
statusbar_signal_item_destroy(void * key,GSList * value)1122 static void statusbar_signal_item_destroy(void *key, GSList *value)
1123 {
1124 while (value != NULL) {
1125 statusbar_signal_remove(GPOINTER_TO_INT(value->data));
1126 value->data = g_slist_remove(value, value->data);
1127 }
1128 }
1129
statusbar_item_signal_destroy(void * key,GSList * value)1130 static void statusbar_item_signal_destroy(void *key, GSList *value)
1131 {
1132 g_slist_free(value);
1133 }
1134
statusbars_create_window_bars(void)1135 void statusbars_create_window_bars(void)
1136 {
1137 g_slist_foreach(mainwindows, (GFunc) statusbars_add_visible, NULL);
1138 }
1139
statusbar_init(void)1140 void statusbar_init(void)
1141 {
1142 statusbar_need_recreate_items = FALSE;
1143 statusbar_groups = NULL;
1144 active_statusbar_group = NULL;
1145 sbar_item_defs = g_hash_table_new((GHashFunc) g_str_hash,
1146 (GCompareFunc) g_str_equal);
1147 sbar_item_funcs = g_hash_table_new((GHashFunc) g_str_hash,
1148 (GCompareFunc) g_str_equal);
1149 sbar_signal_items = g_hash_table_new((GHashFunc) g_direct_hash,
1150 (GCompareFunc) g_direct_equal);
1151 sbar_item_signals = g_hash_table_new((GHashFunc) g_direct_hash,
1152 (GCompareFunc) g_direct_equal);
1153 named_sbar_items = g_hash_table_new((GHashFunc) g_str_hash,
1154 (GCompareFunc) g_str_equal);
1155
1156 signal_add("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
1157 signal_add("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
1158 signal_add("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
1159 signal_add("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
1160 signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
1161 signal_add("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
1162
1163 statusbar_items_init();
1164 statusbar_config_init(); /* signals need to be before this call */
1165 }
1166
statusbar_deinit(void)1167 void statusbar_deinit(void)
1168 {
1169 while (statusbar_groups != NULL)
1170 statusbar_group_destroy(statusbar_groups->data);
1171
1172 g_hash_table_foreach(sbar_item_defs,
1173 (GHFunc) statusbar_item_def_destroy, NULL);
1174 g_hash_table_destroy(sbar_item_defs);
1175
1176 g_hash_table_foreach(sbar_item_funcs, (GHFunc) g_free, NULL);
1177 g_hash_table_destroy(sbar_item_funcs);
1178
1179 g_hash_table_foreach(sbar_signal_items,
1180 (GHFunc) statusbar_signal_item_destroy, NULL);
1181 g_hash_table_destroy(sbar_signal_items);
1182 g_hash_table_foreach(sbar_item_signals,
1183 (GHFunc) statusbar_item_signal_destroy, NULL);
1184 g_hash_table_destroy(sbar_item_signals);
1185 g_hash_table_destroy(named_sbar_items);
1186
1187 signal_remove("terminal resized", (SIGNAL_FUNC) sig_terminal_resized);
1188 signal_remove("mainwindow resized", (SIGNAL_FUNC) sig_mainwindow_resized);
1189 signal_remove("mainwindow moved", (SIGNAL_FUNC) sig_mainwindow_resized);
1190 signal_remove("gui window created", (SIGNAL_FUNC) sig_gui_window_created);
1191 signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
1192 signal_remove("mainwindow destroyed", (SIGNAL_FUNC) sig_mainwindow_destroyed);
1193
1194 statusbar_items_deinit();
1195 statusbar_config_deinit();
1196 }
1197