4 Copyright (C) 1999-2000 Timo Sirainen
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.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "module-formats.h"
28 #include "printtext.h"
31 #include "statusbar.h"
32 #include "gui-windows.h"
34 #define NEW_WINDOW_SIZE (WINDOW_MIN_SIZE + 1)
37 MAIN_WINDOW_REC *active_mainwin;
39 static int reserved_up, reserved_down;
40 static int screen_width, screen_height;
42 static MAIN_WINDOW_REC *find_window_with_room(void)
44 MAIN_WINDOW_REC *biggest_rec;
48 biggest = 0; biggest_rec = NULL;
49 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
50 MAIN_WINDOW_REC *rec = tmp->data;
53 if (space >= WINDOW_MIN_SIZE+NEW_WINDOW_SIZE && space > biggest) {
62 #ifdef USE_CURSES_WINDOWS
63 static void create_curses_window(MAIN_WINDOW_REC *window)
65 window->curses_win = newwin(window->height, window->width,
66 window->first_line, 0);
67 idlok(window->curses_win, 1);
71 static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff)
75 if (xdiff == 0 && ydiff == 0)
78 window->width += xdiff;
79 window->height = window->last_line-window->first_line+1;
80 #ifdef USE_CURSES_WINDOWS
81 #ifdef HAVE_CURSES_WRESIZE
82 wresize(window->curses_win, window->height, window->width);
83 mvwin(window->curses_win, window->first_line, 0);
85 delwin(window->curses_win);
86 create_curses_window(window);
90 for (tmp = windows; tmp != NULL; tmp = tmp->next) {
91 WINDOW_REC *rec = tmp->data;
93 if (rec->gui_data != NULL &&
94 WINDOW_GUI(rec)->parent == window)
95 gui_window_resize(rec, window->width, window->height);
98 textbuffer_view_set_window(WINDOW_GUI(window->active)->view,
100 signal_emit("mainwindow resized", 1, window);
103 void mainwindows_recreate(void)
107 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
108 MAIN_WINDOW_REC *rec = tmp->data;
110 #ifdef USE_CURSES_WINDOWS
111 create_curses_window(rec);
113 textbuffer_view_set_window(WINDOW_GUI(rec->active)->view,
118 MAIN_WINDOW_REC *mainwindow_create(void)
120 MAIN_WINDOW_REC *rec, *parent;
123 rec = g_new0(MAIN_WINDOW_REC, 1);
124 rec->width = screen_width;
125 rec->statusbar_lines = 1;
127 if (mainwindows == NULL) {
128 active_mainwin = rec;
130 rec->first_line = reserved_up;
131 rec->last_line = screen_height-1 -
132 reserved_down-rec->statusbar_lines;
133 rec->height = rec->last_line-rec->first_line+1;
135 parent = WINDOW_GUI(active_win)->parent;
136 if (parent->height < WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
137 parent = find_window_with_room();
139 return NULL; /* not enough space */
141 space = (parent->height-parent->statusbar_lines)/2;
142 rec->first_line = parent->first_line;
143 rec->last_line = rec->first_line + space-rec->statusbar_lines;
144 rec->height = rec->last_line-rec->first_line+1;
145 parent->first_line = rec->last_line+1+rec->statusbar_lines;
146 parent->height = parent->last_line-parent->first_line+1;
148 mainwindow_resize(parent, 0, -space-1);
151 #ifdef USE_CURSES_WINDOWS
152 rec->curses_win = newwin(rec->height, rec->width, rec->first_line, 0);
156 mainwindows = g_slist_append(mainwindows, rec);
157 signal_emit("mainwindow created", 1, rec);
161 static MAIN_WINDOW_REC *mainwindows_find_lower(int line)
163 MAIN_WINDOW_REC *best;
167 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
168 MAIN_WINDOW_REC *rec = tmp->data;
170 if (rec->first_line > line &&
171 (best == NULL || rec->first_line < best->first_line))
178 static MAIN_WINDOW_REC *mainwindows_find_upper(int line)
180 MAIN_WINDOW_REC *best;
184 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
185 MAIN_WINDOW_REC *rec = tmp->data;
187 if (rec->last_line < line &&
188 (best == NULL || rec->last_line > best->last_line))
195 static void mainwindows_add_space(int first_line, int last_line)
197 MAIN_WINDOW_REC *rec;
200 if (last_line < first_line)
203 size = last_line-first_line+1;
205 rec = mainwindows_find_lower(last_line);
207 rec->first_line = first_line;
208 mainwindow_resize(rec, 0, size);
212 rec = mainwindows_find_upper(first_line);
214 rec->last_line = last_line-rec->statusbar_lines;
215 mainwindow_resize(rec, 0, size);
219 static void gui_windows_remove_parent(MAIN_WINDOW_REC *window)
221 MAIN_WINDOW_REC *new_parent;
224 new_parent = mainwindows->data;
225 for (tmp = windows; tmp != NULL; tmp = tmp->next) {
226 WINDOW_REC *rec = tmp->data;
228 if (rec->gui_data != NULL && WINDOW_GUI(rec)->parent == window)
229 gui_window_reparent(rec, new_parent);
233 void mainwindow_destroy(MAIN_WINDOW_REC *window)
235 g_return_if_fail(window != NULL);
237 #ifdef USE_CURSES_WINDOWS
238 delwin(window->curses_win);
241 mainwindows = g_slist_remove(mainwindows, window);
242 signal_emit("mainwindow destroyed", 1, window);
244 if (!quitting && mainwindows != NULL) {
245 gui_windows_remove_parent(window);
246 mainwindows_add_space(window->first_line, window->last_line+window->statusbar_lines);
248 mainwindows_redraw();
249 statusbar_redraw(NULL);
253 if (active_mainwin == window) active_mainwin = NULL;
256 void mainwindows_redraw(void)
260 screen_refresh_freeze();
261 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
262 MAIN_WINDOW_REC *rec = tmp->data;
264 gui_window_redraw(rec->active);
266 screen_refresh_thaw();
269 static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2)
271 return w1->first_line < w2->first_line ? -1 : 1;
274 static int mainwindows_compare_reverse(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2)
276 return w1->first_line < w2->first_line ? 1 : -1;
279 GSList *mainwindows_get_sorted(int reverse)
284 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
285 list = g_slist_insert_sorted(list, tmp->data, (GCompareFunc)
286 (reverse ? mainwindows_compare_reverse : mainwindows_compare));
292 static void mainwindows_resize_too_small(int xdiff, int ydiff)
294 GSList *sorted, *tmp;
297 /* terminal is too small - just take the space whereever possible */
298 sorted = mainwindows_get_sorted(FALSE);
300 for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
301 MAIN_WINDOW_REC *rec = tmp->data;
304 if (ydiff == 0 || space <= 0) {
306 rec->first_line -= moved;
307 rec->last_line -= moved;
308 signal_emit("mainwindow moved", 1, rec);
313 if (space > -ydiff) space = -ydiff;
315 rec->first_line -= moved;
317 rec->last_line -= space;
318 mainwindow_resize(rec, xdiff, -space);
320 g_slist_free(sorted);
323 static void mainwindows_resize_smaller(int xdiff, int ydiff)
325 GSList *sorted, *tmp;
329 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
330 MAIN_WINDOW_REC *rec = tmp->data;
332 space += rec->height-WINDOW_MIN_SIZE;
335 if (space < -ydiff) {
336 /* not enough space, use different algorithm */
337 mainwindows_resize_too_small(xdiff, ydiff);
341 /* resize windows that have space */
342 sorted = mainwindows_get_sorted(TRUE);
343 for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) {
344 MAIN_WINDOW_REC *rec = tmp->data;
346 space = rec->height-WINDOW_MIN_SIZE;
348 rec->first_line += ydiff;
349 rec->last_line += ydiff;
350 signal_emit("mainwindow moved", 1, rec);
354 if (space <= 0) space = 1;
355 if (space > -ydiff) space = -ydiff;
356 rec->last_line += ydiff;
358 rec->first_line += ydiff;
360 mainwindow_resize(rec, xdiff, -space);
362 g_slist_free(sorted);
365 static void mainwindows_resize_bigger(int xdiff, int ydiff)
367 GSList *sorted, *tmp;
370 sorted = mainwindows_get_sorted(FALSE);
372 for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
373 MAIN_WINDOW_REC *rec = tmp->data;
375 space = rec->height-WINDOW_MIN_SIZE;
376 if (ydiff == 0 || (space >= 0 && tmp->next != NULL)) {
378 rec->first_line += moved;
379 rec->last_line += moved;
380 signal_emit("mainwindow moved", 1, rec);
385 if (space < 0 && tmp->next != NULL) {
386 /* space below minimum */
388 if (space > ydiff) space = ydiff;
390 /* lowest window - give all the extra space for it */
394 rec->first_line += moved;
396 rec->last_line += moved;
398 mainwindow_resize(rec, xdiff, space);
400 g_slist_free(sorted);
403 void mainwindows_resize_horiz(int xdiff)
407 for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) {
408 MAIN_WINDOW_REC *rec = tmp->data;
410 mainwindow_resize(rec, xdiff, 0);
414 void mainwindows_resize(int width, int height)
418 xdiff = width-screen_width;
419 ydiff = height-screen_height;
420 screen_width = width;
421 screen_height = height;
423 screen_refresh_freeze();
425 mainwindows_resize_smaller(xdiff, ydiff);
427 mainwindows_resize_bigger(xdiff, ydiff);
429 mainwindows_resize_horiz(xdiff);
432 screen_refresh_thaw();
435 int mainwindows_reserve_lines(int count, int up)
437 MAIN_WINDOW_REC *window;
441 g_return_val_if_fail(count > 0 || reserved_up > count, -1);
444 reserved_up += count;
446 window = mainwindows_find_lower(-1);
447 if (window != NULL) window->first_line += count;
449 g_return_val_if_fail(count > 0 || reserved_down > count, -1);
452 reserved_down += count;
454 window = mainwindows_find_upper(screen_height);
455 if (window != NULL) window->last_line -= count;
459 mainwindow_resize(window, 0, -count);
464 static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win,
465 MAIN_WINDOW_REC *shrink_win, int count)
467 mainwindow_resize(grow_win, 0, count);
468 mainwindow_resize(shrink_win, 0, -count);
469 gui_window_redraw(grow_win->active);
470 gui_window_redraw(shrink_win->active);
471 statusbar_redraw(grow_win->statusbar);
472 statusbar_redraw(shrink_win->statusbar);
475 static int mainwindow_grow(MAIN_WINDOW_REC *window, int count)
477 MAIN_WINDOW_REC *shrink_win;
479 /* shrink lower window */
480 shrink_win = mainwindows_find_lower(window->last_line);
481 if (shrink_win != NULL && shrink_win->height-count >= WINDOW_MIN_SIZE) {
482 window->last_line += count;
483 shrink_win->first_line += count;
485 /* shrink upper window */
486 shrink_win = mainwindows_find_upper(window->first_line);
487 if (shrink_win == NULL ||
488 shrink_win->height-count < WINDOW_MIN_SIZE)
491 window->first_line -= count;
492 shrink_win->last_line -= count;
495 mainwindows_resize_two(window, shrink_win, count);
499 static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count)
501 MAIN_WINDOW_REC *grow_win;
503 if (window->height-count < WINDOW_MIN_SIZE)
506 grow_win = mainwindows_find_lower(window->last_line);
507 if (grow_win != NULL) {
508 window->last_line -= count;
509 grow_win->first_line -= count;
511 grow_win = mainwindows_find_upper(window->first_line);
512 if (grow_win == NULL) return FALSE;
514 window->first_line += count;
515 grow_win->last_line += count;
518 mainwindows_resize_two(grow_win, window, count);
522 void mainwindow_set_size(MAIN_WINDOW_REC *window, int size)
524 size -= window->height;
526 mainwindow_shrink(window, size);
528 mainwindow_grow(window, size);
531 /* SYNTAX: WINDOW GROW [<lines>] */
532 static void cmd_window_grow(const char *data)
534 MAIN_WINDOW_REC *window;
537 count = *data == '\0' ? 1 : atoi(data);
538 window = WINDOW_GUI(active_win)->parent;
540 if (!mainwindow_grow(window, count)) {
541 printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
542 TXT_WINDOW_TOO_SMALL);
546 /* SYNTAX: WINDOW SHRINK [<lines>] */
547 static void cmd_window_shrink(const char *data)
549 MAIN_WINDOW_REC *window;
552 count = *data == '\0' ? 1 : atoi(data);
553 window = WINDOW_GUI(active_win)->parent;
555 if (!mainwindow_shrink(window, count)) {
556 printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
557 TXT_WINDOW_TOO_SMALL);
561 /* SYNTAX: WINDOW SIZE <lines> */
562 static void cmd_window_size(const char *data)
564 char sizestr[MAX_INT_STRLEN];
567 if (!is_numeric(data, 0)) return;
570 size -= WINDOW_GUI(active_win)->parent->height;
571 if (size == 0) return;
573 ltoa(sizestr, size < 0 ? -size : size);
575 cmd_window_shrink(sizestr);
577 cmd_window_grow(sizestr);
580 /* SYNTAX: WINDOW BALANCE */
581 static void cmd_window_balance(void)
583 GSList *sorted, *tmp;
584 int avail_size, unit_size, bigger_units;
585 int windows, last_line, old_size;
587 windows = g_slist_length(mainwindows);
588 if (windows == 1) return;
590 avail_size = screen_height - reserved_up-reserved_down;
591 unit_size = avail_size/windows;
592 bigger_units = avail_size%windows;
594 sorted = mainwindows_get_sorted(FALSE);
596 for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
597 MAIN_WINDOW_REC *rec = tmp->data;
599 old_size = rec->height;
600 rec->first_line = last_line+1;
601 rec->last_line = rec->first_line-1 + unit_size -
602 rec->statusbar_lines;
603 rec->height = rec->last_line-rec->first_line+1;
605 if (bigger_units > 0) {
609 last_line = rec->last_line + rec->statusbar_lines;
611 mainwindow_resize(rec, 0, rec->height-old_size);
613 g_slist_free(sorted);
615 mainwindows_redraw();
616 statusbar_redraw(NULL);
619 /* SYNTAX: WINDOW HIDE [<number>|<name>] */
620 static void cmd_window_hide(const char *data)
624 if (mainwindows->next == NULL) {
625 printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
632 else if (is_numeric(data, 0)) {
633 window = window_find_refnum(atoi(data));
634 if (window == NULL) {
635 printformat_window(active_win, MSGLEVEL_CLIENTERROR,
636 TXT_REFNUM_NOT_FOUND, data);
639 window = window_find_item(active_win->active_server, data);
642 if (window == NULL || !is_window_visible(window))
645 if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
646 printformat_window(active_win, MSGLEVEL_CLIENTERROR,
647 TXT_CANT_HIDE_STICKY_WINDOWS);
651 mainwindow_destroy(WINDOW_GUI(window)->parent);
653 if (active_mainwin == NULL) {
654 active_mainwin = WINDOW_GUI(active_win)->parent;
655 window_set_active(active_mainwin->active);
659 /* SYNTAX: WINDOW SHOW <number>|<name> */
660 static void cmd_window_show(const char *data)
662 MAIN_WINDOW_REC *parent;
665 if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
667 if (is_numeric(data, '\0')) {
668 window = window_find_refnum(atoi(data));
669 if (window == NULL) {
670 printformat_window(active_win, MSGLEVEL_CLIENTERROR,
671 TXT_REFNUM_NOT_FOUND, data);
674 window = window_find_item(active_win->active_server, data);
677 if (window == NULL || is_window_visible(window))
680 if (WINDOW_GUI(window)->parent->sticky_windows != NULL) {
681 printformat_window(active_win, MSGLEVEL_CLIENTERROR,
682 TXT_CANT_SHOW_STICKY_WINDOWS);
686 parent = mainwindow_create();
687 if (settings_get_bool("autostick_split_windows")) {
688 parent->sticky_windows =
689 g_slist_append(parent->sticky_windows, window);
691 parent->active = window;
692 gui_window_reparent(window, parent);
694 active_mainwin = NULL;
695 window_set_active(window);
698 /* SYNTAX: WINDOW UP */
699 static void cmd_window_up(void)
701 MAIN_WINDOW_REC *rec;
703 rec = mainwindows_find_upper(active_mainwin->first_line);
705 window_set_active(rec->active);
708 /* SYNTAX: WINDOW DOWN */
709 static void cmd_window_down(void)
711 MAIN_WINDOW_REC *rec;
713 rec = mainwindows_find_lower(active_mainwin->last_line);
715 window_set_active(rec->active);
718 /* SYNTAX: WINDOW LEFT */
719 static void cmd_window_left(const char *data, SERVER_REC *server, void *item)
721 MAIN_WINDOW_REC *parent;
726 if (active_mainwin->sticky_windows == NULL) {
727 /* no sticky windows, go to previous non-sticky window */
728 num = active_win->refnum;
730 num = window_refnum_prev(num, TRUE);
735 window = window_find_refnum(num);
736 parent = WINDOW_GUI(window)->parent;
737 } while (g_slist_find(parent->sticky_windows, window) != NULL);
739 pos = g_slist_index(active_mainwin->sticky_windows,
742 window = g_slist_nth_data(
743 active_mainwin->sticky_windows, pos-1);
745 window = g_slist_last(
746 active_mainwin->sticky_windows)->data;
751 window_set_active(window);
754 /* SYNTAX: WINDOW RIGHT */
755 static void cmd_window_right(void)
757 MAIN_WINDOW_REC *parent;
763 if (active_mainwin->sticky_windows == NULL) {
764 /* no sticky windows, go to next non-sticky window */
765 num = active_win->refnum;
767 num = window_refnum_next(num, TRUE);
772 window = window_find_refnum(num);
773 parent = WINDOW_GUI(window)->parent;
774 } while (g_slist_find(parent->sticky_windows, window) != NULL);
776 tmp = g_slist_find(active_mainwin->sticky_windows, active_win);
778 window = tmp->next != NULL ? tmp->next->data :
779 active_mainwin->sticky_windows->data;
784 window_set_active(window);
787 static void mainwindow_change_window(MAIN_WINDOW_REC *mainwin,
790 MAIN_WINDOW_REC *parent;
793 if (mainwin->sticky_windows != NULL) {
795 window_set_active(mainwin->sticky_windows->data);
799 for (tmp = windows; tmp != NULL; tmp = tmp->next) {
800 WINDOW_REC *rec = tmp->data;
802 parent = WINDOW_GUI(rec)->parent;
804 g_slist_find(parent->sticky_windows, rec) == NULL) {
805 window_set_active(rec);
810 /* no more non-sticky windows, remove main window */
811 mainwindow_destroy(mainwin);
814 /* SYNTAX: WINDOW STICK [ON|OFF|<ref#>] */
815 static void cmd_window_stick(const char *data)
817 MAIN_WINDOW_REC *window = active_mainwin;
819 if (is_numeric(data, '\0')) {
820 WINDOW_REC *win = window_find_refnum(atoi(data));
822 printformat_window(active_win, MSGLEVEL_CLIENTERROR,
823 TXT_REFNUM_NOT_FOUND, data);
826 window = WINDOW_GUI(win)->parent;
829 if (g_strncasecmp(data, "OF", 2) == 0 || toupper(*data) == 'N') {
831 if (g_slist_find(window->sticky_windows, active_win) == NULL) {
832 printformat_window(active_win, MSGLEVEL_CLIENTERROR,
833 TXT_WINDOW_NOT_STICKY);
835 window->sticky_windows =
836 g_slist_remove(window->sticky_windows,
838 printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
839 TXT_WINDOW_UNSET_STICKY);
843 active_mainwin->sticky_windows =
844 g_slist_remove(active_mainwin->sticky_windows,
847 if (g_slist_find(window->sticky_windows, active_win) == NULL) {
848 window->sticky_windows =
849 g_slist_append(window->sticky_windows,
852 if (window != active_mainwin) {
855 movewin = active_win;
856 gui_window_reparent(movewin, window);
857 mainwindow_change_window(active_mainwin, movewin);
859 active_mainwin = window;
860 window_set_active(movewin);
863 printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
864 TXT_WINDOW_SET_STICKY);
868 void mainwindows_init(void)
871 screen_height = LINES;
874 active_mainwin = NULL;
875 reserved_up = reserved_down = 0;
878 mainwindows_reserve_lines(1, FALSE);
880 command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow);
881 command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink);
882 command_bind("window size", NULL, (SIGNAL_FUNC) cmd_window_size);
883 command_bind("window balance", NULL, (SIGNAL_FUNC) cmd_window_balance);
884 command_bind("window hide", NULL, (SIGNAL_FUNC) cmd_window_hide);
885 command_bind("window show", NULL, (SIGNAL_FUNC) cmd_window_show);
886 command_bind("window up", NULL, (SIGNAL_FUNC) cmd_window_up);
887 command_bind("window down", NULL, (SIGNAL_FUNC) cmd_window_down);
888 command_bind("window left", NULL, (SIGNAL_FUNC) cmd_window_left);
889 command_bind("window right", NULL, (SIGNAL_FUNC) cmd_window_right);
890 command_bind("window stick", NULL, (SIGNAL_FUNC) cmd_window_stick);
893 void mainwindows_deinit(void)
895 while (mainwindows != NULL)
896 mainwindow_destroy(mainwindows->data);
898 command_unbind("window grow", (SIGNAL_FUNC) cmd_window_grow);
899 command_unbind("window shrink", (SIGNAL_FUNC) cmd_window_shrink);
900 command_unbind("window size", (SIGNAL_FUNC) cmd_window_size);
901 command_unbind("window balance", (SIGNAL_FUNC) cmd_window_balance);
902 command_unbind("window hide", (SIGNAL_FUNC) cmd_window_hide);
903 command_unbind("window show", (SIGNAL_FUNC) cmd_window_show);
904 command_unbind("window up", (SIGNAL_FUNC) cmd_window_up);
905 command_unbind("window down", (SIGNAL_FUNC) cmd_window_down);
906 command_unbind("window left", (SIGNAL_FUNC) cmd_window_left);
907 command_unbind("window right", (SIGNAL_FUNC) cmd_window_right);
908 command_unbind("window stick", (SIGNAL_FUNC) cmd_window_stick);