Update to c0948a94827c1941d90ddb1db8c383dae4f86c05
[gnt.git] / gntwm.c
blob67f20984ae323fbefa85e75c3101f4e974218296
1 /**
2 * GNT - The GLib Ncurses Toolkit
4 * GNT is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #define _GNU_SOURCE
24 #if defined(__APPLE__) || defined(__unix__)
25 #define _XOPEN_SOURCE_EXTENDED
26 #endif
28 #include "config.h"
30 #include <glib.h>
31 #include <ctype.h>
32 #include <gmodule.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
37 #include "gntwm.h"
38 #include "gntstyle.h"
39 #include "gntmarshal.h"
40 #include "gnt.h"
41 #include "gntbox.h"
42 #include "gntbutton.h"
43 #include "gntentry.h"
44 #include "gntlabel.h"
45 #include "gntmenu.h"
46 #include "gnttextview.h"
47 #include "gnttree.h"
48 #include "gntutils.h"
49 #include "gntwindow.h"
51 #define IDLE_CHECK_INTERVAL 5 /* 5 seconds */
53 enum
55 SIG_NEW_WIN,
56 SIG_DECORATE_WIN,
57 SIG_CLOSE_WIN,
58 SIG_CONFIRM_RESIZE,
59 SIG_RESIZED,
60 SIG_CONFIRM_MOVE,
61 SIG_MOVED,
62 SIG_UPDATE_WIN,
63 SIG_GIVE_FOCUS,
64 SIG_KEY_PRESS,
65 SIG_MOUSE_CLICK,
66 SIG_TERMINAL_REFRESH,
67 SIGS
70 static guint signals[SIGS] = { 0 };
71 static void gnt_wm_new_window_real(GntWM *wm, GntWidget *widget);
72 static void gnt_wm_win_resized(GntWM *wm, GntNode *node);
73 static void gnt_wm_win_moved(GntWM *wm, GntNode *node);
74 static void gnt_wm_give_focus(GntWM *wm, GntWidget *widget);
75 static void update_window_in_list(GntWM *wm, GntWidget *wid);
76 static void shift_window(GntWM *wm, GntWidget *widget, int dir);
77 static gboolean workspace_next(GntBindable *wm, GList *n);
78 static gboolean workspace_prev(GntBindable *wm, GList *n);
80 #ifndef NO_WIDECHAR
81 static int widestringwidth(wchar_t *wide);
82 #endif
84 static gboolean write_already(gpointer data);
85 static int write_timeout;
86 static time_t last_active_time;
87 static gboolean idle_update;
88 static GList *act = NULL; /* list of WS with unseen activitiy */
89 static gboolean ignore_keys = FALSE;
91 static GList *
92 g_list_bring_to_front(GList *list, gpointer data)
94 list = g_list_remove(list, data);
95 list = g_list_prepend(list, data);
96 return list;
99 static void
100 free_node(gpointer data)
102 GntNode *node = data;
103 hide_panel(node->panel);
104 del_panel(node->panel);
105 g_free(node);
108 void
109 gnt_wm_copy_win(GntWidget *widget, GntNode *node)
111 WINDOW *src, *dst;
112 if (!node)
113 return;
114 src = widget->window;
115 dst = node->window;
116 copywin(src, dst, node->scroll, 0, 0, 0, getmaxy(dst) - 1, getmaxx(dst) - 1, 0);
120 * The following is a workaround for a bug in most versions of ncursesw.
121 * Read about it in: http://article.gmane.org/gmane.comp.lib.ncurses.bugs/2751
123 * In short, if a panel hides one cell of a multi-cell character, then the rest
124 * of the characters in that line get screwed. The workaround here is to erase
125 * any such character preemptively.
127 * Caveat: If a wide character is erased, and the panel above it is moved enough
128 * to expose the entire character, it is not always redrawn.
130 static void
131 work_around_for_ncurses_bug()
133 #ifndef NO_WIDECHAR
134 PANEL *panel = NULL;
135 while ((panel = panel_below(panel)) != NULL) {
136 int sx, ex, sy, ey, w, y;
137 cchar_t ch;
138 PANEL *below = panel;
140 sx = panel->win->_begx;
141 ex = panel->win->_maxx + sx;
142 sy = panel->win->_begy;
143 ey = panel->win->_maxy + sy;
145 while ((below = panel_below(below)) != NULL) {
146 if (sy > below->win->_begy + below->win->_maxy ||
147 ey < below->win->_begy)
148 continue;
149 if (sx > below->win->_begx + below->win->_maxx ||
150 ex < below->win->_begx)
151 continue;
152 for (y = MAX(sy, below->win->_begy); y <= MIN(ey, below->win->_begy + below->win->_maxy); y++) {
153 if (mvwin_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch) != OK)
154 goto right;
155 w = widestringwidth(ch.chars);
156 if (w > 1 && (ch.attr & 1)) {
157 ch.chars[0] = ' ';
158 ch.attr &= ~ A_CHARTEXT;
159 mvwadd_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch);
160 touchline(below->win, y - below->win->_begy, 1);
162 right:
163 if (mvwin_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch) != OK)
164 continue;
165 w = widestringwidth(ch.chars);
166 if (w > 1 && !(ch.attr & 1)) {
167 ch.chars[0] = ' ';
168 ch.attr &= ~ A_CHARTEXT;
169 mvwadd_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch);
170 touchline(below->win, y - below->win->_begy, 1);
175 #endif
178 static void
179 update_act_msg()
181 GntWidget *label;
182 GList *iter;
183 static GntWidget *message = NULL;
184 GString *text = g_string_new("act: ");
185 if (message)
186 gnt_widget_destroy(message);
187 if (g_list_length(act) == 0)
188 return;
189 for (iter = act; iter; iter = iter->next) {
190 GntWS *ws = iter->data;
191 g_string_append_printf(text, "%s, ", gnt_ws_get_name(ws));
193 g_string_erase(text, text->len - 2, 2);
194 message = gnt_vbox_new(FALSE);
195 label = gnt_label_new_with_format(text->str, GNT_TEXT_FLAG_BOLD | GNT_TEXT_FLAG_HIGHLIGHT);
196 GNT_WIDGET_UNSET_FLAGS(GNT_BOX(message), GNT_WIDGET_CAN_TAKE_FOCUS);
197 GNT_WIDGET_SET_FLAGS(GNT_BOX(message), GNT_WIDGET_TRANSIENT);
198 gnt_box_add_widget(GNT_BOX(message), label);
199 gnt_widget_set_name(message, "wm-message");
200 gnt_widget_set_position(message, 0, 0);
201 gnt_widget_draw(message);
202 g_string_free(text, TRUE);
205 static gboolean
206 update_screen(GntWM *wm)
208 if (wm->mode == GNT_KP_MODE_WAIT_ON_CHILD)
209 return TRUE;
211 if (wm->menu) {
212 GntMenu *top = wm->menu;
213 while (top) {
214 GntNode *node = g_hash_table_lookup(wm->nodes, top);
215 if (node)
216 top_panel(node->panel);
217 top = top->submenu;
220 work_around_for_ncurses_bug();
221 update_panels();
222 doupdate();
223 return TRUE;
226 static gboolean
227 sanitize_position(GntWidget *widget, int *x, int *y)
229 int X_MAX = getmaxx(stdscr);
230 int Y_MAX = getmaxy(stdscr) - 1;
231 int w, h;
232 int nx, ny;
233 gboolean changed = FALSE;
235 gnt_widget_get_size(widget, &w, &h);
236 if (x) {
237 if (*x + w > X_MAX) {
238 nx = MAX(0, X_MAX - w);
239 if (nx != *x) {
240 *x = nx;
241 changed = TRUE;
245 if (y) {
246 if (*y + h > Y_MAX) {
247 ny = MAX(0, Y_MAX - h);
248 if (ny != *y) {
249 *y = ny;
250 changed = TRUE;
254 return changed;
257 static void
258 refresh_node(GntWidget *widget, GntNode *node, gpointer null)
260 int x, y, w, h;
261 int nw, nh;
263 int X_MAX = getmaxx(stdscr);
264 int Y_MAX = getmaxy(stdscr) - 1;
266 gnt_widget_get_position(widget, &x, &y);
267 gnt_widget_get_size(widget, &w, &h);
269 if (sanitize_position(widget, &x, &y))
270 gnt_screen_move_widget(widget, x, y);
272 nw = MIN(w, X_MAX);
273 nh = MIN(h, Y_MAX);
274 if (nw != w || nh != h)
275 gnt_screen_resize_widget(widget, nw, nh);
278 static void
279 read_window_positions(GntWM *wm)
281 #if GLIB_CHECK_VERSION(2,6,0)
282 GKeyFile *gfile = g_key_file_new();
283 char *filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL);
284 GError *error = NULL;
285 char **keys;
286 gsize nk;
288 if (!g_key_file_load_from_file(gfile, filename, G_KEY_FILE_NONE, &error)) {
289 g_printerr("GntWM: %s\n", error->message);
290 g_error_free(error);
291 g_free(filename);
292 return;
295 keys = g_key_file_get_keys(gfile, "positions", &nk, &error);
296 if (error) {
297 g_printerr("GntWM: %s\n", error->message);
298 g_error_free(error);
299 error = NULL;
300 } else {
301 while (nk--) {
302 char *title = keys[nk];
303 gsize l;
304 char **coords = g_key_file_get_string_list(gfile, "positions", title, &l, NULL);
305 if (l == 2) {
306 int x = atoi(coords[0]);
307 int y = atoi(coords[1]);
308 GntPosition *p = g_new0(GntPosition, 1);
309 p->x = x;
310 p->y = y;
311 g_hash_table_replace(wm->positions, g_strdup(title + 1), p);
312 } else {
313 g_printerr("GntWM: Invalid number of arguments for positioing a window.\n");
315 g_strfreev(coords);
317 g_strfreev(keys);
320 g_free(filename);
321 g_key_file_free(gfile);
322 #endif
325 static gboolean check_idle(gpointer n)
327 if (idle_update) {
328 time(&last_active_time);
329 idle_update = FALSE;
331 return TRUE;
334 static void
335 gnt_wm_init(GTypeInstance *instance, gpointer class)
337 GntWM *wm = GNT_WM(instance);
338 wm->workspaces = NULL;
339 wm->name_places = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
340 wm->title_places = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
341 gnt_style_read_workspaces(wm);
342 if (wm->workspaces == NULL) {
343 wm->cws = gnt_ws_new("default");
344 gnt_wm_add_workspace(wm, wm->cws);
345 } else {
346 wm->cws = wm->workspaces->data;
348 wm->event_stack = FALSE;
349 wm->tagged = NULL;
350 wm->windows = NULL;
351 wm->actions = NULL;
352 wm->nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node);
353 wm->positions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
354 if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE))
355 read_window_positions(wm);
356 g_timeout_add(IDLE_CHECK_INTERVAL * 1000, check_idle, NULL);
357 time(&last_active_time);
358 gnt_wm_switch_workspace(wm, 0);
361 static void
362 switch_window(GntWM *wm, int direction)
364 GntWidget *w = NULL, *wid = NULL;
365 int pos;
367 if (wm->_list.window || wm->menu)
368 return;
370 if (!wm->cws->ordered || !wm->cws->ordered->next)
371 return;
373 w = wm->cws->ordered->data;
374 pos = g_list_index(wm->cws->list, w);
375 pos += direction;
377 if (pos < 0)
378 wid = g_list_last(wm->cws->list)->data;
379 else if (pos >= g_list_length(wm->cws->list))
380 wid = wm->cws->list->data;
381 else if (pos >= 0)
382 wid = g_list_nth_data(wm->cws->list, pos);
384 gnt_wm_raise_window(wm, wid);
387 static gboolean
388 window_next(GntBindable *bindable, GList *null)
390 GntWM *wm = GNT_WM(bindable);
391 switch_window(wm, 1);
392 return TRUE;
395 static gboolean
396 window_prev(GntBindable *bindable, GList *null)
398 GntWM *wm = GNT_WM(bindable);
399 switch_window(wm, -1);
400 return TRUE;
403 static gboolean
404 switch_window_n(GntBindable *bind, GList *list)
406 GntWM *wm = GNT_WM(bind);
407 GList *l;
408 int n;
410 if (!wm->cws->ordered)
411 return TRUE;
413 if (list)
414 n = GPOINTER_TO_INT(list->data);
415 else
416 n = 0;
418 if ((l = g_list_nth(wm->cws->list, n)) != NULL)
420 gnt_wm_raise_window(wm, l->data);
423 return TRUE;
426 static gboolean
427 window_scroll_up(GntBindable *bindable, GList *null)
429 GntWM *wm = GNT_WM(bindable);
430 GntWidget *window;
431 GntNode *node;
433 if (!wm->cws->ordered)
434 return TRUE;
436 window = wm->cws->ordered->data;
437 node = g_hash_table_lookup(wm->nodes, window);
438 if (!node)
439 return TRUE;
441 if (node->scroll) {
442 node->scroll--;
443 gnt_wm_copy_win(window, node);
444 update_screen(wm);
446 return TRUE;
449 static gboolean
450 window_scroll_down(GntBindable *bindable, GList *null)
452 GntWM *wm = GNT_WM(bindable);
453 GntWidget *window;
454 GntNode *node;
455 int w, h;
457 if (!wm->cws->ordered)
458 return TRUE;
460 window = wm->cws->ordered->data;
461 node = g_hash_table_lookup(wm->nodes, window);
462 if (!node)
463 return TRUE;
465 gnt_widget_get_size(window, &w, &h);
466 if (h - node->scroll > getmaxy(node->window)) {
467 node->scroll++;
468 gnt_wm_copy_win(window, node);
469 update_screen(wm);
471 return TRUE;
474 static gboolean
475 window_close(GntBindable *bindable, GList *null)
477 GntWM *wm = GNT_WM(bindable);
479 if (wm->_list.window)
480 return TRUE;
482 if (wm->cws->ordered) {
483 gnt_widget_destroy(wm->cws->ordered->data);
486 return TRUE;
489 static void
490 destroy__list(GntWidget *widget, GntWM *wm)
492 wm->_list.window = NULL;
493 wm->_list.tree = NULL;
494 wm->windows = NULL;
495 wm->actions = NULL;
496 update_screen(wm);
499 static void
500 setup__list(GntWM *wm)
502 GntWidget *tree, *win;
503 win = wm->_list.window = gnt_box_new(FALSE, FALSE);
504 gnt_box_set_toplevel(GNT_BOX(win), TRUE);
505 gnt_box_set_pad(GNT_BOX(win), 0);
506 GNT_WIDGET_SET_FLAGS(win, GNT_WIDGET_TRANSIENT);
508 tree = wm->_list.tree = gnt_tree_new();
509 gnt_box_add_widget(GNT_BOX(win), tree);
511 g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(destroy__list), wm);
514 static void
515 window_list_activate(GntTree *tree, GntWM *wm)
517 GntBindable *sel = gnt_tree_get_selection_data(GNT_TREE(tree));
519 gnt_widget_destroy(wm->_list.window);
521 if (!sel)
522 return;
524 if (GNT_IS_WS(sel)) {
525 gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, sel));
526 } else {
527 gnt_wm_raise_window(wm, GNT_WIDGET(sel));
531 static void
532 populate_window_list(GntWM *wm, gboolean workspace)
534 GList *iter;
535 GntTree *tree = GNT_TREE(wm->windows->tree);
536 if (!workspace) {
537 for (iter = wm->cws->list; iter; iter = iter->next) {
538 GntBox *box = GNT_BOX(iter->data);
540 gnt_tree_add_row_last(tree, box,
541 gnt_tree_create_row(tree, box->title), NULL);
542 update_window_in_list(wm, GNT_WIDGET(box));
544 } else {
545 GList *ws = wm->workspaces;
546 for (; ws; ws = ws->next) {
547 gnt_tree_add_row_last(tree, ws->data,
548 gnt_tree_create_row(tree, gnt_ws_get_name(GNT_WS(ws->data))), NULL);
549 for (iter = GNT_WS(ws->data)->list; iter; iter = iter->next) {
550 GntBox *box = GNT_BOX(iter->data);
552 gnt_tree_add_row_last(tree, box,
553 gnt_tree_create_row(tree, box->title), ws->data);
554 update_window_in_list(wm, GNT_WIDGET(box));
560 static gboolean
561 window_list_key_pressed(GntWidget *widget, const char *text, GntWM *wm)
563 if (text[1] == 0 && wm->cws->ordered) {
564 GntBindable *sel = gnt_tree_get_selection_data(GNT_TREE(widget));
565 switch (text[0]) {
566 case '-':
567 case ',':
568 if (GNT_IS_WS(sel)) {
569 /* reorder the workspace. */
570 } else
571 shift_window(wm, GNT_WIDGET(sel), -1);
572 break;
573 case '=':
574 case '.':
575 if (GNT_IS_WS(sel)) {
576 /* reorder the workspace. */
577 } else
578 shift_window(wm, GNT_WIDGET(sel), 1);
579 break;
580 default:
581 return FALSE;
583 gnt_tree_remove_all(GNT_TREE(widget));
584 populate_window_list(wm, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "workspace")));
585 gnt_tree_set_selected(GNT_TREE(widget), sel);
586 return TRUE;
588 return FALSE;
591 static void
592 list_of_windows(GntWM *wm, gboolean workspace)
594 GntWidget *tree, *win;
595 setup__list(wm);
596 wm->windows = &wm->_list;
598 win = wm->windows->window;
599 tree = wm->windows->tree;
601 gnt_box_set_title(GNT_BOX(win), workspace ? "Workspace List" : "Window List");
603 populate_window_list(wm, workspace);
605 if (wm->cws->ordered)
606 gnt_tree_set_selected(GNT_TREE(tree), wm->cws->ordered->data);
607 else if (workspace)
608 gnt_tree_set_selected(GNT_TREE(tree), wm->cws);
610 g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(window_list_activate), wm);
611 g_signal_connect(G_OBJECT(tree), "key_pressed", G_CALLBACK(window_list_key_pressed), wm);
612 g_object_set_data(G_OBJECT(tree), "workspace", GINT_TO_POINTER(workspace));
614 gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3);
615 gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2);
616 gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4);
618 gnt_widget_show(win);
621 static gboolean
622 window_list(GntBindable *bindable, GList *null)
624 GntWM *wm = GNT_WM(bindable);
626 if (wm->_list.window || wm->menu)
627 return TRUE;
629 if (!wm->cws->ordered)
630 return TRUE;
632 list_of_windows(wm, FALSE);
634 return TRUE;
637 static gboolean
638 dump_screen(GntBindable *bindable, GList *null)
640 int x, y;
641 chtype old = 0, now = 0;
642 FILE *file = fopen("dump.html", "w");
643 struct {
644 char ascii;
645 char *unicode;
646 } unis[] = {
647 {'q', "&#x2500;"},
648 {'t', "&#x251c;"},
649 {'u', "&#x2524;"},
650 {'x', "&#x2502;"},
651 {'-', "&#x2191;"},
652 {'.', "&#x2193;"},
653 {'l', "&#x250c;"},
654 {'k', "&#x2510;"},
655 {'m', "&#x2514;"},
656 {'j', "&#x2518;"},
657 {'a', "&#x2592;"},
658 {'n', "&#x253c;"},
659 {'w', "&#x252c;"},
660 {'v', "&#x2534;"},
661 {'\0', NULL}
664 fprintf(file, "<head>\n <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\n</head>\n<body>\n");
665 fprintf(file, "<pre>");
666 for (y = 0; y < getmaxy(stdscr); y++) {
667 for (x = 0; x < getmaxx(stdscr); x++) {
668 char ch[2] = {0, 0}, *print;
669 #ifdef NO_WIDECHAR
670 now = mvwinch(curscr, y, x);
671 ch[0] = now & A_CHARTEXT;
672 now ^= ch[0];
673 #else
674 cchar_t wch;
675 char unicode[12];
676 mvwin_wch(curscr, y, x, &wch);
677 now = wch.attr;
678 ch[0] = (char)(wch.chars[0] & 0xff);
679 #endif
681 #define CHECK(attr, start, end) \
682 do \
684 if (now & attr) \
686 if (!(old & attr)) \
687 fprintf(file, "%s", start); \
689 else if (old & attr) \
691 fprintf(file, "%s", end); \
693 } while (0)
695 CHECK(A_BOLD, "<b>", "</b>");
696 CHECK(A_UNDERLINE, "<u>", "</u>");
697 CHECK(A_BLINK, "<blink>", "</blink>");
699 if ((now & A_COLOR) != (old & A_COLOR) ||
700 (now & A_REVERSE) != (old & A_REVERSE))
702 int ret;
703 short fgp, bgp, r, g, b;
704 struct
706 int r, g, b;
707 } fg, bg;
709 ret = pair_content(PAIR_NUMBER(now & A_COLOR), &fgp, &bgp);
710 if (fgp == -1)
711 fgp = COLOR_BLACK;
712 if (bgp == -1)
713 bgp = COLOR_WHITE;
714 if (now & A_REVERSE)
716 short tmp = fgp;
717 fgp = bgp;
718 bgp = tmp;
720 ret = color_content(fgp, &r, &g, &b);
721 fg.r = r; fg.b = b; fg.g = g;
722 ret = color_content(bgp, &r, &g, &b);
723 bg.r = r; bg.b = b; bg.g = g;
724 #define ADJUST(x) (x = x * 255 / 1000)
725 ADJUST(fg.r);
726 ADJUST(fg.g);
727 ADJUST(fg.b);
728 ADJUST(bg.r);
729 ADJUST(bg.b);
730 ADJUST(bg.g);
732 if (x) fprintf(file, "</span>");
733 fprintf(file, "<span style=\"background:#%02x%02x%02x;color:#%02x%02x%02x\">",
734 bg.r, bg.g, bg.b, fg.r, fg.g, fg.b);
736 print = ch;
737 #ifndef NO_WIDECHAR
738 if (wch.chars[0] > 255) {
739 snprintf(unicode, sizeof(unicode), "&#x%x;", (unsigned int)wch.chars[0]);
740 print = unicode;
742 #endif
743 if (now & A_ALTCHARSET)
745 int u;
746 for (u = 0; unis[u].ascii; u++) {
747 if (ch[0] == unis[u].ascii) {
748 print = unis[u].unicode;
749 break;
752 if (!unis[u].ascii)
753 print = " ";
755 if (ch[0] == '&')
756 fprintf(file, "&amp;");
757 else if (ch[0] == '<')
758 fprintf(file, "&lt;");
759 else if (ch[0] == '>')
760 fprintf(file, "&gt;");
761 else
762 fprintf(file, "%s", print);
763 old = now;
765 fprintf(file, "</span>\n");
766 old = 0;
768 fprintf(file, "</pre>\n</body>");
769 fclose(file);
770 return TRUE;
773 static void
774 shift_window(GntWM *wm, GntWidget *widget, int dir)
776 GList *all = wm->cws->list;
777 GList *list = g_list_find(all, widget);
778 int length, pos;
779 if (!list)
780 return;
782 length = g_list_length(all);
783 pos = g_list_position(all, list);
785 pos += dir;
786 if (dir > 0)
787 pos++;
789 if (pos < 0)
790 pos = length;
791 else if (pos > length)
792 pos = 0;
794 all = g_list_insert(all, widget, pos);
795 all = g_list_delete_link(all, list);
796 wm->cws->list = all;
797 gnt_ws_draw_taskbar(wm->cws, FALSE);
800 static gboolean
801 shift_left(GntBindable *bindable, GList *null)
803 GntWM *wm = GNT_WM(bindable);
804 if (wm->_list.window)
805 return TRUE;
807 if(!wm->cws->ordered)
808 return FALSE;
810 shift_window(wm, wm->cws->ordered->data, -1);
811 return TRUE;
814 static gboolean
815 shift_right(GntBindable *bindable, GList *null)
817 GntWM *wm = GNT_WM(bindable);
819 if (wm->_list.window)
820 return TRUE;
822 if(!wm->cws->ordered)
823 return FALSE;
825 shift_window(wm, wm->cws->ordered->data, 1);
826 return TRUE;
829 static void
830 action_list_activate(GntTree *tree, GntWM *wm)
832 GntAction *action = gnt_tree_get_selection_data(tree);
833 action->callback();
834 gnt_widget_destroy(wm->_list.window);
837 static int
838 compare_action(gconstpointer p1, gconstpointer p2)
840 const GntAction *a1 = p1;
841 const GntAction *a2 = p2;
843 return g_utf8_collate(a1->label, a2->label);
846 static gboolean
847 list_actions(GntBindable *bindable, GList *null)
849 GntWidget *tree, *win;
850 GList *iter;
851 GntWM *wm = GNT_WM(bindable);
852 if (wm->_list.window || wm->menu)
853 return TRUE;
855 if (wm->acts == NULL)
856 return TRUE;
858 setup__list(wm);
859 wm->actions = &wm->_list;
861 win = wm->actions->window;
862 tree = wm->actions->tree;
864 gnt_box_set_title(GNT_BOX(win), "Actions");
865 GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER);
866 /* XXX: Do we really want this? */
867 gnt_tree_set_compare_func(GNT_TREE(tree), compare_action);
869 for (iter = wm->acts; iter; iter = iter->next) {
870 GntAction *action = iter->data;
871 gnt_tree_add_row_last(GNT_TREE(tree), action,
872 gnt_tree_create_row(GNT_TREE(tree), action->label), NULL);
874 g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(action_list_activate), wm);
875 gnt_widget_set_size(tree, 0, g_list_length(wm->acts));
876 gnt_widget_set_position(win, 0, getmaxy(stdscr) - 3 - g_list_length(wm->acts));
878 gnt_widget_show(win);
879 return TRUE;
882 #ifndef NO_WIDECHAR
883 static int
884 widestringwidth(wchar_t *wide)
886 int len, ret;
887 char *string;
889 len = wcstombs(NULL, wide, 0) + 1;
890 string = g_new0(char, len);
891 wcstombs(string, wide, len);
892 ret = string ? gnt_util_onscreen_width(string, NULL) : 1;
893 g_free(string);
894 return ret;
896 #endif
898 /* Returns the onscreen width of the character at the position */
899 static int
900 reverse_char(WINDOW *d, int y, int x, gboolean set)
902 #define DECIDE(ch) (set ? ((ch) | A_REVERSE) : ((ch) & ~A_REVERSE))
904 #ifdef NO_WIDECHAR
905 chtype ch;
906 ch = mvwinch(d, y, x);
907 mvwaddch(d, y, x, DECIDE(ch));
908 return 1;
909 #else
910 cchar_t ch;
911 int wc = 1;
912 if (mvwin_wch(d, y, x, &ch) == OK) {
913 wc = widestringwidth(ch.chars);
914 ch.attr = DECIDE(ch.attr);
915 ch.attr &= WA_ATTRIBUTES; /* XXX: This is a workaround for a bug */
916 mvwadd_wch(d, y, x, &ch);
919 return wc;
920 #endif
923 static void
924 window_reverse(GntWidget *win, gboolean set, GntWM *wm)
926 int i;
927 int w, h;
928 WINDOW *d;
930 if (GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_NO_BORDER))
931 return;
933 d = win->window;
934 gnt_widget_get_size(win, &w, &h);
936 if (gnt_widget_has_shadow(win)) {
937 --w;
938 --h;
941 /* the top and bottom */
942 for (i = 0; i < w; i += reverse_char(d, 0, i, set));
943 for (i = 0; i < w; i += reverse_char(d, h-1, i, set));
945 /* the left and right */
946 for (i = 0; i < h; i += reverse_char(d, i, 0, set));
947 for (i = 0; i < h; i += reverse_char(d, i, w-1, set));
949 gnt_wm_copy_win(win, g_hash_table_lookup(wm->nodes, win));
950 update_screen(wm);
953 static gboolean
954 start_move(GntBindable *bindable, GList *null)
956 GntWM *wm = GNT_WM(bindable);
957 if (wm->_list.window || wm->menu)
958 return TRUE;
959 if (!wm->cws->ordered)
960 return TRUE;
962 wm->mode = GNT_KP_MODE_MOVE;
963 window_reverse(GNT_WIDGET(wm->cws->ordered->data), TRUE, wm);
965 return TRUE;
968 static gboolean
969 start_resize(GntBindable *bindable, GList *null)
971 GntWM *wm = GNT_WM(bindable);
972 if (wm->_list.window || wm->menu)
973 return TRUE;
974 if (!wm->cws->ordered)
975 return TRUE;
977 wm->mode = GNT_KP_MODE_RESIZE;
978 window_reverse(GNT_WIDGET(wm->cws->ordered->data), TRUE, wm);
980 return TRUE;
983 static gboolean
984 wm_quit(GntBindable *bindable, GList *list)
986 GntWM *wm = GNT_WM(bindable);
987 if (write_timeout)
988 write_already(wm);
989 g_main_loop_quit(wm->loop);
990 return TRUE;
993 static gboolean
994 return_true(GntWM *wm, GntWidget *w, int *a, int *b)
996 return TRUE;
999 static gboolean
1000 refresh_screen(GntBindable *bindable, GList *null)
1002 GntWM *wm = GNT_WM(bindable);
1004 endwin();
1005 refresh();
1007 g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, GINT_TO_POINTER(TRUE));
1008 g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0);
1009 update_screen(wm);
1010 gnt_ws_draw_taskbar(wm->cws, TRUE);
1011 curs_set(0); /* endwin resets the cursor to normal */
1013 return TRUE;
1016 static gboolean
1017 toggle_clipboard(GntBindable *bindable, GList *n)
1019 static GntWidget *clip;
1020 gchar *text;
1021 int maxx, maxy;
1022 if (clip) {
1023 gnt_widget_destroy(clip);
1024 clip = NULL;
1025 return TRUE;
1027 getmaxyx(stdscr, maxy, maxx);
1028 text = gnt_get_clipboard_string();
1029 clip = gnt_hwindow_new(FALSE);
1030 GNT_WIDGET_SET_FLAGS(clip, GNT_WIDGET_TRANSIENT);
1031 GNT_WIDGET_SET_FLAGS(clip, GNT_WIDGET_NO_BORDER);
1032 gnt_box_set_pad(GNT_BOX(clip), 0);
1033 gnt_box_add_widget(GNT_BOX(clip), gnt_label_new(" "));
1034 gnt_box_add_widget(GNT_BOX(clip), gnt_label_new(text));
1035 gnt_box_add_widget(GNT_BOX(clip), gnt_label_new(" "));
1036 gnt_widget_set_position(clip, 0, 0);
1037 gnt_widget_draw(clip);
1038 g_free(text);
1039 return TRUE;
1042 static void remove_tag(gpointer wid, gpointer wim)
1044 GntWM *wm = GNT_WM(wim);
1045 GntWidget *w = GNT_WIDGET(wid);
1046 wm->tagged = g_list_remove(wm->tagged, w);
1047 mvwhline(w->window, 0, 1, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL), 3);
1048 gnt_widget_draw(w);
1051 static gboolean
1052 tag_widget(GntBindable *b, GList *params)
1054 GntWM *wm = GNT_WM(b);
1055 GntWidget *widget;
1057 if (!wm->cws->ordered)
1058 return FALSE;
1059 widget = wm->cws->ordered->data;
1061 if (g_list_find(wm->tagged, widget)) {
1062 remove_tag(widget, wm);
1063 return TRUE;
1066 wm->tagged = g_list_prepend(wm->tagged, widget);
1067 wbkgdset(widget->window, ' ' | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
1068 mvwprintw(widget->window, 0, 1, "[T]");
1069 gnt_widget_draw(widget);
1070 return TRUE;
1073 static void
1074 widget_move_ws(gpointer wid, gpointer w)
1076 GntWM *wm = GNT_WM(w);
1077 gnt_wm_widget_move_workspace(wm, wm->cws, GNT_WIDGET(wid));
1080 static gboolean
1081 place_tagged(GntBindable *b, GList *params)
1083 GntWM *wm = GNT_WM(b);
1084 g_list_foreach(wm->tagged, widget_move_ws, wm);
1085 g_list_foreach(wm->tagged, remove_tag, wm);
1086 g_list_free(wm->tagged);
1087 wm->tagged = NULL;
1088 return TRUE;
1091 static gboolean
1092 workspace_list(GntBindable *b, GList *params)
1094 GntWM *wm = GNT_WM(b);
1096 if (wm->_list.window || wm->menu)
1097 return TRUE;
1099 list_of_windows(wm, TRUE);
1101 return TRUE;
1104 static gboolean
1105 workspace_new(GntBindable *bindable, GList *null)
1107 GntWM *wm = GNT_WM(bindable);
1108 GntWS *ws = gnt_ws_new(NULL);
1109 gnt_wm_add_workspace(wm, ws);
1110 gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws));
1111 return TRUE;
1114 static gboolean
1115 ignore_keys_start(GntBindable *bindable, GList *n)
1117 GntWM *wm = GNT_WM(bindable);
1119 if(!wm->menu && !wm->_list.window && wm->mode == GNT_KP_MODE_NORMAL){
1120 ignore_keys = TRUE;
1121 return TRUE;
1123 return FALSE;
1126 static gboolean
1127 ignore_keys_end(GntBindable *bindable, GList *n)
1129 return ignore_keys ? !(ignore_keys = FALSE) : FALSE;
1132 static gboolean
1133 help_for_bindable(GntWM *wm, GntBindable *bindable)
1135 gboolean ret = TRUE;
1136 GntBindableClass *klass = GNT_BINDABLE_GET_CLASS(bindable);
1138 if (klass->help_window) {
1139 gnt_wm_raise_window(wm, GNT_WIDGET(klass->help_window));
1140 } else {
1141 ret = gnt_bindable_build_help_window(bindable);
1143 return ret;
1146 static gboolean
1147 help_for_wm(GntBindable *bindable, GList *null)
1149 return help_for_bindable(GNT_WM(bindable),bindable);
1152 static gboolean
1153 help_for_window(GntBindable *bindable, GList *null)
1155 GntWM *wm = GNT_WM(bindable);
1156 GntWidget *widget;
1158 if(!wm->cws->ordered)
1159 return FALSE;
1161 widget = wm->cws->ordered->data;
1163 return help_for_bindable(wm,GNT_BINDABLE(widget));
1166 static gboolean
1167 help_for_widget(GntBindable *bindable, GList *null)
1169 GntWM *wm = GNT_WM(bindable);
1170 GntWidget *widget;
1172 if (!wm->cws->ordered)
1173 return TRUE;
1175 widget = wm->cws->ordered->data;
1176 if (!GNT_IS_BOX(widget))
1177 return TRUE;
1179 return help_for_bindable(wm, GNT_BINDABLE(GNT_BOX(widget)->active));
1182 static void
1183 accumulate_windows(gpointer window, gpointer node, gpointer p)
1185 GList *list = *(GList**)p;
1186 list = g_list_prepend(list, window);
1187 *(GList**)p = list;
1190 static void
1191 gnt_wm_destroy(GObject *obj)
1193 GntWM *wm = GNT_WM(obj);
1194 GList *list = NULL;
1195 g_hash_table_foreach(wm->nodes, accumulate_windows, &list);
1196 g_list_foreach(list, (GFunc)gnt_widget_destroy, NULL);
1197 g_list_free(list);
1198 g_hash_table_destroy(wm->nodes);
1199 wm->nodes = NULL;
1201 while (wm->workspaces) {
1202 g_object_unref(wm->workspaces->data);
1203 wm->workspaces = g_list_delete_link(wm->workspaces, wm->workspaces);
1207 static void
1208 gnt_wm_class_init(GntWMClass *klass)
1210 int i;
1211 GObjectClass *gclass = G_OBJECT_CLASS(klass);
1213 gclass->dispose = gnt_wm_destroy;
1215 klass->new_window = gnt_wm_new_window_real;
1216 klass->decorate_window = NULL;
1217 klass->close_window = NULL;
1218 klass->window_resize_confirm = return_true;
1219 klass->window_resized = gnt_wm_win_resized;
1220 klass->window_move_confirm = return_true;
1221 klass->window_moved = gnt_wm_win_moved;
1222 klass->window_update = NULL;
1223 klass->key_pressed = NULL;
1224 klass->mouse_clicked = NULL;
1225 klass->give_focus = gnt_wm_give_focus;
1227 signals[SIG_NEW_WIN] =
1228 g_signal_new("new_win",
1229 G_TYPE_FROM_CLASS(klass),
1230 G_SIGNAL_RUN_LAST,
1231 G_STRUCT_OFFSET(GntWMClass, new_window),
1232 NULL, NULL,
1233 g_cclosure_marshal_VOID__POINTER,
1234 G_TYPE_NONE, 1, G_TYPE_POINTER);
1235 signals[SIG_DECORATE_WIN] =
1236 g_signal_new("decorate_win",
1237 G_TYPE_FROM_CLASS(klass),
1238 G_SIGNAL_RUN_LAST,
1239 G_STRUCT_OFFSET(GntWMClass, decorate_window),
1240 NULL, NULL,
1241 g_cclosure_marshal_VOID__POINTER,
1242 G_TYPE_NONE, 1, G_TYPE_POINTER);
1243 signals[SIG_CLOSE_WIN] =
1244 g_signal_new("close_win",
1245 G_TYPE_FROM_CLASS(klass),
1246 G_SIGNAL_RUN_LAST,
1247 G_STRUCT_OFFSET(GntWMClass, close_window),
1248 NULL, NULL,
1249 g_cclosure_marshal_VOID__POINTER,
1250 G_TYPE_NONE, 1, G_TYPE_POINTER);
1251 signals[SIG_CONFIRM_RESIZE] =
1252 g_signal_new("confirm_resize",
1253 G_TYPE_FROM_CLASS(klass),
1254 G_SIGNAL_RUN_LAST,
1255 G_STRUCT_OFFSET(GntWMClass, window_resize_confirm),
1256 gnt_boolean_handled_accumulator, NULL,
1257 gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER,
1258 G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
1260 signals[SIG_CONFIRM_MOVE] =
1261 g_signal_new("confirm_move",
1262 G_TYPE_FROM_CLASS(klass),
1263 G_SIGNAL_RUN_LAST,
1264 G_STRUCT_OFFSET(GntWMClass, window_move_confirm),
1265 gnt_boolean_handled_accumulator, NULL,
1266 gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER,
1267 G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
1269 signals[SIG_RESIZED] =
1270 g_signal_new("window_resized",
1271 G_TYPE_FROM_CLASS(klass),
1272 G_SIGNAL_RUN_LAST,
1273 G_STRUCT_OFFSET(GntWMClass, window_resized),
1274 NULL, NULL,
1275 g_cclosure_marshal_VOID__POINTER,
1276 G_TYPE_NONE, 1, G_TYPE_POINTER);
1277 signals[SIG_MOVED] =
1278 g_signal_new("window_moved",
1279 G_TYPE_FROM_CLASS(klass),
1280 G_SIGNAL_RUN_LAST,
1281 G_STRUCT_OFFSET(GntWMClass, window_moved),
1282 NULL, NULL,
1283 g_cclosure_marshal_VOID__POINTER,
1284 G_TYPE_NONE, 1, G_TYPE_POINTER);
1285 signals[SIG_UPDATE_WIN] =
1286 g_signal_new("window_update",
1287 G_TYPE_FROM_CLASS(klass),
1288 G_SIGNAL_RUN_LAST,
1289 G_STRUCT_OFFSET(GntWMClass, window_update),
1290 NULL, NULL,
1291 g_cclosure_marshal_VOID__POINTER,
1292 G_TYPE_NONE, 1, G_TYPE_POINTER);
1294 signals[SIG_GIVE_FOCUS] =
1295 g_signal_new("give_focus",
1296 G_TYPE_FROM_CLASS(klass),
1297 G_SIGNAL_RUN_LAST,
1298 G_STRUCT_OFFSET(GntWMClass, give_focus),
1299 NULL, NULL,
1300 g_cclosure_marshal_VOID__POINTER,
1301 G_TYPE_NONE, 1, G_TYPE_POINTER);
1303 signals[SIG_MOUSE_CLICK] =
1304 g_signal_new("mouse_clicked",
1305 G_TYPE_FROM_CLASS(klass),
1306 G_SIGNAL_RUN_LAST,
1307 G_STRUCT_OFFSET(GntWMClass, mouse_clicked),
1308 gnt_boolean_handled_accumulator, NULL,
1309 gnt_closure_marshal_BOOLEAN__INT_INT_INT_POINTER,
1310 G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER);
1312 signals[SIG_TERMINAL_REFRESH] =
1313 g_signal_new("terminal-refresh",
1314 G_TYPE_FROM_CLASS(klass),
1315 G_SIGNAL_RUN_LAST,
1316 G_STRUCT_OFFSET(GntWMClass, terminal_refresh),
1317 NULL, NULL,
1318 g_cclosure_marshal_VOID__VOID,
1319 G_TYPE_NONE, 0);
1321 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next", window_next,
1322 "\033" "n", NULL);
1323 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev", window_prev,
1324 "\033" "p", NULL);
1325 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-close", window_close,
1326 "\033" "c", NULL);
1327 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-list", window_list,
1328 "\033" "w", NULL);
1329 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "dump-screen", dump_screen,
1330 "\033" "d", NULL);
1331 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-left", shift_left,
1332 "\033" ",", NULL);
1333 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-right", shift_right,
1334 "\033" ".", NULL);
1335 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "action-list", list_actions,
1336 "\033" "a", NULL);
1337 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-move", start_move,
1338 "\033" "m", NULL);
1339 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-resize", start_resize,
1340 "\033" "r", NULL);
1341 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "wm-quit", wm_quit,
1342 "\033" "q", NULL);
1343 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "refresh-screen", refresh_screen,
1344 "\033" "l", NULL);
1345 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "switch-window-n", switch_window_n,
1346 NULL, NULL);
1347 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-scroll-down", window_scroll_down,
1348 "\033" GNT_KEY_CTRL_J, NULL);
1349 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-scroll-up", window_scroll_up,
1350 "\033" GNT_KEY_CTRL_K, NULL);
1351 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-widget", help_for_widget,
1352 "\033" "/", NULL);
1353 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-new", workspace_new,
1354 GNT_KEY_F9, NULL);
1355 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-next", workspace_next,
1356 "\033" ">", NULL);
1357 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-prev", workspace_prev,
1358 "\033" "<", NULL);
1359 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-tag", tag_widget,
1360 "\033" "t", NULL);
1361 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "place-tagged", place_tagged,
1362 "\033" "T", NULL);
1363 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-list", workspace_list,
1364 "\033" "s", NULL);
1365 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "toggle-clipboard", toggle_clipboard,
1366 "\033" "C", NULL);
1367 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-wm", help_for_wm,
1368 "\033" "\\", NULL);
1369 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-window", help_for_window,
1370 "\033" "|", NULL);
1371 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start,
1372 GNT_KEY_CTRL_G, NULL);
1373 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end,
1374 "\033" GNT_KEY_CTRL_G, NULL);
1376 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
1378 /* Make sure Alt+x are detected properly. */
1379 for (i = '0'; i <= '9'; i++) {
1380 char str[] = "\033X";
1381 str[1] = i;
1382 gnt_keys_add_combination(str);
1385 GNTDEBUG;
1388 /******************************************************************************
1389 * GntWM API
1390 *****************************************************************************/
1391 GType
1392 gnt_wm_get_gtype(void)
1394 static GType type = 0;
1396 if(type == 0) {
1397 static const GTypeInfo info = {
1398 sizeof(GntWMClass),
1399 NULL, /* base_init */
1400 NULL, /* base_finalize */
1401 (GClassInitFunc)gnt_wm_class_init,
1402 NULL,
1403 NULL, /* class_data */
1404 sizeof(GntWM),
1405 0, /* n_preallocs */
1406 gnt_wm_init, /* instance_init */
1407 NULL /* value_table */
1410 type = g_type_register_static(GNT_TYPE_BINDABLE,
1411 "GntWM",
1412 &info, 0);
1415 return type;
1418 void
1419 gnt_wm_add_workspace(GntWM *wm, GntWS *ws)
1421 wm->workspaces = g_list_append(wm->workspaces, ws);
1424 gboolean
1425 gnt_wm_switch_workspace(GntWM *wm, gint n)
1427 GntWS *s = g_list_nth_data(wm->workspaces, n);
1428 if (!s)
1429 return FALSE;
1431 if (wm->_list.window) {
1432 gnt_widget_destroy(wm->_list.window);
1434 gnt_ws_hide(wm->cws, wm->nodes);
1435 wm->cws = s;
1436 gnt_ws_show(wm->cws, wm->nodes);
1438 gnt_ws_draw_taskbar(wm->cws, TRUE);
1439 update_screen(wm);
1440 if (wm->cws->ordered) {
1441 gnt_wm_raise_window(wm, wm->cws->ordered->data);
1444 if (act && g_list_find(act, wm->cws)) {
1445 act = g_list_remove(act, wm->cws);
1446 update_act_msg();
1448 return TRUE;
1451 gboolean
1452 gnt_wm_switch_workspace_prev(GntWM *wm)
1454 int n = g_list_index(wm->workspaces, wm->cws);
1455 return gnt_wm_switch_workspace(wm, --n);
1458 gboolean
1459 gnt_wm_switch_workspace_next(GntWM *wm)
1461 int n = g_list_index(wm->workspaces, wm->cws);
1462 return gnt_wm_switch_workspace(wm, ++n);
1465 static gboolean
1466 workspace_next(GntBindable *wm, GList *n)
1468 return gnt_wm_switch_workspace_next(GNT_WM(wm));
1471 static gboolean
1472 workspace_prev(GntBindable *wm, GList *n)
1474 return gnt_wm_switch_workspace_prev(GNT_WM(wm));
1477 void
1478 gnt_wm_widget_move_workspace(GntWM *wm, GntWS *neww, GntWidget *widget)
1480 GntWS *oldw = gnt_wm_widget_find_workspace(wm, widget);
1481 GntNode *node;
1482 if (!oldw || oldw == neww)
1483 return;
1484 node = g_hash_table_lookup(wm->nodes, widget);
1485 if (node && node->ws == neww)
1486 return;
1488 if (node)
1489 node->ws = neww;
1491 gnt_ws_remove_widget(oldw, widget);
1492 gnt_ws_add_widget(neww, widget);
1493 if (neww == wm->cws) {
1494 gnt_ws_widget_show(widget, wm->nodes);
1495 } else {
1496 gnt_ws_widget_hide(widget, wm->nodes);
1500 static gint widget_in_workspace(gconstpointer workspace, gconstpointer wid)
1502 GntWS *s = (GntWS *)workspace;
1503 if (s->list && g_list_find(s->list, wid))
1504 return 0;
1505 return 1;
1508 GntWS *gnt_wm_widget_find_workspace(GntWM *wm, GntWidget *widget)
1510 GList *l = g_list_find_custom(wm->workspaces, widget, widget_in_workspace);
1511 if (l)
1512 return l->data;
1513 return NULL;
1516 static void free_workspaces(gpointer data, gpointer n)
1518 GntWS *s = data;
1519 g_free(s->name);
1522 void gnt_wm_set_workspaces(GntWM *wm, GList *workspaces)
1524 g_list_foreach(wm->workspaces, free_workspaces, NULL);
1525 wm->workspaces = workspaces;
1526 gnt_wm_switch_workspace(wm, 0);
1529 static void
1530 update_window_in_list(GntWM *wm, GntWidget *wid)
1532 GntTextFormatFlags flag = 0;
1534 if (wm->windows == NULL)
1535 return;
1537 if (wm->cws->ordered && wid == wm->cws->ordered->data)
1538 flag |= GNT_TEXT_FLAG_DIM;
1539 else if (GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT))
1540 flag |= GNT_TEXT_FLAG_BOLD;
1542 gnt_tree_set_row_flags(GNT_TREE(wm->windows->tree), wid, flag);
1545 static gboolean
1546 match_title(gpointer title, gpointer n, gpointer wid_title)
1548 /* XXX: do any regex magic here. */
1549 if (g_strrstr((gchar *)wid_title, (gchar *)title))
1550 return TRUE;
1551 return FALSE;
1554 #if !GLIB_CHECK_VERSION(2,4,0)
1555 struct
1557 gpointer data;
1558 gpointer value;
1559 } table_find_data;
1561 static void
1562 table_find_helper(gpointer key, gpointer value, gpointer data)
1564 GHRFunc func = data;
1565 if (func(key, value, table_find_data.data))
1566 table_find_data.value = value;
1569 static gpointer
1570 g_hash_table_find(GHashTable * table, GHRFunc func, gpointer data)
1572 table_find_data.data = data;
1573 table_find_data.value = NULL;
1574 g_hash_table_foreach(table, table_find_helper, func);
1575 return table_find_data.value;
1577 #endif
1579 static GntWS *
1580 new_widget_find_workspace(GntWM *wm, GntWidget *widget)
1582 GntWS *ret = NULL;
1583 const gchar *name, *title;
1584 title = GNT_BOX(widget)->title;
1585 if (title)
1586 ret = g_hash_table_find(wm->title_places, match_title, (gpointer)title);
1587 if (ret)
1588 return ret;
1589 name = gnt_widget_get_name(widget);
1590 if (name)
1591 ret = g_hash_table_find(wm->name_places, match_title, (gpointer)name);
1592 return ret ? ret : wm->cws;
1595 static void
1596 gnt_wm_new_window_real(GntWM *wm, GntWidget *widget)
1598 GntNode *node;
1599 gboolean transient = FALSE;
1601 if (widget->window == NULL)
1602 return;
1604 node = g_new0(GntNode, 1);
1605 node->me = widget;
1606 node->scroll = 0;
1608 g_hash_table_replace(wm->nodes, widget, node);
1610 refresh_node(widget, node, NULL);
1612 transient = !!GNT_WIDGET_IS_FLAG_SET(node->me, GNT_WIDGET_TRANSIENT);
1614 #if 1
1616 int x, y, w, h, maxx, maxy;
1617 gboolean shadow = TRUE;
1619 if (!gnt_widget_has_shadow(widget))
1620 shadow = FALSE;
1621 x = widget->priv.x;
1622 y = widget->priv.y;
1623 w = widget->priv.width + shadow;
1624 h = widget->priv.height + shadow;
1626 maxx = getmaxx(stdscr);
1627 maxy = getmaxy(stdscr) - 1; /* room for the taskbar */
1629 x = MAX(0, x);
1630 y = MAX(0, y);
1631 if (x + w >= maxx)
1632 x = MAX(0, maxx - w);
1633 if (y + h >= maxy)
1634 y = MAX(0, maxy - h);
1636 w = MIN(w, maxx);
1637 h = MIN(h, maxy);
1638 node->window = newwin(h, w, y, x);
1639 gnt_wm_copy_win(widget, node);
1641 #endif
1643 node->panel = new_panel(node->window);
1644 set_panel_userptr(node->panel, node);
1646 if (!transient) {
1647 GntWS *ws = wm->cws;
1648 if (node->me != wm->_list.window) {
1649 if (GNT_IS_BOX(widget)) {
1650 ws = new_widget_find_workspace(wm, widget);
1652 node->ws = ws;
1653 ws->list = g_list_append(ws->list, widget);
1654 ws->ordered = g_list_append(ws->ordered, widget);
1657 if (wm->event_stack || node->me == wm->_list.window ||
1658 node->me == ws->ordered->data) {
1659 gnt_wm_raise_window(wm, node->me);
1660 } else {
1661 bottom_panel(node->panel); /* New windows should not grab focus */
1662 gnt_widget_set_focus(node->me, FALSE);
1663 gnt_widget_set_urgent(node->me);
1664 if (wm->cws != ws)
1665 gnt_ws_widget_hide(widget, wm->nodes);
1670 void gnt_wm_new_window(GntWM *wm, GntWidget *widget)
1672 while (widget->parent)
1673 widget = widget->parent;
1675 if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_INVISIBLE) ||
1676 g_hash_table_lookup(wm->nodes, widget)) {
1677 update_screen(wm);
1678 return;
1681 if (GNT_IS_BOX(widget)) {
1682 const char *title = GNT_BOX(widget)->title;
1683 GntPosition *p = NULL;
1684 if (title && (p = g_hash_table_lookup(wm->positions, title)) != NULL) {
1685 sanitize_position(widget, &p->x, &p->y);
1686 gnt_widget_set_position(widget, p->x, p->y);
1687 mvwin(widget->window, p->y, p->x);
1691 g_signal_emit(wm, signals[SIG_NEW_WIN], 0, widget);
1692 g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget);
1694 if (wm->windows && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
1695 if ((GNT_IS_BOX(widget) && GNT_BOX(widget)->title) && wm->_list.window != widget
1696 && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS)) {
1697 gnt_tree_add_row_last(GNT_TREE(wm->windows->tree), widget,
1698 gnt_tree_create_row(GNT_TREE(wm->windows->tree), GNT_BOX(widget)->title),
1699 g_object_get_data(G_OBJECT(wm->windows->tree), "workspace") ? wm->cws : NULL);
1700 update_window_in_list(wm, widget);
1704 update_screen(wm);
1705 gnt_ws_draw_taskbar(wm->cws, FALSE);
1708 void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget)
1710 g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget);
1713 void gnt_wm_window_close(GntWM *wm, GntWidget *widget)
1715 GntWS *s;
1716 int pos;
1718 s = gnt_wm_widget_find_workspace(wm, widget);
1720 if (g_hash_table_lookup(wm->nodes, widget) == NULL)
1721 return;
1723 g_signal_emit(wm, signals[SIG_CLOSE_WIN], 0, widget);
1724 g_hash_table_remove(wm->nodes, widget);
1726 if (wm->windows) {
1727 gnt_tree_remove(GNT_TREE(wm->windows->tree), widget);
1730 if (s) {
1731 pos = g_list_index(s->list, widget);
1733 if (pos != -1) {
1734 s->list = g_list_remove(s->list, widget);
1735 s->ordered = g_list_remove(s->ordered, widget);
1737 if (s->ordered && wm->cws == s)
1738 gnt_wm_raise_window(wm, s->ordered->data);
1742 update_screen(wm);
1743 gnt_ws_draw_taskbar(wm->cws, FALSE);
1746 time_t gnt_wm_get_idle_time()
1748 return time(NULL) - last_active_time;
1751 gboolean gnt_wm_process_input(GntWM *wm, const char *keys)
1753 gboolean ret = FALSE;
1755 keys = gnt_bindable_remap_keys(GNT_BINDABLE(wm), keys);
1757 idle_update = TRUE;
1758 if(ignore_keys){
1759 if(keys && !strcmp(keys, "\033" GNT_KEY_CTRL_G)){
1760 if(gnt_bindable_perform_action_key(GNT_BINDABLE(wm), keys)){
1761 return TRUE;
1764 return wm->cws->ordered ? gnt_widget_key_pressed(GNT_WIDGET(wm->cws->ordered->data), keys) : FALSE;
1767 if (gnt_bindable_perform_action_key(GNT_BINDABLE(wm), keys)) {
1768 return TRUE;
1771 /* Do some manual checking */
1772 if (wm->cws->ordered && wm->mode != GNT_KP_MODE_NORMAL) {
1773 int xmin = 0, ymin = 0, xmax = getmaxx(stdscr), ymax = getmaxy(stdscr) - 1;
1774 int x, y, w, h;
1775 GntWidget *widget = GNT_WIDGET(wm->cws->ordered->data);
1776 int ox, oy, ow, oh;
1778 gnt_widget_get_position(widget, &x, &y);
1779 gnt_widget_get_size(widget, &w, &h);
1780 ox = x; oy = y;
1781 ow = w; oh = h;
1783 if (wm->mode == GNT_KP_MODE_MOVE) {
1784 if (strcmp(keys, GNT_KEY_LEFT) == 0) {
1785 if (x > xmin)
1786 x--;
1787 } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) {
1788 if (x + w < xmax)
1789 x++;
1790 } else if (strcmp(keys, GNT_KEY_UP) == 0) {
1791 if (y > ymin)
1792 y--;
1793 } else if (strcmp(keys, GNT_KEY_DOWN) == 0) {
1794 if (y + h < ymax)
1795 y++;
1797 if (ox != x || oy != y) {
1798 gnt_screen_move_widget(widget, x, y);
1799 window_reverse(widget, TRUE, wm);
1800 return TRUE;
1802 } else if (wm->mode == GNT_KP_MODE_RESIZE) {
1803 if (strcmp(keys, GNT_KEY_LEFT) == 0) {
1804 w--;
1805 } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) {
1806 if (x + w < xmax)
1807 w++;
1808 } else if (strcmp(keys, GNT_KEY_UP) == 0) {
1809 h--;
1810 } else if (strcmp(keys, GNT_KEY_DOWN) == 0) {
1811 if (y + h < ymax)
1812 h++;
1814 if (oh != h || ow != w) {
1815 gnt_screen_resize_widget(widget, w, h);
1816 window_reverse(widget, TRUE, wm);
1817 return TRUE;
1820 if (strcmp(keys, "\r") == 0 || strcmp(keys, "\033") == 0) {
1821 window_reverse(widget, FALSE, wm);
1822 wm->mode = GNT_KP_MODE_NORMAL;
1824 return TRUE;
1827 /* Escape to close the window-list or action-list window */
1828 if (strcmp(keys, "\033") == 0) {
1829 if (wm->_list.window) {
1830 gnt_widget_destroy(wm->_list.window);
1831 return TRUE;
1833 } else if (keys[0] == '\033' && isdigit(keys[1]) && keys[2] == '\0') {
1834 /* Alt+x for quick switch */
1835 int n = *(keys + 1) - '0';
1836 GList *list = NULL;
1838 if (n == 0)
1839 n = 10;
1841 list = g_list_append(list, GINT_TO_POINTER(n - 1));
1842 switch_window_n(GNT_BINDABLE(wm), list);
1843 g_list_free(list);
1844 return TRUE;
1847 if (wm->menu)
1848 ret = gnt_widget_key_pressed(GNT_WIDGET(wm->menu), keys);
1849 else if (wm->_list.window)
1850 ret = gnt_widget_key_pressed(wm->_list.window, keys);
1851 else if (wm->cws->ordered) {
1852 GntWidget *win = wm->cws->ordered->data;
1853 if (GNT_IS_WINDOW(win)) {
1854 GntMenu *menu = GNT_WINDOW(win)->menu;
1855 if (menu) {
1856 const char *id = gnt_window_get_accel_item(GNT_WINDOW(win), keys);
1857 if (id)
1858 ret = (gnt_menu_get_item(menu, id) != NULL);
1861 if (!ret)
1862 ret = gnt_widget_key_pressed(win, keys);
1864 return ret;
1867 static void
1868 gnt_wm_win_resized(GntWM *wm, GntNode *node)
1870 /*refresh_node(node->me, node, NULL);*/
1873 static void
1874 gnt_wm_win_moved(GntWM *wm, GntNode *node)
1876 refresh_node(node->me, node, NULL);
1879 void gnt_wm_resize_window(GntWM *wm, GntWidget *widget, int width, int height)
1881 gboolean ret = TRUE;
1882 GntNode *node;
1883 int maxx, maxy;
1885 while (widget->parent)
1886 widget = widget->parent;
1887 node = g_hash_table_lookup(wm->nodes, widget);
1888 if (!node)
1889 return;
1891 g_signal_emit(wm, signals[SIG_CONFIRM_RESIZE], 0, widget, &width, &height, &ret);
1892 if (!ret)
1893 return; /* resize is not permitted */
1894 hide_panel(node->panel);
1895 gnt_widget_set_size(widget, width, height);
1896 gnt_widget_draw(widget);
1898 maxx = getmaxx(stdscr);
1899 maxy = getmaxy(stdscr) - 1;
1900 height = MIN(height, maxy);
1901 width = MIN(width, maxx);
1902 wresize(node->window, height, width);
1903 replace_panel(node->panel, node->window);
1905 g_signal_emit(wm, signals[SIG_RESIZED], 0, node);
1907 show_panel(node->panel);
1908 update_screen(wm);
1911 static void
1912 write_gdi(gpointer key, gpointer value, gpointer data)
1914 GntPosition *p = value;
1915 fprintf(data, ".%s = %d;%d\n", (char *)key, p->x, p->y);
1918 static gboolean
1919 write_already(gpointer data)
1921 GntWM *wm = data;
1922 FILE *file;
1923 char *filename;
1925 filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL);
1927 file = fopen(filename, "wb");
1928 if (file == NULL) {
1929 g_printerr("GntWM: error opening file to save positions\n");
1930 } else {
1931 fprintf(file, "[positions]\n");
1932 g_hash_table_foreach(wm->positions, write_gdi, file);
1933 fclose(file);
1936 g_free(filename);
1937 g_source_remove(write_timeout);
1938 write_timeout = 0;
1939 return FALSE;
1942 static void
1943 write_positions_to_file(GntWM *wm)
1945 if (write_timeout) {
1946 g_source_remove(write_timeout);
1948 write_timeout = g_timeout_add(10000, write_already, wm);
1951 void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y)
1953 gboolean ret = TRUE;
1954 GntNode *node;
1956 while (widget->parent)
1957 widget = widget->parent;
1958 node = g_hash_table_lookup(wm->nodes, widget);
1959 if (!node)
1960 return;
1962 g_signal_emit(wm, signals[SIG_CONFIRM_MOVE], 0, widget, &x, &y, &ret);
1963 if (!ret)
1964 return; /* resize is not permitted */
1966 gnt_widget_set_position(widget, x, y);
1967 move_panel(node->panel, y, x);
1969 g_signal_emit(wm, signals[SIG_MOVED], 0, node);
1970 if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE) && GNT_IS_BOX(widget) &&
1971 !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
1972 const char *title = GNT_BOX(widget)->title;
1973 if (title) {
1974 GntPosition *p = g_new0(GntPosition, 1);
1975 GntWidget *wid = node->me;
1976 p->x = wid->priv.x;
1977 p->y = wid->priv.y;
1978 g_hash_table_replace(wm->positions, g_strdup(title), p);
1979 write_positions_to_file(wm);
1983 update_screen(wm);
1986 static void
1987 gnt_wm_give_focus(GntWM *wm, GntWidget *widget)
1989 GntNode *node = g_hash_table_lookup(wm->nodes, widget);
1991 if (!node)
1992 return;
1994 if (widget != wm->_list.window && !GNT_IS_MENU(widget) &&
1995 wm->cws->ordered->data != widget) {
1996 GntWidget *w = wm->cws->ordered->data;
1997 wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget);
1998 gnt_widget_set_focus(w, FALSE);
2001 gnt_widget_set_focus(widget, TRUE);
2002 GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_URGENT);
2003 gnt_widget_draw(widget);
2004 top_panel(node->panel);
2006 if (wm->_list.window) {
2007 GntNode *nd = g_hash_table_lookup(wm->nodes, wm->_list.window);
2008 top_panel(nd->panel);
2010 update_screen(wm);
2011 gnt_ws_draw_taskbar(wm->cws, FALSE);
2014 void gnt_wm_update_window(GntWM *wm, GntWidget *widget)
2016 GntNode *node = NULL;
2017 GntWS *ws;
2019 while (widget->parent)
2020 widget = widget->parent;
2021 if (!GNT_IS_MENU(widget)) {
2022 if (!GNT_IS_BOX(widget))
2023 return;
2024 gnt_box_sync_children(GNT_BOX(widget));
2027 ws = gnt_wm_widget_find_workspace(wm, widget);
2028 node = g_hash_table_lookup(wm->nodes, widget);
2029 if (node == NULL) {
2030 gnt_wm_new_window(wm, widget);
2031 } else
2032 g_signal_emit(wm, signals[SIG_UPDATE_WIN], 0, node);
2034 if (ws == wm->cws || GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
2035 gnt_wm_copy_win(widget, node);
2036 update_screen(wm);
2037 gnt_ws_draw_taskbar(wm->cws, FALSE);
2038 } else if (ws && ws != wm->cws && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_URGENT)) {
2039 if (!act || (act && !g_list_find(act, ws)))
2040 act = g_list_prepend(act, ws);
2041 update_act_msg();
2045 gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget)
2047 gboolean ret = TRUE;
2048 idle_update = TRUE;
2049 g_signal_emit(wm, signals[SIG_MOUSE_CLICK], 0, event, x, y, widget, &ret);
2050 return ret;
2053 void gnt_wm_raise_window(GntWM *wm, GntWidget *widget)
2055 GntWS *ws = gnt_wm_widget_find_workspace(wm, widget);
2056 if (wm->cws != ws)
2057 gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws));
2058 if (widget != wm->cws->ordered->data) {
2059 GntWidget *wid = wm->cws->ordered->data;
2060 wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget);
2061 gnt_widget_set_focus(wid, FALSE);
2062 gnt_widget_draw(wid);
2064 gnt_widget_set_focus(widget, TRUE);
2065 gnt_widget_draw(widget);
2066 g_signal_emit(wm, signals[SIG_GIVE_FOCUS], 0, widget);
2069 void gnt_wm_set_event_stack(GntWM *wm, gboolean set)
2071 wm->event_stack = set;