This is the latest rev of mtn.
[gnt.git] / gntwm.c
bloba14a5d649c1ebb29f0ea86f8163680e294d466db
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 int shadow;
113 if (!node)
114 return;
115 src = widget->window;
116 dst = node->window;
117 shadow = gnt_widget_has_shadow(widget) ? 1 : 0;
118 copywin(src, dst, node->scroll, 0, 0, 0, getmaxy(dst) - 1, getmaxx(dst) - 1, 0);
122 * The following is a workaround for a bug in most versions of ncursesw.
123 * Read about it in: http://article.gmane.org/gmane.comp.lib.ncurses.bugs/2751
125 * In short, if a panel hides one cell of a multi-cell character, then the rest
126 * of the characters in that line get screwed. The workaround here is to erase
127 * any such character preemptively.
129 * Caveat: If a wide character is erased, and the panel above it is moved enough
130 * to expose the entire character, it is not always redrawn.
132 static void
133 work_around_for_ncurses_bug()
135 #ifndef NO_WIDECHAR
136 PANEL *panel = NULL;
137 while ((panel = panel_below(panel)) != NULL) {
138 int sx, ex, sy, ey, w, y;
139 cchar_t ch;
140 PANEL *below = panel;
142 sx = panel->win->_begx;
143 ex = panel->win->_maxx + sx;
144 sy = panel->win->_begy;
145 ey = panel->win->_maxy + sy;
147 while ((below = panel_below(below)) != NULL) {
148 if (sy > below->win->_begy + below->win->_maxy ||
149 ey < below->win->_begy)
150 continue;
151 if (sx > below->win->_begx + below->win->_maxx ||
152 ex < below->win->_begx)
153 continue;
154 for (y = MAX(sy, below->win->_begy); y <= MIN(ey, below->win->_begy + below->win->_maxy); y++) {
155 if (mvwin_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch) != OK)
156 goto right;
157 w = widestringwidth(ch.chars);
158 if (w > 1 && (ch.attr & 1)) {
159 ch.chars[0] = ' ';
160 ch.attr &= ~ A_CHARTEXT;
161 mvwadd_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch);
162 touchline(below->win, y - below->win->_begy, 1);
164 right:
165 if (mvwin_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch) != OK)
166 continue;
167 w = widestringwidth(ch.chars);
168 if (w > 1 && !(ch.attr & 1)) {
169 ch.chars[0] = ' ';
170 ch.attr &= ~ A_CHARTEXT;
171 mvwadd_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch);
172 touchline(below->win, y - below->win->_begy, 1);
177 #endif
180 static void
181 update_act_msg()
183 GntWidget *label;
184 GList *iter;
185 static GntWidget *message = NULL;
186 GString *text = g_string_new("act: ");
187 if (message)
188 gnt_widget_destroy(message);
189 if (g_list_length(act) == 0)
190 return;
191 for (iter = act; iter; iter = iter->next) {
192 GntWS *ws = iter->data;
193 g_string_append_printf(text, "%s, ", gnt_ws_get_name(ws));
195 g_string_erase(text, text->len - 2, 2);
196 message = gnt_vbox_new(FALSE);
197 label = gnt_label_new_with_format(text->str, GNT_TEXT_FLAG_BOLD | GNT_TEXT_FLAG_HIGHLIGHT);
198 GNT_WIDGET_UNSET_FLAGS(GNT_BOX(message), GNT_WIDGET_CAN_TAKE_FOCUS);
199 GNT_WIDGET_SET_FLAGS(GNT_BOX(message), GNT_WIDGET_TRANSIENT);
200 gnt_box_add_widget(GNT_BOX(message), label);
201 gnt_widget_set_name(message, "wm-message");
202 gnt_widget_set_position(message, 0, 0);
203 gnt_widget_draw(message);
204 g_string_free(text, TRUE);
207 static gboolean
208 update_screen(GntWM *wm)
210 if (wm->mode == GNT_KP_MODE_WAIT_ON_CHILD)
211 return TRUE;
213 if (wm->menu) {
214 GntMenu *top = wm->menu;
215 while (top) {
216 GntNode *node = g_hash_table_lookup(wm->nodes, top);
217 if (node)
218 top_panel(node->panel);
219 top = top->submenu;
222 work_around_for_ncurses_bug();
223 update_panels();
224 doupdate();
225 return TRUE;
228 static gboolean
229 sanitize_position(GntWidget *widget, int *x, int *y)
231 int X_MAX = getmaxx(stdscr);
232 int Y_MAX = getmaxy(stdscr) - 1;
233 int w, h;
234 int nx, ny;
235 gboolean changed = FALSE;
237 gnt_widget_get_size(widget, &w, &h);
238 if (x) {
239 if (*x + w > X_MAX) {
240 nx = MAX(0, X_MAX - w);
241 if (nx != *x) {
242 *x = nx;
243 changed = TRUE;
247 if (y) {
248 if (*y + h > Y_MAX) {
249 ny = MAX(0, Y_MAX - h);
250 if (ny != *y) {
251 *y = ny;
252 changed = TRUE;
256 return changed;
259 static void
260 refresh_node(GntWidget *widget, GntNode *node, gpointer null)
262 int x, y, w, h;
263 int nw, nh;
265 int X_MAX = getmaxx(stdscr);
266 int Y_MAX = getmaxy(stdscr) - 1;
268 gnt_widget_get_position(widget, &x, &y);
269 gnt_widget_get_size(widget, &w, &h);
271 if (sanitize_position(widget, &x, &y))
272 gnt_screen_move_widget(widget, x, y);
274 nw = MIN(w, X_MAX);
275 nh = MIN(h, Y_MAX);
276 if (nw != w || nh != h)
277 gnt_screen_resize_widget(widget, nw, nh);
280 static void
281 read_window_positions(GntWM *wm)
283 #if GLIB_CHECK_VERSION(2,6,0)
284 GKeyFile *gfile = g_key_file_new();
285 char *filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL);
286 GError *error = NULL;
287 char **keys;
288 gsize nk;
290 if (!g_key_file_load_from_file(gfile, filename, G_KEY_FILE_NONE, &error)) {
291 g_printerr("GntWM: %s\n", error->message);
292 g_error_free(error);
293 g_free(filename);
294 return;
297 keys = g_key_file_get_keys(gfile, "positions", &nk, &error);
298 if (error) {
299 g_printerr("GntWM: %s\n", error->message);
300 g_error_free(error);
301 error = NULL;
302 } else {
303 while (nk--) {
304 char *title = keys[nk];
305 gsize l;
306 char **coords = g_key_file_get_string_list(gfile, "positions", title, &l, NULL);
307 if (l == 2) {
308 int x = atoi(coords[0]);
309 int y = atoi(coords[1]);
310 GntPosition *p = g_new0(GntPosition, 1);
311 p->x = x;
312 p->y = y;
313 g_hash_table_replace(wm->positions, g_strdup(title + 1), p);
314 } else {
315 g_printerr("GntWM: Invalid number of arguments for positioing a window.\n");
317 g_strfreev(coords);
319 g_strfreev(keys);
322 g_free(filename);
323 g_key_file_free(gfile);
324 #endif
327 static gboolean check_idle(gpointer n)
329 if (idle_update) {
330 time(&last_active_time);
331 idle_update = FALSE;
333 return TRUE;
336 static void
337 gnt_wm_init(GTypeInstance *instance, gpointer class)
339 GntWM *wm = GNT_WM(instance);
340 wm->workspaces = NULL;
341 wm->name_places = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
342 wm->title_places = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
343 gnt_style_read_workspaces(wm);
344 if (wm->workspaces == NULL) {
345 wm->cws = gnt_ws_new("default");
346 gnt_wm_add_workspace(wm, wm->cws);
347 } else {
348 wm->cws = wm->workspaces->data;
350 wm->event_stack = FALSE;
351 wm->tagged = NULL;
352 wm->windows = NULL;
353 wm->actions = NULL;
354 wm->nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node);
355 wm->positions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
356 if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE))
357 read_window_positions(wm);
358 g_timeout_add(IDLE_CHECK_INTERVAL * 1000, check_idle, NULL);
359 time(&last_active_time);
360 gnt_wm_switch_workspace(wm, 0);
363 static void
364 switch_window(GntWM *wm, int direction)
366 GntWidget *w = NULL, *wid = NULL;
367 int pos;
369 if (wm->_list.window || wm->menu)
370 return;
372 if (!wm->cws->ordered || !wm->cws->ordered->next)
373 return;
375 w = wm->cws->ordered->data;
376 pos = g_list_index(wm->cws->list, w);
377 pos += direction;
379 if (pos < 0)
380 wid = g_list_last(wm->cws->list)->data;
381 else if (pos >= g_list_length(wm->cws->list))
382 wid = wm->cws->list->data;
383 else if (pos >= 0)
384 wid = g_list_nth_data(wm->cws->list, pos);
386 gnt_wm_raise_window(wm, wid);
389 static gboolean
390 window_next(GntBindable *bindable, GList *null)
392 GntWM *wm = GNT_WM(bindable);
393 switch_window(wm, 1);
394 return TRUE;
397 static gboolean
398 window_prev(GntBindable *bindable, GList *null)
400 GntWM *wm = GNT_WM(bindable);
401 switch_window(wm, -1);
402 return TRUE;
405 static gboolean
406 switch_window_n(GntBindable *bind, GList *list)
408 GntWM *wm = GNT_WM(bind);
409 GList *l;
410 int n;
412 if (!wm->cws->ordered)
413 return TRUE;
415 if (list)
416 n = GPOINTER_TO_INT(list->data);
417 else
418 n = 0;
420 if ((l = g_list_nth(wm->cws->list, n)) != NULL)
422 gnt_wm_raise_window(wm, l->data);
425 return TRUE;
428 static gboolean
429 window_scroll_up(GntBindable *bindable, GList *null)
431 GntWM *wm = GNT_WM(bindable);
432 GntWidget *window;
433 GntNode *node;
435 if (!wm->cws->ordered)
436 return TRUE;
438 window = wm->cws->ordered->data;
439 node = g_hash_table_lookup(wm->nodes, window);
440 if (!node)
441 return TRUE;
443 if (node->scroll) {
444 node->scroll--;
445 gnt_wm_copy_win(window, node);
446 update_screen(wm);
448 return TRUE;
451 static gboolean
452 window_scroll_down(GntBindable *bindable, GList *null)
454 GntWM *wm = GNT_WM(bindable);
455 GntWidget *window;
456 GntNode *node;
457 int w, h;
459 if (!wm->cws->ordered)
460 return TRUE;
462 window = wm->cws->ordered->data;
463 node = g_hash_table_lookup(wm->nodes, window);
464 if (!node)
465 return TRUE;
467 gnt_widget_get_size(window, &w, &h);
468 if (h - node->scroll > getmaxy(node->window)) {
469 node->scroll++;
470 gnt_wm_copy_win(window, node);
471 update_screen(wm);
473 return TRUE;
476 static gboolean
477 window_close(GntBindable *bindable, GList *null)
479 GntWM *wm = GNT_WM(bindable);
481 if (wm->_list.window)
482 return TRUE;
484 if (wm->cws->ordered) {
485 gnt_widget_destroy(wm->cws->ordered->data);
488 return TRUE;
491 static void
492 destroy__list(GntWidget *widget, GntWM *wm)
494 wm->_list.window = NULL;
495 wm->_list.tree = NULL;
496 wm->windows = NULL;
497 wm->actions = NULL;
498 update_screen(wm);
501 static void
502 setup__list(GntWM *wm)
504 GntWidget *tree, *win;
505 win = wm->_list.window = gnt_box_new(FALSE, FALSE);
506 gnt_box_set_toplevel(GNT_BOX(win), TRUE);
507 gnt_box_set_pad(GNT_BOX(win), 0);
508 GNT_WIDGET_SET_FLAGS(win, GNT_WIDGET_TRANSIENT);
510 tree = wm->_list.tree = gnt_tree_new();
511 gnt_box_add_widget(GNT_BOX(win), tree);
513 g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(destroy__list), wm);
516 static void
517 window_list_activate(GntTree *tree, GntWM *wm)
519 GntBindable *sel = gnt_tree_get_selection_data(GNT_TREE(tree));
521 gnt_widget_destroy(wm->_list.window);
523 if (!sel)
524 return;
526 if (GNT_IS_WS(sel)) {
527 gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, sel));
528 } else {
529 gnt_wm_raise_window(wm, GNT_WIDGET(sel));
533 static void
534 populate_window_list(GntWM *wm, gboolean workspace)
536 GList *iter;
537 GntTree *tree = GNT_TREE(wm->windows->tree);
538 if (!workspace) {
539 for (iter = wm->cws->list; iter; iter = iter->next) {
540 GntBox *box = GNT_BOX(iter->data);
542 gnt_tree_add_row_last(tree, box,
543 gnt_tree_create_row(tree, box->title), NULL);
544 update_window_in_list(wm, GNT_WIDGET(box));
546 } else {
547 GList *ws = wm->workspaces;
548 for (; ws; ws = ws->next) {
549 gnt_tree_add_row_last(tree, ws->data,
550 gnt_tree_create_row(tree, gnt_ws_get_name(GNT_WS(ws->data))), NULL);
551 for (iter = GNT_WS(ws->data)->list; iter; iter = iter->next) {
552 GntBox *box = GNT_BOX(iter->data);
554 gnt_tree_add_row_last(tree, box,
555 gnt_tree_create_row(tree, box->title), ws->data);
556 update_window_in_list(wm, GNT_WIDGET(box));
562 static gboolean
563 window_list_key_pressed(GntWidget *widget, const char *text, GntWM *wm)
565 if (text[1] == 0 && wm->cws->ordered) {
566 GntBindable *sel = gnt_tree_get_selection_data(GNT_TREE(widget));
567 switch (text[0]) {
568 case '-':
569 case ',':
570 if (GNT_IS_WS(sel)) {
571 /* reorder the workspace. */
572 } else
573 shift_window(wm, GNT_WIDGET(sel), -1);
574 break;
575 case '=':
576 case '.':
577 if (GNT_IS_WS(sel)) {
578 /* reorder the workspace. */
579 } else
580 shift_window(wm, GNT_WIDGET(sel), 1);
581 break;
582 default:
583 return FALSE;
585 gnt_tree_remove_all(GNT_TREE(widget));
586 populate_window_list(wm, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "workspace")));
587 gnt_tree_set_selected(GNT_TREE(widget), sel);
588 return TRUE;
590 return FALSE;
593 static void
594 list_of_windows(GntWM *wm, gboolean workspace)
596 GntWidget *tree, *win;
597 setup__list(wm);
598 wm->windows = &wm->_list;
600 win = wm->windows->window;
601 tree = wm->windows->tree;
603 gnt_box_set_title(GNT_BOX(win), workspace ? "Workspace List" : "Window List");
605 populate_window_list(wm, workspace);
607 if (wm->cws->ordered)
608 gnt_tree_set_selected(GNT_TREE(tree), wm->cws->ordered->data);
609 else if (workspace)
610 gnt_tree_set_selected(GNT_TREE(tree), wm->cws);
612 g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(window_list_activate), wm);
613 g_signal_connect(G_OBJECT(tree), "key_pressed", G_CALLBACK(window_list_key_pressed), wm);
614 g_object_set_data(G_OBJECT(tree), "workspace", GINT_TO_POINTER(workspace));
616 gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3);
617 gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2);
618 gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4);
620 gnt_widget_show(win);
623 static gboolean
624 window_list(GntBindable *bindable, GList *null)
626 GntWM *wm = GNT_WM(bindable);
628 if (wm->_list.window || wm->menu)
629 return TRUE;
631 if (!wm->cws->ordered)
632 return TRUE;
634 list_of_windows(wm, FALSE);
636 return TRUE;
639 static gboolean
640 dump_screen(GntBindable *bindable, GList *null)
642 int x, y;
643 chtype old = 0, now = 0;
644 FILE *file = fopen("dump.html", "w");
645 struct {
646 char ascii;
647 char *unicode;
648 } unis[] = {
649 {'q', "&#x2500;"},
650 {'t', "&#x251c;"},
651 {'u', "&#x2524;"},
652 {'x', "&#x2502;"},
653 {'-', "&#x2191;"},
654 {'.', "&#x2193;"},
655 {'l', "&#x250c;"},
656 {'k', "&#x2510;"},
657 {'m', "&#x2514;"},
658 {'j', "&#x2518;"},
659 {'a', "&#x2592;"},
660 {'n', "&#x253c;"},
661 {'w', "&#x252c;"},
662 {'v', "&#x2534;"},
663 {'\0', NULL}
666 fprintf(file, "<head>\n <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\n</head>\n<body>\n");
667 fprintf(file, "<pre>");
668 for (y = 0; y < getmaxy(stdscr); y++) {
669 for (x = 0; x < getmaxx(stdscr); x++) {
670 char ch[2] = {0, 0}, *print;
671 #ifdef NO_WIDECHAR
672 now = mvwinch(curscr, y, x);
673 ch[0] = now & A_CHARTEXT;
674 now ^= ch[0];
675 #else
676 cchar_t wch;
677 char unicode[12];
678 mvwin_wch(curscr, y, x, &wch);
679 now = wch.attr;
680 ch[0] = (char)(wch.chars[0] & 0xff);
681 #endif
683 #define CHECK(attr, start, end) \
684 do \
686 if (now & attr) \
688 if (!(old & attr)) \
689 fprintf(file, "%s", start); \
691 else if (old & attr) \
693 fprintf(file, "%s", end); \
695 } while (0)
697 CHECK(A_BOLD, "<b>", "</b>");
698 CHECK(A_UNDERLINE, "<u>", "</u>");
699 CHECK(A_BLINK, "<blink>", "</blink>");
701 if ((now & A_COLOR) != (old & A_COLOR) ||
702 (now & A_REVERSE) != (old & A_REVERSE))
704 int ret;
705 short fgp, bgp, r, g, b;
706 struct
708 int r, g, b;
709 } fg, bg;
711 ret = pair_content(PAIR_NUMBER(now & A_COLOR), &fgp, &bgp);
712 if (fgp == -1)
713 fgp = COLOR_BLACK;
714 if (bgp == -1)
715 bgp = COLOR_WHITE;
716 if (now & A_REVERSE)
718 short tmp = fgp;
719 fgp = bgp;
720 bgp = tmp;
722 ret = color_content(fgp, &r, &g, &b);
723 fg.r = r; fg.b = b; fg.g = g;
724 ret = color_content(bgp, &r, &g, &b);
725 bg.r = r; bg.b = b; bg.g = g;
726 #define ADJUST(x) (x = x * 255 / 1000)
727 ADJUST(fg.r);
728 ADJUST(fg.g);
729 ADJUST(fg.b);
730 ADJUST(bg.r);
731 ADJUST(bg.b);
732 ADJUST(bg.g);
734 if (x) fprintf(file, "</span>");
735 fprintf(file, "<span style=\"background:#%02x%02x%02x;color:#%02x%02x%02x\">",
736 bg.r, bg.g, bg.b, fg.r, fg.g, fg.b);
738 print = ch;
739 #ifndef NO_WIDECHAR
740 if (wch.chars[0] > 255) {
741 snprintf(unicode, sizeof(unicode), "&#x%x;", (unsigned int)wch.chars[0]);
742 print = unicode;
744 #endif
745 if (now & A_ALTCHARSET)
747 int u;
748 for (u = 0; unis[u].ascii; u++) {
749 if (ch[0] == unis[u].ascii) {
750 print = unis[u].unicode;
751 break;
754 if (!unis[u].ascii)
755 print = " ";
757 if (ch[0] == '&')
758 fprintf(file, "&amp;");
759 else if (ch[0] == '<')
760 fprintf(file, "&lt;");
761 else if (ch[0] == '>')
762 fprintf(file, "&gt;");
763 else
764 fprintf(file, "%s", print);
765 old = now;
767 fprintf(file, "</span>\n");
768 old = 0;
770 fprintf(file, "</pre>\n</body>");
771 fclose(file);
772 return TRUE;
775 static void
776 shift_window(GntWM *wm, GntWidget *widget, int dir)
778 GList *all = wm->cws->list;
779 GList *list = g_list_find(all, widget);
780 int length, pos;
781 if (!list)
782 return;
784 length = g_list_length(all);
785 pos = g_list_position(all, list);
787 pos += dir;
788 if (dir > 0)
789 pos++;
791 if (pos < 0)
792 pos = length;
793 else if (pos > length)
794 pos = 0;
796 all = g_list_insert(all, widget, pos);
797 all = g_list_delete_link(all, list);
798 wm->cws->list = all;
799 gnt_ws_draw_taskbar(wm->cws, FALSE);
802 static gboolean
803 shift_left(GntBindable *bindable, GList *null)
805 GntWM *wm = GNT_WM(bindable);
806 if (wm->_list.window)
807 return TRUE;
809 if(!wm->cws->ordered)
810 return FALSE;
812 shift_window(wm, wm->cws->ordered->data, -1);
813 return TRUE;
816 static gboolean
817 shift_right(GntBindable *bindable, GList *null)
819 GntWM *wm = GNT_WM(bindable);
821 if (wm->_list.window)
822 return TRUE;
824 if(!wm->cws->ordered)
825 return FALSE;
827 shift_window(wm, wm->cws->ordered->data, 1);
828 return TRUE;
831 static void
832 action_list_activate(GntTree *tree, GntWM *wm)
834 GntAction *action = gnt_tree_get_selection_data(tree);
835 action->callback();
836 gnt_widget_destroy(wm->_list.window);
839 static int
840 compare_action(gconstpointer p1, gconstpointer p2)
842 const GntAction *a1 = p1;
843 const GntAction *a2 = p2;
845 return g_utf8_collate(a1->label, a2->label);
848 static gboolean
849 list_actions(GntBindable *bindable, GList *null)
851 GntWidget *tree, *win;
852 GList *iter;
853 GntWM *wm = GNT_WM(bindable);
854 if (wm->_list.window || wm->menu)
855 return TRUE;
857 if (wm->acts == NULL)
858 return TRUE;
860 setup__list(wm);
861 wm->actions = &wm->_list;
863 win = wm->actions->window;
864 tree = wm->actions->tree;
866 gnt_box_set_title(GNT_BOX(win), "Actions");
867 GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER);
868 /* XXX: Do we really want this? */
869 gnt_tree_set_compare_func(GNT_TREE(tree), compare_action);
871 for (iter = wm->acts; iter; iter = iter->next) {
872 GntAction *action = iter->data;
873 gnt_tree_add_row_last(GNT_TREE(tree), action,
874 gnt_tree_create_row(GNT_TREE(tree), action->label), NULL);
876 g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(action_list_activate), wm);
877 gnt_widget_set_size(tree, 0, g_list_length(wm->acts));
878 gnt_widget_set_position(win, 0, getmaxy(stdscr) - 3 - g_list_length(wm->acts));
880 gnt_widget_show(win);
881 return TRUE;
884 #ifndef NO_WIDECHAR
885 static int
886 widestringwidth(wchar_t *wide)
888 int len, ret;
889 char *string;
891 len = wcstombs(NULL, wide, 0) + 1;
892 string = g_new0(char, len);
893 wcstombs(string, wide, len);
894 ret = string ? gnt_util_onscreen_width(string, NULL) : 1;
895 g_free(string);
896 return ret;
898 #endif
900 /* Returns the onscreen width of the character at the position */
901 static int
902 reverse_char(WINDOW *d, int y, int x, gboolean set)
904 #define DECIDE(ch) (set ? ((ch) | A_REVERSE) : ((ch) & ~A_REVERSE))
906 #ifdef NO_WIDECHAR
907 chtype ch;
908 ch = mvwinch(d, y, x);
909 mvwaddch(d, y, x, DECIDE(ch));
910 return 1;
911 #else
912 cchar_t ch;
913 int wc = 1;
914 if (mvwin_wch(d, y, x, &ch) == OK) {
915 wc = widestringwidth(ch.chars);
916 ch.attr = DECIDE(ch.attr);
917 ch.attr &= WA_ATTRIBUTES; /* XXX: This is a workaround for a bug */
918 mvwadd_wch(d, y, x, &ch);
921 return wc;
922 #endif
925 static void
926 window_reverse(GntWidget *win, gboolean set, GntWM *wm)
928 int i;
929 int w, h;
930 WINDOW *d;
932 if (GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_NO_BORDER))
933 return;
935 d = win->window;
936 gnt_widget_get_size(win, &w, &h);
938 if (gnt_widget_has_shadow(win)) {
939 --w;
940 --h;
943 /* the top and bottom */
944 for (i = 0; i < w; i += reverse_char(d, 0, i, set));
945 for (i = 0; i < w; i += reverse_char(d, h-1, i, set));
947 /* the left and right */
948 for (i = 0; i < h; i += reverse_char(d, i, 0, set));
949 for (i = 0; i < h; i += reverse_char(d, i, w-1, set));
951 gnt_wm_copy_win(win, g_hash_table_lookup(wm->nodes, win));
952 update_screen(wm);
955 static gboolean
956 start_move(GntBindable *bindable, GList *null)
958 GntWM *wm = GNT_WM(bindable);
959 if (wm->_list.window || wm->menu)
960 return TRUE;
961 if (!wm->cws->ordered)
962 return TRUE;
964 wm->mode = GNT_KP_MODE_MOVE;
965 window_reverse(GNT_WIDGET(wm->cws->ordered->data), TRUE, wm);
967 return TRUE;
970 static gboolean
971 start_resize(GntBindable *bindable, GList *null)
973 GntWM *wm = GNT_WM(bindable);
974 if (wm->_list.window || wm->menu)
975 return TRUE;
976 if (!wm->cws->ordered)
977 return TRUE;
979 wm->mode = GNT_KP_MODE_RESIZE;
980 window_reverse(GNT_WIDGET(wm->cws->ordered->data), TRUE, wm);
982 return TRUE;
985 static gboolean
986 wm_quit(GntBindable *bindable, GList *list)
988 GntWM *wm = GNT_WM(bindable);
989 if (write_timeout)
990 write_already(wm);
991 g_main_loop_quit(wm->loop);
992 return TRUE;
995 static gboolean
996 return_true(GntWM *wm, GntWidget *w, int *a, int *b)
998 return TRUE;
1001 static gboolean
1002 refresh_screen(GntBindable *bindable, GList *null)
1004 GntWM *wm = GNT_WM(bindable);
1006 endwin();
1008 g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL);
1009 refresh();
1010 g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0);
1011 update_screen(wm);
1012 gnt_ws_draw_taskbar(wm->cws, TRUE);
1013 curs_set(0); /* endwin resets the cursor to normal */
1015 return TRUE;
1018 static gboolean
1019 toggle_clipboard(GntBindable *bindable, GList *n)
1021 static GntWidget *clip;
1022 gchar *text;
1023 int maxx, maxy;
1024 if (clip) {
1025 gnt_widget_destroy(clip);
1026 clip = NULL;
1027 return TRUE;
1029 getmaxyx(stdscr, maxy, maxx);
1030 text = gnt_get_clipboard_string();
1031 clip = gnt_hwindow_new(FALSE);
1032 GNT_WIDGET_SET_FLAGS(clip, GNT_WIDGET_TRANSIENT);
1033 GNT_WIDGET_SET_FLAGS(clip, GNT_WIDGET_NO_BORDER);
1034 gnt_box_set_pad(GNT_BOX(clip), 0);
1035 gnt_box_add_widget(GNT_BOX(clip), gnt_label_new(" "));
1036 gnt_box_add_widget(GNT_BOX(clip), gnt_label_new(text));
1037 gnt_box_add_widget(GNT_BOX(clip), gnt_label_new(" "));
1038 gnt_widget_set_position(clip, 0, 0);
1039 gnt_widget_draw(clip);
1040 g_free(text);
1041 return TRUE;
1044 static void remove_tag(gpointer wid, gpointer wim)
1046 GntWM *wm = GNT_WM(wim);
1047 GntWidget *w = GNT_WIDGET(wid);
1048 wm->tagged = g_list_remove(wm->tagged, w);
1049 mvwhline(w->window, 0, 1, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL), 3);
1050 gnt_widget_draw(w);
1053 static gboolean
1054 tag_widget(GntBindable *b, GList *params)
1056 GntWM *wm = GNT_WM(b);
1057 GntWidget *widget;
1059 if (!wm->cws->ordered)
1060 return FALSE;
1061 widget = wm->cws->ordered->data;
1063 if (g_list_find(wm->tagged, widget)) {
1064 remove_tag(widget, wm);
1065 return TRUE;
1068 wm->tagged = g_list_prepend(wm->tagged, widget);
1069 wbkgdset(widget->window, ' ' | gnt_color_pair(GNT_COLOR_HIGHLIGHT));
1070 mvwprintw(widget->window, 0, 1, "[T]");
1071 gnt_widget_draw(widget);
1072 return TRUE;
1075 static void
1076 widget_move_ws(gpointer wid, gpointer w)
1078 GntWM *wm = GNT_WM(w);
1079 gnt_wm_widget_move_workspace(wm, wm->cws, GNT_WIDGET(wid));
1082 static gboolean
1083 place_tagged(GntBindable *b, GList *params)
1085 GntWM *wm = GNT_WM(b);
1086 g_list_foreach(wm->tagged, widget_move_ws, wm);
1087 g_list_foreach(wm->tagged, remove_tag, wm);
1088 g_list_free(wm->tagged);
1089 wm->tagged = NULL;
1090 return TRUE;
1093 static gboolean
1094 workspace_list(GntBindable *b, GList *params)
1096 GntWM *wm = GNT_WM(b);
1098 if (wm->_list.window || wm->menu)
1099 return TRUE;
1101 list_of_windows(wm, TRUE);
1103 return TRUE;
1106 static gboolean
1107 workspace_new(GntBindable *bindable, GList *null)
1109 GntWM *wm = GNT_WM(bindable);
1110 GntWS *ws = gnt_ws_new(NULL);
1111 gnt_wm_add_workspace(wm, ws);
1112 gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws));
1113 return TRUE;
1116 static gboolean
1117 ignore_keys_start(GntBindable *bindable, GList *n)
1119 GntWM *wm = GNT_WM(bindable);
1121 if(!wm->menu && !wm->_list.window && wm->mode == GNT_KP_MODE_NORMAL){
1122 ignore_keys = TRUE;
1123 return TRUE;
1125 return FALSE;
1128 static gboolean
1129 ignore_keys_end(GntBindable *bindable, GList *n)
1131 return ignore_keys ? !(ignore_keys = FALSE) : FALSE;
1134 static gboolean
1135 help_for_bindable(GntWM *wm, GntBindable *bindable)
1137 gboolean ret = TRUE;
1138 GntBindableClass *klass = GNT_BINDABLE_GET_CLASS(bindable);
1140 if (klass->help_window) {
1141 gnt_wm_raise_window(wm, GNT_WIDGET(klass->help_window));
1142 } else {
1143 ret = gnt_bindable_build_help_window(bindable);
1145 return ret;
1148 static gboolean
1149 help_for_wm(GntBindable *bindable, GList *null)
1151 return help_for_bindable(GNT_WM(bindable),bindable);
1154 static gboolean
1155 help_for_window(GntBindable *bindable, GList *null)
1157 GntWM *wm = GNT_WM(bindable);
1158 GntWidget *widget;
1160 if(!wm->cws->ordered)
1161 return FALSE;
1163 widget = wm->cws->ordered->data;
1165 return help_for_bindable(wm,GNT_BINDABLE(widget));
1168 static gboolean
1169 help_for_widget(GntBindable *bindable, GList *null)
1171 GntWM *wm = GNT_WM(bindable);
1172 GntWidget *widget;
1174 if (!wm->cws->ordered)
1175 return TRUE;
1177 widget = wm->cws->ordered->data;
1178 if (!GNT_IS_BOX(widget))
1179 return TRUE;
1181 return help_for_bindable(wm, GNT_BINDABLE(GNT_BOX(widget)->active));
1184 static void
1185 accumulate_windows(gpointer window, gpointer node, gpointer p)
1187 GList *list = *(GList**)p;
1188 list = g_list_prepend(list, window);
1189 *(GList**)p = list;
1192 static void
1193 gnt_wm_destroy(GObject *obj)
1195 GntWM *wm = GNT_WM(obj);
1196 GList *list = NULL;
1197 g_hash_table_foreach(wm->nodes, accumulate_windows, &list);
1198 g_list_foreach(list, (GFunc)gnt_widget_destroy, NULL);
1199 g_list_free(list);
1200 g_hash_table_destroy(wm->nodes);
1201 wm->nodes = NULL;
1203 while (wm->workspaces) {
1204 g_object_unref(wm->workspaces->data);
1205 wm->workspaces = g_list_delete_link(wm->workspaces, wm->workspaces);
1209 static void
1210 gnt_wm_class_init(GntWMClass *klass)
1212 int i;
1213 GObjectClass *gclass = G_OBJECT_CLASS(klass);
1215 gclass->dispose = gnt_wm_destroy;
1217 klass->new_window = gnt_wm_new_window_real;
1218 klass->decorate_window = NULL;
1219 klass->close_window = NULL;
1220 klass->window_resize_confirm = return_true;
1221 klass->window_resized = gnt_wm_win_resized;
1222 klass->window_move_confirm = return_true;
1223 klass->window_moved = gnt_wm_win_moved;
1224 klass->window_update = NULL;
1225 klass->key_pressed = NULL;
1226 klass->mouse_clicked = NULL;
1227 klass->give_focus = gnt_wm_give_focus;
1229 signals[SIG_NEW_WIN] =
1230 g_signal_new("new_win",
1231 G_TYPE_FROM_CLASS(klass),
1232 G_SIGNAL_RUN_LAST,
1233 G_STRUCT_OFFSET(GntWMClass, new_window),
1234 NULL, NULL,
1235 g_cclosure_marshal_VOID__POINTER,
1236 G_TYPE_NONE, 1, G_TYPE_POINTER);
1237 signals[SIG_DECORATE_WIN] =
1238 g_signal_new("decorate_win",
1239 G_TYPE_FROM_CLASS(klass),
1240 G_SIGNAL_RUN_LAST,
1241 G_STRUCT_OFFSET(GntWMClass, decorate_window),
1242 NULL, NULL,
1243 g_cclosure_marshal_VOID__POINTER,
1244 G_TYPE_NONE, 1, G_TYPE_POINTER);
1245 signals[SIG_CLOSE_WIN] =
1246 g_signal_new("close_win",
1247 G_TYPE_FROM_CLASS(klass),
1248 G_SIGNAL_RUN_LAST,
1249 G_STRUCT_OFFSET(GntWMClass, close_window),
1250 NULL, NULL,
1251 g_cclosure_marshal_VOID__POINTER,
1252 G_TYPE_NONE, 1, G_TYPE_POINTER);
1253 signals[SIG_CONFIRM_RESIZE] =
1254 g_signal_new("confirm_resize",
1255 G_TYPE_FROM_CLASS(klass),
1256 G_SIGNAL_RUN_LAST,
1257 G_STRUCT_OFFSET(GntWMClass, window_resize_confirm),
1258 gnt_boolean_handled_accumulator, NULL,
1259 gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER,
1260 G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
1262 signals[SIG_CONFIRM_MOVE] =
1263 g_signal_new("confirm_move",
1264 G_TYPE_FROM_CLASS(klass),
1265 G_SIGNAL_RUN_LAST,
1266 G_STRUCT_OFFSET(GntWMClass, window_move_confirm),
1267 gnt_boolean_handled_accumulator, NULL,
1268 gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER,
1269 G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
1271 signals[SIG_RESIZED] =
1272 g_signal_new("window_resized",
1273 G_TYPE_FROM_CLASS(klass),
1274 G_SIGNAL_RUN_LAST,
1275 G_STRUCT_OFFSET(GntWMClass, window_resized),
1276 NULL, NULL,
1277 g_cclosure_marshal_VOID__POINTER,
1278 G_TYPE_NONE, 1, G_TYPE_POINTER);
1279 signals[SIG_MOVED] =
1280 g_signal_new("window_moved",
1281 G_TYPE_FROM_CLASS(klass),
1282 G_SIGNAL_RUN_LAST,
1283 G_STRUCT_OFFSET(GntWMClass, window_moved),
1284 NULL, NULL,
1285 g_cclosure_marshal_VOID__POINTER,
1286 G_TYPE_NONE, 1, G_TYPE_POINTER);
1287 signals[SIG_UPDATE_WIN] =
1288 g_signal_new("window_update",
1289 G_TYPE_FROM_CLASS(klass),
1290 G_SIGNAL_RUN_LAST,
1291 G_STRUCT_OFFSET(GntWMClass, window_update),
1292 NULL, NULL,
1293 g_cclosure_marshal_VOID__POINTER,
1294 G_TYPE_NONE, 1, G_TYPE_POINTER);
1296 signals[SIG_GIVE_FOCUS] =
1297 g_signal_new("give_focus",
1298 G_TYPE_FROM_CLASS(klass),
1299 G_SIGNAL_RUN_LAST,
1300 G_STRUCT_OFFSET(GntWMClass, give_focus),
1301 NULL, NULL,
1302 g_cclosure_marshal_VOID__POINTER,
1303 G_TYPE_NONE, 1, G_TYPE_POINTER);
1305 signals[SIG_MOUSE_CLICK] =
1306 g_signal_new("mouse_clicked",
1307 G_TYPE_FROM_CLASS(klass),
1308 G_SIGNAL_RUN_LAST,
1309 G_STRUCT_OFFSET(GntWMClass, mouse_clicked),
1310 gnt_boolean_handled_accumulator, NULL,
1311 gnt_closure_marshal_BOOLEAN__INT_INT_INT_POINTER,
1312 G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER);
1314 signals[SIG_TERMINAL_REFRESH] =
1315 g_signal_new("terminal-refresh",
1316 G_TYPE_FROM_CLASS(klass),
1317 G_SIGNAL_RUN_LAST,
1318 G_STRUCT_OFFSET(GntWMClass, terminal_refresh),
1319 NULL, NULL,
1320 g_cclosure_marshal_VOID__VOID,
1321 G_TYPE_NONE, 0);
1323 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next", window_next,
1324 "\033" "n", NULL);
1325 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev", window_prev,
1326 "\033" "p", NULL);
1327 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-close", window_close,
1328 "\033" "c", NULL);
1329 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-list", window_list,
1330 "\033" "w", NULL);
1331 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "dump-screen", dump_screen,
1332 "\033" "d", NULL);
1333 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-left", shift_left,
1334 "\033" ",", NULL);
1335 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-right", shift_right,
1336 "\033" ".", NULL);
1337 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "action-list", list_actions,
1338 "\033" "a", NULL);
1339 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-move", start_move,
1340 "\033" "m", NULL);
1341 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-resize", start_resize,
1342 "\033" "r", NULL);
1343 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "wm-quit", wm_quit,
1344 "\033" "q", NULL);
1345 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "refresh-screen", refresh_screen,
1346 "\033" "l", NULL);
1347 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "switch-window-n", switch_window_n,
1348 NULL, NULL);
1349 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-scroll-down", window_scroll_down,
1350 "\033" GNT_KEY_CTRL_J, NULL);
1351 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-scroll-up", window_scroll_up,
1352 "\033" GNT_KEY_CTRL_K, NULL);
1353 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-widget", help_for_widget,
1354 "\033" "/", NULL);
1355 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-new", workspace_new,
1356 GNT_KEY_F9, NULL);
1357 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-next", workspace_next,
1358 "\033" ">", NULL);
1359 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-prev", workspace_prev,
1360 "\033" "<", NULL);
1361 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-tag", tag_widget,
1362 "\033" "t", NULL);
1363 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "place-tagged", place_tagged,
1364 "\033" "T", NULL);
1365 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "workspace-list", workspace_list,
1366 "\033" "s", NULL);
1367 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "toggle-clipboard", toggle_clipboard,
1368 "\033" "C", NULL);
1369 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-wm", help_for_wm,
1370 "\033" "\\", NULL);
1371 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "help-for-window", help_for_window,
1372 "\033" "|", NULL);
1373 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-start", ignore_keys_start,
1374 GNT_KEY_CTRL_G, NULL);
1375 gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "ignore-keys-end", ignore_keys_end,
1376 "\033" GNT_KEY_CTRL_G, NULL);
1378 gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
1380 /* Make sure Alt+x are detected properly. */
1381 for (i = '0'; i <= '9'; i++) {
1382 char str[] = "\033X";
1383 str[1] = i;
1384 gnt_keys_add_combination(str);
1387 GNTDEBUG;
1390 /******************************************************************************
1391 * GntWM API
1392 *****************************************************************************/
1393 GType
1394 gnt_wm_get_gtype(void)
1396 static GType type = 0;
1398 if(type == 0) {
1399 static const GTypeInfo info = {
1400 sizeof(GntWMClass),
1401 NULL, /* base_init */
1402 NULL, /* base_finalize */
1403 (GClassInitFunc)gnt_wm_class_init,
1404 NULL,
1405 NULL, /* class_data */
1406 sizeof(GntWM),
1407 0, /* n_preallocs */
1408 gnt_wm_init, /* instance_init */
1409 NULL /* value_table */
1412 type = g_type_register_static(GNT_TYPE_BINDABLE,
1413 "GntWM",
1414 &info, 0);
1417 return type;
1420 void
1421 gnt_wm_add_workspace(GntWM *wm, GntWS *ws)
1423 wm->workspaces = g_list_append(wm->workspaces, ws);
1426 gboolean
1427 gnt_wm_switch_workspace(GntWM *wm, gint n)
1429 GntWS *s = g_list_nth_data(wm->workspaces, n);
1430 if (!s)
1431 return FALSE;
1433 if (wm->_list.window) {
1434 gnt_widget_destroy(wm->_list.window);
1436 gnt_ws_hide(wm->cws, wm->nodes);
1437 wm->cws = s;
1438 gnt_ws_show(wm->cws, wm->nodes);
1440 gnt_ws_draw_taskbar(wm->cws, TRUE);
1441 update_screen(wm);
1442 if (wm->cws->ordered) {
1443 gnt_wm_raise_window(wm, wm->cws->ordered->data);
1446 if (act && g_list_find(act, wm->cws)) {
1447 act = g_list_remove(act, wm->cws);
1448 update_act_msg();
1450 return TRUE;
1453 gboolean
1454 gnt_wm_switch_workspace_prev(GntWM *wm)
1456 int n = g_list_index(wm->workspaces, wm->cws);
1457 return gnt_wm_switch_workspace(wm, --n);
1460 gboolean
1461 gnt_wm_switch_workspace_next(GntWM *wm)
1463 int n = g_list_index(wm->workspaces, wm->cws);
1464 return gnt_wm_switch_workspace(wm, ++n);
1467 static gboolean
1468 workspace_next(GntBindable *wm, GList *n)
1470 return gnt_wm_switch_workspace_next(GNT_WM(wm));
1473 static gboolean
1474 workspace_prev(GntBindable *wm, GList *n)
1476 return gnt_wm_switch_workspace_prev(GNT_WM(wm));
1479 void
1480 gnt_wm_widget_move_workspace(GntWM *wm, GntWS *neww, GntWidget *widget)
1482 GntWS *oldw = gnt_wm_widget_find_workspace(wm, widget);
1483 GntNode *node;
1484 if (!oldw || oldw == neww)
1485 return;
1486 node = g_hash_table_lookup(wm->nodes, widget);
1487 if (node && node->ws == neww)
1488 return;
1490 if (node)
1491 node->ws = neww;
1493 gnt_ws_remove_widget(oldw, widget);
1494 gnt_ws_add_widget(neww, widget);
1495 if (neww == wm->cws) {
1496 gnt_ws_widget_show(widget, wm->nodes);
1497 } else {
1498 gnt_ws_widget_hide(widget, wm->nodes);
1502 static gint widget_in_workspace(gconstpointer workspace, gconstpointer wid)
1504 GntWS *s = (GntWS *)workspace;
1505 if (s->list && g_list_find(s->list, wid))
1506 return 0;
1507 return 1;
1510 GntWS *gnt_wm_widget_find_workspace(GntWM *wm, GntWidget *widget)
1512 GList *l = g_list_find_custom(wm->workspaces, widget, widget_in_workspace);
1513 if (l)
1514 return l->data;
1515 return NULL;
1518 static void free_workspaces(gpointer data, gpointer n)
1520 GntWS *s = data;
1521 g_free(s->name);
1524 void gnt_wm_set_workspaces(GntWM *wm, GList *workspaces)
1526 g_list_foreach(wm->workspaces, free_workspaces, NULL);
1527 wm->workspaces = workspaces;
1528 gnt_wm_switch_workspace(wm, 0);
1531 static void
1532 update_window_in_list(GntWM *wm, GntWidget *wid)
1534 GntTextFormatFlags flag = 0;
1536 if (wm->windows == NULL)
1537 return;
1539 if (wm->cws->ordered && wid == wm->cws->ordered->data)
1540 flag |= GNT_TEXT_FLAG_DIM;
1541 else if (GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT))
1542 flag |= GNT_TEXT_FLAG_BOLD;
1544 gnt_tree_set_row_flags(GNT_TREE(wm->windows->tree), wid, flag);
1547 static gboolean
1548 match_title(gpointer title, gpointer n, gpointer wid_title)
1550 /* XXX: do any regex magic here. */
1551 if (g_strrstr((gchar *)wid_title, (gchar *)title))
1552 return TRUE;
1553 return FALSE;
1556 #if !GLIB_CHECK_VERSION(2,4,0)
1557 struct
1559 gpointer data;
1560 gpointer value;
1561 } table_find_data;
1563 static void
1564 table_find_helper(gpointer key, gpointer value, gpointer data)
1566 GHRFunc func = data;
1567 if (func(key, value, table_find_data.data))
1568 table_find_data.value = value;
1571 static gpointer
1572 g_hash_table_find(GHashTable * table, GHRFunc func, gpointer data)
1574 table_find_data.data = data;
1575 table_find_data.value = NULL;
1576 g_hash_table_foreach(table, table_find_helper, func);
1577 return table_find_data.value;
1579 #endif
1581 static GntWS *
1582 new_widget_find_workspace(GntWM *wm, GntWidget *widget)
1584 GntWS *ret = NULL;
1585 const gchar *name, *title;
1586 title = GNT_BOX(widget)->title;
1587 if (title)
1588 ret = g_hash_table_find(wm->title_places, match_title, (gpointer)title);
1589 if (ret)
1590 return ret;
1591 name = gnt_widget_get_name(widget);
1592 if (name)
1593 ret = g_hash_table_find(wm->name_places, match_title, (gpointer)name);
1594 return ret ? ret : wm->cws;
1597 static void
1598 gnt_wm_new_window_real(GntWM *wm, GntWidget *widget)
1600 GntNode *node;
1601 gboolean transient = FALSE;
1603 if (widget->window == NULL)
1604 return;
1606 node = g_new0(GntNode, 1);
1607 node->me = widget;
1608 node->scroll = 0;
1610 g_hash_table_replace(wm->nodes, widget, node);
1612 refresh_node(widget, node, NULL);
1614 transient = !!GNT_WIDGET_IS_FLAG_SET(node->me, GNT_WIDGET_TRANSIENT);
1616 #if 1
1618 int x, y, w, h, maxx, maxy;
1619 gboolean shadow = TRUE;
1621 if (!gnt_widget_has_shadow(widget))
1622 shadow = FALSE;
1623 x = widget->priv.x;
1624 y = widget->priv.y;
1625 w = widget->priv.width;
1626 h = widget->priv.height;
1628 getmaxyx(stdscr, maxy, maxx);
1629 maxy -= 1; /* room for the taskbar */
1630 maxy -= shadow;
1631 maxx -= shadow;
1633 x = MAX(0, x);
1634 y = MAX(0, y);
1635 if (x + w >= maxx)
1636 x = MAX(0, maxx - w);
1637 if (y + h >= maxy)
1638 y = MAX(0, maxy - h);
1640 w = MIN(w, maxx);
1641 h = MIN(h, maxy);
1642 node->window = newwin(h + shadow, w + shadow, y, x);
1643 gnt_wm_copy_win(widget, node);
1645 #endif
1647 node->panel = new_panel(node->window);
1648 set_panel_userptr(node->panel, node);
1650 if (!transient) {
1651 GntWS *ws = wm->cws;
1652 if (node->me != wm->_list.window) {
1653 if (GNT_IS_BOX(widget)) {
1654 ws = new_widget_find_workspace(wm, widget);
1656 node->ws = ws;
1657 ws->list = g_list_append(ws->list, widget);
1658 ws->ordered = g_list_append(ws->ordered, widget);
1661 if (wm->event_stack || node->me == wm->_list.window ||
1662 node->me == ws->ordered->data) {
1663 gnt_wm_raise_window(wm, node->me);
1664 } else {
1665 bottom_panel(node->panel); /* New windows should not grab focus */
1666 gnt_widget_set_focus(node->me, FALSE);
1667 gnt_widget_set_urgent(node->me);
1668 if (wm->cws != ws)
1669 gnt_ws_widget_hide(widget, wm->nodes);
1674 void gnt_wm_new_window(GntWM *wm, GntWidget *widget)
1676 while (widget->parent)
1677 widget = widget->parent;
1679 if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_INVISIBLE) ||
1680 g_hash_table_lookup(wm->nodes, widget)) {
1681 update_screen(wm);
1682 return;
1685 if (GNT_IS_BOX(widget)) {
1686 const char *title = GNT_BOX(widget)->title;
1687 GntPosition *p = NULL;
1688 if (title && (p = g_hash_table_lookup(wm->positions, title)) != NULL) {
1689 sanitize_position(widget, &p->x, &p->y);
1690 gnt_widget_set_position(widget, p->x, p->y);
1691 mvwin(widget->window, p->y, p->x);
1695 g_signal_emit(wm, signals[SIG_NEW_WIN], 0, widget);
1696 g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget);
1698 if (wm->windows && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
1699 if ((GNT_IS_BOX(widget) && GNT_BOX(widget)->title) && wm->_list.window != widget
1700 && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS)) {
1701 gnt_tree_add_row_last(GNT_TREE(wm->windows->tree), widget,
1702 gnt_tree_create_row(GNT_TREE(wm->windows->tree), GNT_BOX(widget)->title),
1703 g_object_get_data(G_OBJECT(wm->windows->tree), "workspace") ? wm->cws : NULL);
1704 update_window_in_list(wm, widget);
1708 update_screen(wm);
1709 gnt_ws_draw_taskbar(wm->cws, FALSE);
1712 void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget)
1714 g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget);
1717 void gnt_wm_window_close(GntWM *wm, GntWidget *widget)
1719 GntWS *s;
1720 int pos;
1722 s = gnt_wm_widget_find_workspace(wm, widget);
1724 if (g_hash_table_lookup(wm->nodes, widget) == NULL)
1725 return;
1727 g_signal_emit(wm, signals[SIG_CLOSE_WIN], 0, widget);
1728 g_hash_table_remove(wm->nodes, widget);
1730 if (wm->windows) {
1731 gnt_tree_remove(GNT_TREE(wm->windows->tree), widget);
1734 if (s) {
1735 pos = g_list_index(s->list, widget);
1737 if (pos != -1) {
1738 s->list = g_list_remove(s->list, widget);
1739 s->ordered = g_list_remove(s->ordered, widget);
1741 if (s->ordered && wm->cws == s)
1742 gnt_wm_raise_window(wm, s->ordered->data);
1746 update_screen(wm);
1747 gnt_ws_draw_taskbar(wm->cws, FALSE);
1750 time_t gnt_wm_get_idle_time()
1752 return time(NULL) - last_active_time;
1755 gboolean gnt_wm_process_input(GntWM *wm, const char *keys)
1757 gboolean ret = FALSE;
1759 keys = gnt_bindable_remap_keys(GNT_BINDABLE(wm), keys);
1761 idle_update = TRUE;
1762 if(ignore_keys){
1763 if(keys && !strcmp(keys, "\033" GNT_KEY_CTRL_G)){
1764 if(gnt_bindable_perform_action_key(GNT_BINDABLE(wm), keys)){
1765 return TRUE;
1768 return wm->cws->ordered ? gnt_widget_key_pressed(GNT_WIDGET(wm->cws->ordered->data), keys) : FALSE;
1771 if (gnt_bindable_perform_action_key(GNT_BINDABLE(wm), keys)) {
1772 return TRUE;
1775 /* Do some manual checking */
1776 if (wm->cws->ordered && wm->mode != GNT_KP_MODE_NORMAL) {
1777 int xmin = 0, ymin = 0, xmax = getmaxx(stdscr), ymax = getmaxy(stdscr) - 1;
1778 int x, y, w, h;
1779 GntWidget *widget = GNT_WIDGET(wm->cws->ordered->data);
1780 int ox, oy, ow, oh;
1782 gnt_widget_get_position(widget, &x, &y);
1783 gnt_widget_get_size(widget, &w, &h);
1784 ox = x; oy = y;
1785 ow = w; oh = h;
1787 if (wm->mode == GNT_KP_MODE_MOVE) {
1788 if (strcmp(keys, GNT_KEY_LEFT) == 0) {
1789 if (x > xmin)
1790 x--;
1791 } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) {
1792 if (x + w < xmax)
1793 x++;
1794 } else if (strcmp(keys, GNT_KEY_UP) == 0) {
1795 if (y > ymin)
1796 y--;
1797 } else if (strcmp(keys, GNT_KEY_DOWN) == 0) {
1798 if (y + h < ymax)
1799 y++;
1801 if (ox != x || oy != y) {
1802 gnt_screen_move_widget(widget, x, y);
1803 window_reverse(widget, TRUE, wm);
1804 return TRUE;
1806 } else if (wm->mode == GNT_KP_MODE_RESIZE) {
1807 if (strcmp(keys, GNT_KEY_LEFT) == 0) {
1808 w--;
1809 } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) {
1810 if (x + w < xmax)
1811 w++;
1812 } else if (strcmp(keys, GNT_KEY_UP) == 0) {
1813 h--;
1814 } else if (strcmp(keys, GNT_KEY_DOWN) == 0) {
1815 if (y + h < ymax)
1816 h++;
1818 if (oh != h || ow != w) {
1819 gnt_screen_resize_widget(widget, w, h);
1820 window_reverse(widget, TRUE, wm);
1821 return TRUE;
1824 if (strcmp(keys, "\r") == 0 || strcmp(keys, "\033") == 0) {
1825 window_reverse(widget, FALSE, wm);
1826 wm->mode = GNT_KP_MODE_NORMAL;
1828 return TRUE;
1831 /* Escape to close the window-list or action-list window */
1832 if (strcmp(keys, "\033") == 0) {
1833 if (wm->_list.window) {
1834 gnt_widget_destroy(wm->_list.window);
1835 return TRUE;
1837 } else if (keys[0] == '\033' && isdigit(keys[1]) && keys[2] == '\0') {
1838 /* Alt+x for quick switch */
1839 int n = *(keys + 1) - '0';
1840 GList *list = NULL;
1842 if (n == 0)
1843 n = 10;
1845 list = g_list_append(list, GINT_TO_POINTER(n - 1));
1846 switch_window_n(GNT_BINDABLE(wm), list);
1847 g_list_free(list);
1848 return TRUE;
1851 if (wm->menu)
1852 ret = gnt_widget_key_pressed(GNT_WIDGET(wm->menu), keys);
1853 else if (wm->_list.window)
1854 ret = gnt_widget_key_pressed(wm->_list.window, keys);
1855 else if (wm->cws->ordered) {
1856 GntWidget *win = wm->cws->ordered->data;
1857 if (GNT_IS_WINDOW(win)) {
1858 GntMenu *menu = GNT_WINDOW(win)->menu;
1859 if (menu) {
1860 const char *id = gnt_window_get_accel_item(GNT_WINDOW(win), keys);
1861 if (id)
1862 ret = (gnt_menu_get_item(menu, id) != NULL);
1865 if (!ret)
1866 ret = gnt_widget_key_pressed(win, keys);
1868 return ret;
1871 static void
1872 gnt_wm_win_resized(GntWM *wm, GntNode *node)
1874 /*refresh_node(node->me, node, NULL);*/
1877 static void
1878 gnt_wm_win_moved(GntWM *wm, GntNode *node)
1880 refresh_node(node->me, node, NULL);
1883 void gnt_wm_resize_window(GntWM *wm, GntWidget *widget, int width, int height)
1885 gboolean ret = TRUE;
1886 GntNode *node;
1887 int shadow;
1888 int maxx, maxy;
1890 while (widget->parent)
1891 widget = widget->parent;
1892 node = g_hash_table_lookup(wm->nodes, widget);
1893 if (!node)
1894 return;
1896 g_signal_emit(wm, signals[SIG_CONFIRM_RESIZE], 0, widget, &width, &height, &ret);
1897 if (!ret)
1898 return; /* resize is not permitted */
1899 hide_panel(node->panel);
1900 gnt_widget_set_size(widget, width, height);
1901 gnt_widget_draw(widget);
1903 shadow = gnt_widget_has_shadow(widget) ? 1 : 0;
1904 maxx = getmaxx(stdscr) - shadow;
1905 maxy = getmaxy(stdscr) - 1 - shadow;
1906 height = MIN(height, maxy);
1907 width = MIN(width, maxx);
1908 wresize(node->window, height, width);
1909 replace_panel(node->panel, node->window);
1911 g_signal_emit(wm, signals[SIG_RESIZED], 0, node);
1913 show_panel(node->panel);
1914 update_screen(wm);
1917 static void
1918 write_gdi(gpointer key, gpointer value, gpointer data)
1920 GntPosition *p = value;
1921 fprintf(data, ".%s = %d;%d\n", (char *)key, p->x, p->y);
1924 static gboolean
1925 write_already(gpointer data)
1927 GntWM *wm = data;
1928 FILE *file;
1929 char *filename;
1931 filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL);
1933 file = fopen(filename, "wb");
1934 if (file == NULL) {
1935 g_printerr("GntWM: error opening file to save positions\n");
1936 } else {
1937 fprintf(file, "[positions]\n");
1938 g_hash_table_foreach(wm->positions, write_gdi, file);
1939 fclose(file);
1942 g_free(filename);
1943 g_source_remove(write_timeout);
1944 write_timeout = 0;
1945 return FALSE;
1948 static void
1949 write_positions_to_file(GntWM *wm)
1951 if (write_timeout) {
1952 g_source_remove(write_timeout);
1954 write_timeout = g_timeout_add(10000, write_already, wm);
1957 void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y)
1959 gboolean ret = TRUE;
1960 GntNode *node;
1962 while (widget->parent)
1963 widget = widget->parent;
1964 node = g_hash_table_lookup(wm->nodes, widget);
1965 if (!node)
1966 return;
1968 g_signal_emit(wm, signals[SIG_CONFIRM_MOVE], 0, widget, &x, &y, &ret);
1969 if (!ret)
1970 return; /* resize is not permitted */
1972 gnt_widget_set_position(widget, x, y);
1973 move_panel(node->panel, y, x);
1975 g_signal_emit(wm, signals[SIG_MOVED], 0, node);
1976 if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE) && GNT_IS_BOX(widget) &&
1977 !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
1978 const char *title = GNT_BOX(widget)->title;
1979 if (title) {
1980 GntPosition *p = g_new0(GntPosition, 1);
1981 GntWidget *wid = node->me;
1982 p->x = wid->priv.x;
1983 p->y = wid->priv.y;
1984 g_hash_table_replace(wm->positions, g_strdup(title), p);
1985 write_positions_to_file(wm);
1989 update_screen(wm);
1992 static void
1993 gnt_wm_give_focus(GntWM *wm, GntWidget *widget)
1995 GntNode *node = g_hash_table_lookup(wm->nodes, widget);
1997 if (!node)
1998 return;
2000 if (widget != wm->_list.window && !GNT_IS_MENU(widget) &&
2001 wm->cws->ordered->data != widget) {
2002 GntWidget *w = wm->cws->ordered->data;
2003 wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget);
2004 gnt_widget_set_focus(w, FALSE);
2007 gnt_widget_set_focus(widget, TRUE);
2008 GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_URGENT);
2009 gnt_widget_draw(widget);
2010 top_panel(node->panel);
2012 if (wm->_list.window) {
2013 GntNode *nd = g_hash_table_lookup(wm->nodes, wm->_list.window);
2014 top_panel(nd->panel);
2016 update_screen(wm);
2017 gnt_ws_draw_taskbar(wm->cws, FALSE);
2020 void gnt_wm_update_window(GntWM *wm, GntWidget *widget)
2022 GntNode *node = NULL;
2023 GntWS *ws;
2025 while (widget->parent)
2026 widget = widget->parent;
2027 if (!GNT_IS_MENU(widget)) {
2028 if (!GNT_IS_BOX(widget))
2029 return;
2030 gnt_box_sync_children(GNT_BOX(widget));
2033 ws = gnt_wm_widget_find_workspace(wm, widget);
2034 node = g_hash_table_lookup(wm->nodes, widget);
2035 if (node == NULL) {
2036 gnt_wm_new_window(wm, widget);
2037 } else
2038 g_signal_emit(wm, signals[SIG_UPDATE_WIN], 0, node);
2040 if (ws == wm->cws || GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
2041 gnt_wm_copy_win(widget, node);
2042 update_screen(wm);
2043 gnt_ws_draw_taskbar(wm->cws, FALSE);
2044 } else if (ws && ws != wm->cws && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_URGENT)) {
2045 if (!act || (act && !g_list_find(act, ws)))
2046 act = g_list_prepend(act, ws);
2047 update_act_msg();
2051 gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget)
2053 gboolean ret = TRUE;
2054 idle_update = TRUE;
2055 g_signal_emit(wm, signals[SIG_MOUSE_CLICK], 0, event, x, y, widget, &ret);
2056 return ret;
2059 void gnt_wm_raise_window(GntWM *wm, GntWidget *widget)
2061 GntWS *ws = gnt_wm_widget_find_workspace(wm, widget);
2062 if (wm->cws != ws)
2063 gnt_wm_switch_workspace(wm, g_list_index(wm->workspaces, ws));
2064 if (widget != wm->cws->ordered->data) {
2065 GntWidget *wid = wm->cws->ordered->data;
2066 wm->cws->ordered = g_list_bring_to_front(wm->cws->ordered, widget);
2067 gnt_widget_set_focus(wid, FALSE);
2068 gnt_widget_draw(wid);
2070 gnt_widget_set_focus(widget, TRUE);
2071 gnt_widget_draw(widget);
2072 g_signal_emit(wm, signals[SIG_GIVE_FOCUS], 0, widget);
2075 void gnt_wm_set_event_stack(GntWM *wm, gboolean set)
2077 wm->event_stack = set;