Mercurial > pidgin
diff console/libgnt/gntwm.c @ 14900:065e7ac30338
[gaim-migrate @ 17672]
Rearranging a bunch of stuff. Users shouldn't notice any change, apart from
the added ability to bind keys for the window-manager. I will update the manual
in a while.
I need to know how to revert a commit in case things go terribly wrong.
... I am going to remind everyone that Dido is AWESOME!
committer: Tailor Script <tailor@pidgin.im>
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Sun, 05 Nov 2006 17:28:33 +0000 |
parents | |
children | 0512fddbaaf2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntwm.c Sun Nov 05 17:28:33 2006 +0000 @@ -0,0 +1,1170 @@ +#define _GNU_SOURCE +#if defined(__APPLE__) +#define _XOPEN_SOURCE_EXTENDED +#endif + +#include "gntwm.h" +#include "gntstyle.h" +#include "gntmarshal.h" +#include "gnt.h" +#include "gntbox.h" +#include "gntmenu.h" +#include "gnttextview.h" +#include "gnttree.h" +#include "gntutils.h" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +enum +{ + SIG_NEW_WIN, + SIG_DECORATE_WIN, + SIG_CLOSE_WIN, + SIG_CONFIRM_RESIZE, + SIG_RESIZED, + SIG_CONFIRM_MOVE, + SIG_MOVED, + SIG_UPDATE_WIN, + SIG_GIVE_FOCUS, + SIG_KEY_PRESS, + SIG_MOUSE_CLICK, + SIGS +}; + +static guint signals[SIGS] = { 0 }; +static void gnt_wm_new_window_real(GntWM *wm, GntWidget *widget); +static void gnt_wm_win_resized(GntWM *wm, GntNode *node); +static void gnt_wm_win_moved(GntWM *wm, GntNode *node); +static void gnt_wm_give_focus(GntWM *wm, GntWidget *widget); +static void update_window_in_list(GntWM *wm, GntWidget *wid); + +static GList * +g_list_bring_to_front(GList *list, gpointer data) +{ + list = g_list_remove(list, data); + list = g_list_prepend(list, data); + return list; +} + +static void +free_node(gpointer data) +{ + GntNode *node = data; + hide_panel(node->panel); + del_panel(node->panel); + g_free(node); +} + +static void +draw_taskbar(GntWM *wm, gboolean reposition) +{ + static WINDOW *taskbar = NULL; + GList *iter; + int n, width = 0; + int i; + + if (taskbar == NULL) { + taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0); + } else if (reposition) { + int Y_MAX = getmaxy(stdscr) - 1; + mvwin(taskbar, Y_MAX, 0); + } + + wbkgdset(taskbar, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); + werase(taskbar); + + n = g_list_length(wm->list); + if (n) + width = getmaxx(stdscr) / n; + + for (i = 0, iter = wm->list; iter; iter = iter->next, i++) + { + GntWidget *w = iter->data; + int color; + const char *title; + + if (w == wm->ordered->data) { + /* This is the current window in focus */ + color = GNT_COLOR_TITLE; + } else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_URGENT)) { + /* This is a window with the URGENT hint set */ + color = GNT_COLOR_URGENT; + } else { + color = GNT_COLOR_NORMAL; + } + wbkgdset(taskbar, '\0' | COLOR_PAIR(color)); + mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width); + title = GNT_BOX(w)->title; + mvwprintw(taskbar, 0, width * i, "%s", title ? title : "<gnt>"); + if (i) + mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | COLOR_PAIR(GNT_COLOR_NORMAL)); + + update_window_in_list(wm, w); + } + + wrefresh(taskbar); +} +static gboolean +update_screen(GntWM *wm) +{ + if (wm->menu) { + GntMenu *top = wm->menu; + while (top) { + GntNode *node = g_hash_table_lookup(wm->nodes, top); + if (node) + top_panel(node->panel); + top = top->submenu; + } + } + update_panels(); + doupdate(); + return TRUE; +} +static void +refresh_node(GntWidget *widget, GntNode *node, gpointer null) +{ + int x, y, w, h; + int nw, nh, nx, ny; + + int X_MAX = getmaxx(stdscr); + int Y_MAX = getmaxy(stdscr) - 1; + + gnt_widget_get_position(widget, &x, &y); + gnt_widget_get_size(widget, &w, &h); + nx = x; ny = y; + + if (x + w >= X_MAX) + nx = MAX(0, X_MAX - w); + if (y + h >= Y_MAX) + ny = MAX(0, Y_MAX - h); + if (x != nx || y != ny) + gnt_screen_move_widget(widget, nx, ny); + + nw = MIN(w, X_MAX); + nh = MIN(h, Y_MAX); + if (nw != w || nh != h) + gnt_screen_resize_widget(widget, nw, nh); +} +static void +gnt_wm_init(GTypeInstance *instance, gpointer class) +{ + GntWM *wm = GNT_WM(instance); + wm->list = NULL; + wm->ordered = NULL; + wm->event_stack = FALSE; + wm->windows = NULL; + wm->actions = NULL; + wm->nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node); +} + +static void +switch_window(GntWM *wm, int direction) +{ + GntWidget *w = NULL, *wid = NULL; + int pos; + + if (wm->_list.window || wm->menu) + return; + + if (!wm->ordered || !wm->ordered->next) + return; + + w = wm->ordered->data; + pos = g_list_index(wm->list, w); + pos += direction; + + if (pos < 0) + wid = g_list_last(wm->list)->data; + else if (pos >= g_list_length(wm->list)) + wid = wm->list->data; + else if (pos >= 0) + wid = g_list_nth_data(wm->list, pos); + + wm->ordered = g_list_bring_to_front(wm->ordered, wid); + + gnt_wm_raise_window(wm, wm->ordered->data); + + if (w != wid) { + gnt_widget_set_focus(w, FALSE); + } +} + +static gboolean +window_next(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + switch_window(wm, 1); + return TRUE; +} + +static gboolean +window_prev(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + switch_window(wm, -1); + return TRUE; +} + +static gboolean +switch_window_n(GntBindable *bind, GList *list) +{ + GntWM *wm = GNT_WM(bind); + GntWidget *w = NULL; + GList *l; + int n; + + if (!wm->ordered || !list) + return TRUE; + + n = GPOINTER_TO_INT(list->data); + + w = wm->ordered->data; + + if ((l = g_list_nth(wm->list, n)) != NULL) + { + gnt_wm_raise_window(wm, l->data); + } + + if (l && w != l->data) + { + gnt_widget_set_focus(w, FALSE); + } + return TRUE; +} +static gboolean +window_close(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + + if (wm->_list.window) + return TRUE; + + if (wm->ordered) { + gnt_widget_destroy(wm->ordered->data); + } + + return TRUE; +} + +static void +destroy__list(GntWidget *widget, GntWM *wm) +{ + wm->_list.window = NULL; + wm->_list.tree = NULL; + wm->windows = NULL; + wm->actions = NULL; + update_screen(wm); +} + +static void +setup__list(GntWM *wm) +{ + GntWidget *tree, *win; + win = wm->_list.window = gnt_box_new(FALSE, FALSE); + gnt_box_set_toplevel(GNT_BOX(win), TRUE); + gnt_box_set_pad(GNT_BOX(win), 0); + GNT_WIDGET_SET_FLAGS(win, GNT_WIDGET_TRANSIENT); + + tree = wm->_list.tree = gnt_tree_new(); + gnt_box_add_widget(GNT_BOX(win), tree); + + g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(destroy__list), wm); +} + +static void +window_list_activate(GntTree *tree, GntWM *wm) +{ + GntWidget *widget = gnt_tree_get_selection_data(GNT_TREE(tree)); + + if (!wm->ordered || !widget) + return; + + gnt_widget_destroy(wm->_list.window); + gnt_wm_raise_window(wm, widget); +} + +static gboolean +window_list(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + GntWidget *tree, *win; + GList *iter; + + if (wm->_list.window || wm->menu) + return TRUE; + + if (!wm->ordered) + return TRUE; + + setup__list(wm); + wm->windows = &wm->_list; + + win = wm->windows->window; + tree = wm->windows->tree; + + gnt_box_set_title(GNT_BOX(win), "Window List"); + + for (iter = wm->list; iter; iter = iter->next) { + GntBox *box = GNT_BOX(iter->data); + + gnt_tree_add_row_last(GNT_TREE(tree), box, + gnt_tree_create_row(GNT_TREE(tree), box->title), NULL); + update_window_in_list(wm, GNT_WIDGET(box)); + } + + gnt_tree_set_selected(GNT_TREE(tree), wm->ordered->data); + g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(window_list_activate), wm); + + gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3); + gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2); + gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4); + + gnt_widget_show(win); + return TRUE; +} + +static gboolean +dump_screen(GntBindable *bindable, GList *null) +{ + int x, y; + chtype old = 0, now = 0; + FILE *file = fopen("dump.html", "w"); + + fprintf(file, "<pre>"); + for (y = 0; y < getmaxy(stdscr); y++) { + for (x = 0; x < getmaxx(stdscr); x++) { + char ch; + now = mvwinch(curscr, y, x); + ch = now & A_CHARTEXT; + now ^= ch; + +#define CHECK(attr, start, end) \ + do \ + { \ + if (now & attr) \ + { \ + if (!(old & attr)) \ + fprintf(file, start); \ + } \ + else if (old & attr) \ + { \ + fprintf(file, end); \ + } \ + } while (0) + + CHECK(A_BOLD, "<b>", "</b>"); + CHECK(A_UNDERLINE, "<u>", "</u>"); + CHECK(A_BLINK, "<blink>", "</blink>"); + + if ((now & A_COLOR) != (old & A_COLOR) || + (now & A_REVERSE) != (old & A_REVERSE)) + { + int ret; + short fgp, bgp, r, g, b; + struct + { + int r, g, b; + } fg, bg; + + ret = pair_content(PAIR_NUMBER(now & A_COLOR), &fgp, &bgp); + if (fgp == -1) + fgp = COLOR_BLACK; + if (bgp == -1) + bgp = COLOR_WHITE; + if (now & A_REVERSE) + fgp ^= bgp ^= fgp ^= bgp; /* *wink* */ + ret = color_content(fgp, &r, &g, &b); + fg.r = r; fg.b = b; fg.g = g; + ret = color_content(bgp, &r, &g, &b); + bg.r = r; bg.b = b; bg.g = g; +#define ADJUST(x) (x = x * 255 / 1000) + ADJUST(fg.r); + ADJUST(fg.g); + ADJUST(fg.b); + ADJUST(bg.r); + ADJUST(bg.b); + ADJUST(bg.g); + + if (x) fprintf(file, "</span>"); + fprintf(file, "<span style=\"background:#%02x%02x%02x;color:#%02x%02x%02x\">", + bg.r, bg.g, bg.b, fg.r, fg.g, fg.b); + } + if (now & A_ALTCHARSET) + { + switch (ch) + { + case 'q': + ch = '-'; break; + case 't': + case 'u': + case 'x': + ch = '|'; break; + case 'v': + case 'w': + case 'l': + case 'm': + case 'k': + case 'j': + case 'n': + ch = '+'; break; + case '-': + ch = '^'; break; + case '.': + ch = 'v'; break; + case 'a': + ch = '#'; break; + default: + ch = ' '; break; + } + } + if (ch == '&') + fprintf(file, "&"); + else if (ch == '<') + fprintf(file, "<"); + else if (ch == '>') + fprintf(file, ">"); + else + fprintf(file, "%c", ch); + old = now; + } + fprintf(file, "</span>\n"); + old = 0; + } + fprintf(file, "</pre>"); + fclose(file); + return TRUE; +} + +static void +shift_window(GntWM *wm, GntWidget *widget, int dir) +{ + GList *all = wm->list; + GList *list = g_list_find(all, widget); + int length, pos; + if (!list) + return; + + length = g_list_length(all); + pos = g_list_position(all, list); + + pos += dir; + if (dir > 0) + pos++; + + if (pos < 0) + pos = length; + else if (pos > length) + pos = 0; + + all = g_list_insert(all, widget, pos); + all = g_list_delete_link(all, list); + wm->list = all; + draw_taskbar(wm, FALSE); +} + +static gboolean +shift_left(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window) + return TRUE; + + shift_window(wm, wm->ordered->data, -1); + return TRUE; +} + +static gboolean +shift_right(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window) + return TRUE; + + shift_window(wm, wm->ordered->data, 1); + return TRUE; +} + +static void +action_list_activate(GntTree *tree, GntWM *wm) +{ + GntAction *action = gnt_tree_get_selection_data(tree); + action->callback(); + gnt_widget_destroy(wm->_list.window); +} + +static int +compare_action(gconstpointer p1, gconstpointer p2) +{ + const GntAction *a1 = p1; + const GntAction *a2 = p2; + + return g_utf8_collate(a1->label, a2->label); +} + +static gboolean +list_actions(GntBindable *bindable, GList *null) +{ + GntWidget *tree, *win; + GList *iter; + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window || wm->menu) + return TRUE; + + if (wm->acts == NULL) + return TRUE; + + setup__list(wm); + wm->actions = &wm->_list; + + win = wm->actions->window; + tree = wm->actions->tree; + + gnt_box_set_title(GNT_BOX(win), "Actions"); + GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER); + /* XXX: Do we really want this? */ + gnt_tree_set_compare_func(GNT_TREE(tree), compare_action); + + for (iter = wm->acts; iter; iter = iter->next) { + GntAction *action = iter->data; + gnt_tree_add_row_last(GNT_TREE(tree), action, + gnt_tree_create_row(GNT_TREE(tree), action->label), NULL); + } + g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(action_list_activate), wm); + gnt_widget_set_size(tree, 0, g_list_length(wm->acts)); + gnt_widget_set_position(win, 0, getmaxy(stdscr) - 3 - g_list_length(wm->acts)); + + gnt_widget_show(win); + return TRUE; +} + +#ifndef NO_WIDECHAR +static int +widestringwidth(wchar_t *wide) +{ + int len, ret; + char *string; + + len = wcstombs(NULL, wide, 0) + 1; + string = g_new0(char, len); + wcstombs(string, wide, len); + ret = gnt_util_onscreen_width(string, NULL); + g_free(string); + return ret; +} +#endif + +/* Returns the onscreen width of the character at the position */ +static int +reverse_char(WINDOW *d, int y, int x, gboolean set) +{ +#define DECIDE(ch) (set ? ((ch) | A_REVERSE) : ((ch) & ~A_REVERSE)) + +#ifdef NO_WIDECHAR + chtype ch; + ch = mvwinch(d, y, x); + mvwaddch(d, y, x, DECIDE(ch)); + return 1; +#else + cchar_t ch; + int wc = 1; + if (mvwin_wch(d, y, x, &ch) == OK) { + wc = widestringwidth(ch.chars); + ch.attr = DECIDE(ch.attr); + ch.attr &= WA_ATTRIBUTES; /* XXX: This is a workaround for a bug */ + mvwadd_wch(d, y, x, &ch); + } + + return wc; +#endif +} + +static void +window_reverse(GntWidget *win, gboolean set) +{ + int i; + int w, h; + WINDOW *d; + + if (GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_NO_BORDER)) + return; + + d = win->window; + gnt_widget_get_size(win, &w, &h); + + if (gnt_widget_has_shadow(win)) { + --w; + --h; + } + + /* the top and bottom */ + for (i = 0; i < w; i += reverse_char(d, 0, i, set)); + for (i = 0; i < w; i += reverse_char(d, h-1, i, set)); + + /* the left and right */ + for (i = 0; i < h; i += reverse_char(d, i, 0, set)); + for (i = 0; i < h; i += reverse_char(d, i, w-1, set)); + + wrefresh(win->window); +} + +static gboolean +start_move(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window || wm->menu) + return TRUE; + if (!wm->ordered) + return TRUE; + + wm->mode = GNT_KP_MODE_MOVE; + window_reverse(GNT_WIDGET(wm->ordered->data), TRUE); + + return TRUE; +} + +static gboolean +start_resize(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window || wm->menu) + return TRUE; + if (!wm->ordered) + return TRUE; + + wm->mode = GNT_KP_MODE_RESIZE; + window_reverse(GNT_WIDGET(wm->ordered->data), TRUE); + + return TRUE; +} + +static gboolean +wm_quit(GntBindable *bindable, GList *list) +{ + GntWM *wm = GNT_WM(bindable); + g_main_loop_quit(wm->loop); + return TRUE; +} + +static gboolean +return_true(GntWM *wm, GntWidget *w, int *a, int *b) +{ + return TRUE; +} + +static gboolean +refresh_screen(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + + endwin(); + refresh(); + + g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL); + update_screen(wm); + draw_taskbar(wm, TRUE); + + return FALSE; +} + +static void +gnt_wm_class_init(GntWMClass *klass) +{ + klass->new_window = gnt_wm_new_window_real; + klass->decorate_window = NULL; + klass->close_window = NULL; + klass->window_resize_confirm = return_true; + klass->window_resized = gnt_wm_win_resized; + klass->window_move_confirm = return_true; + klass->window_moved = gnt_wm_win_moved; + klass->window_update = NULL; + klass->key_pressed = NULL; + klass->mouse_clicked = NULL; + klass->give_focus = gnt_wm_give_focus; + + signals[SIG_NEW_WIN] = + g_signal_new("new_win", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, new_window), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_DECORATE_WIN] = + g_signal_new("decorate_win", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, decorate_window), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_CLOSE_WIN] = + g_signal_new("close_win", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, close_window), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_CONFIRM_RESIZE] = + g_signal_new("confirm_resize", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_resize_confirm), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER, + G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[SIG_CONFIRM_MOVE] = + g_signal_new("confirm_move", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_move_confirm), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER, + G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[SIG_RESIZED] = + g_signal_new("window_resized", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_resized), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_MOVED] = + g_signal_new("window_moved", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_moved), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_UPDATE_WIN] = + g_signal_new("window_update", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_update), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[SIG_GIVE_FOCUS] = + g_signal_new("give_focus", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, give_focus), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[SIG_MOUSE_CLICK] = + g_signal_new("mouse_clicked", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, mouse_clicked), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__INT_INT_INT_POINTER, + G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER); + + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next", window_next, + "\033" "n", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev", window_prev, + "\033" "p", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-close", window_close, + "\033" "c", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-list", window_list, + "\033" "w", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "dump-screen", dump_screen, + "\033" "d", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-left", shift_left, + "\033" ",", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-right", shift_right, + "\033" ".", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "action-list", list_actions, + "\033" "a", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-move", start_move, + "\033" "m", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-resize", start_resize, + "\033" "r", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "wm-quit", wm_quit, + "\033" "q", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "refresh-screen", refresh_screen, + "\033" "l", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "switch-window-n", switch_window_n, + NULL, NULL); + + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); + GNTDEBUG; +} + +/****************************************************************************** + * GntWM API + *****************************************************************************/ +GType +gnt_wm_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) { + static const GTypeInfo info = { + sizeof(GntWMClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_wm_class_init, + NULL, + NULL, /* class_data */ + sizeof(GntWM), + 0, /* n_preallocs */ + gnt_wm_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_BINDABLE, + "GntWM", + &info, 0); + } + + return type; +} +static void +update_window_in_list(GntWM *wm, GntWidget *wid) +{ + GntTextFormatFlags flag = 0; + + if (wm->windows == NULL) + return; + + if (wid == wm->ordered->data) + flag |= GNT_TEXT_FLAG_DIM; + else if (GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT)) + flag |= GNT_TEXT_FLAG_BOLD; + + gnt_tree_set_row_flags(GNT_TREE(wm->windows->tree), wid, flag); +} + +static void +gnt_wm_new_window_real(GntWM *wm, GntWidget *widget) +{ + GntNode *node; + gboolean transient = FALSE; + + if (widget->window == NULL) + return; + + node = g_new0(GntNode, 1); + node->me = widget; + + g_hash_table_replace(wm->nodes, widget, node); + + refresh_node(widget, node, NULL); + + transient = !!GNT_WIDGET_IS_FLAG_SET(node->me, GNT_WIDGET_TRANSIENT); + + node->panel = new_panel(node->me->window); + set_panel_userptr(node->panel, node); + + if (!transient) { + if (node->me != wm->_list.window) { + GntWidget *w = NULL; + + if (wm->ordered) + w = wm->ordered->data; + + wm->list = g_list_append(wm->list, widget); + + if (wm->event_stack) + wm->ordered = g_list_prepend(wm->ordered, widget); + else + wm->ordered = g_list_append(wm->ordered, widget); + + gnt_widget_set_focus(widget, TRUE); + if (w) + gnt_widget_set_focus(w, FALSE); + } + + if (wm->event_stack || node->me == wm->_list.window) { + gnt_wm_raise_window(wm, node->me); + } else { + bottom_panel(node->panel); /* New windows should not grab focus */ + gnt_widget_set_urgent(node->me); + } + } +} + +void gnt_wm_new_window(GntWM *wm, GntWidget *widget) +{ + while (widget->parent) + widget = widget->parent; + + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_INVISIBLE) || + g_hash_table_lookup(wm->nodes, widget)) { + update_screen(wm); + return; + } + + g_signal_emit(wm, signals[SIG_NEW_WIN], 0, widget); + g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget); + + if (wm->windows && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) { + if ((GNT_IS_BOX(widget) && GNT_BOX(widget)->title) && wm->_list.window != widget + && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS)) { + gnt_tree_add_row_last(GNT_TREE(wm->windows->tree), widget, + gnt_tree_create_row(GNT_TREE(wm->windows->tree), GNT_BOX(widget)->title), + NULL); + update_window_in_list(wm, widget); + } + } + + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget) +{ + g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget); +} + +void gnt_wm_window_close(GntWM *wm, GntWidget *widget) +{ + GntNode *node; + int pos; + + if ((node = g_hash_table_lookup(wm->nodes, widget)) == NULL) + return; + + g_signal_emit(wm, signals[SIG_CLOSE_WIN], 0, widget); + g_hash_table_remove(wm->nodes, widget); + + if (wm->windows) { + gnt_tree_remove(GNT_TREE(wm->windows->tree), widget); + } + + pos = g_list_index(wm->list, widget); + + if (pos != -1) { + wm->list = g_list_remove(wm->list, widget); + wm->ordered = g_list_remove(wm->ordered, widget); + + if (wm->ordered) + gnt_wm_raise_window(wm, wm->ordered->data); + } + + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +void gnt_wm_process_input(GntWM *wm, const char *keys) +{ + keys = gnt_bindable_remap_keys(GNT_BINDABLE(wm), keys); + + if (gnt_bindable_perform_action_key(GNT_BINDABLE(wm), keys)) + return; + + /* Do some manual checking */ + if (wm->ordered && wm->mode != GNT_KP_MODE_NORMAL) { + int xmin = 0, ymin = 0, xmax = getmaxx(stdscr), ymax = getmaxy(stdscr) - 1; + int x, y, w, h; + GntWidget *widget = GNT_WIDGET(wm->ordered->data); + int ox, oy, ow, oh; + + gnt_widget_get_position(widget, &x, &y); + gnt_widget_get_size(widget, &w, &h); + ox = x; oy = y; + ow = w; oh = h; + + if (wm->mode == GNT_KP_MODE_MOVE) { + if (strcmp(keys, GNT_KEY_LEFT) == 0) { + if (x > xmin) + x--; + } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) { + if (x + w < xmax) + x++; + } else if (strcmp(keys, GNT_KEY_UP) == 0) { + if (y > ymin) + y--; + } else if (strcmp(keys, GNT_KEY_DOWN) == 0) { + if (y + h < ymax) + y++; + } + if (ox != x || oy != y) { + gnt_screen_move_widget(widget, x, y); + window_reverse(widget, TRUE); + return; + } + } else if (wm->mode == GNT_KP_MODE_RESIZE) { + if (strcmp(keys, GNT_KEY_LEFT) == 0) { + w--; + } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) { + if (x + w < xmax) + w++; + } else if (strcmp(keys, GNT_KEY_UP) == 0) { + h--; + } else if (strcmp(keys, GNT_KEY_DOWN) == 0) { + if (y + h < ymax) + h++; + } + if (oh != h || ow != w) { + gnt_screen_resize_widget(widget, w, h); + window_reverse(widget, TRUE); + return; + } + } + if (strcmp(keys, "\r") == 0 || strcmp(keys, "\033") == 0) { + window_reverse(widget, FALSE); + wm->mode = GNT_KP_MODE_NORMAL; + return; + } + } + + wm->event_stack = TRUE; + + /* Escape to close the window-list or action-list window */ + if (strcmp(keys, "\033") == 0) { + if (wm->_list.window) { + gnt_widget_destroy(wm->_list.window); + wm->event_stack = FALSE; + return; + } + } else if (keys[0] == '\033' && isdigit(keys[1]) && keys[2] == '\0') { + /* Alt+x for quick switch */ + int n = *(keys + 1) - '0'; + GList *list = NULL; + + if (n == 0) + n = 10; + + list = g_list_append(list, GINT_TO_POINTER(n - 1)); + switch_window_n(GNT_BINDABLE(wm), list); + g_list_free(list); + return; + } + + if (wm->menu) + gnt_widget_key_pressed(GNT_WIDGET(wm->menu), keys); + else if (wm->_list.window) + gnt_widget_key_pressed(wm->_list.window, keys); + else if (wm->ordered) + gnt_widget_key_pressed(GNT_WIDGET(wm->ordered->data), keys); + wm->event_stack = FALSE; +} + +static void +gnt_wm_win_resized(GntWM *wm, GntNode *node) +{ + refresh_node(node->me, node, NULL); + replace_panel(node->panel, node->me->window); +} + +static void +gnt_wm_win_moved(GntWM *wm, GntNode *node) +{ + refresh_node(node->me, node, NULL); +} + +void gnt_wm_resize_window(GntWM *wm, GntWidget *widget, int width, int height) +{ + gboolean ret = TRUE; + GntNode *node; + + while (widget->parent) + widget = widget->parent; + node = g_hash_table_lookup(wm->nodes, widget); + if (!node) + return; + + g_signal_emit(wm, signals[SIG_CONFIRM_RESIZE], 0, widget, &width, &height, &ret); + if (!ret) + return; /* resize is not permitted */ + hide_panel(node->panel); + gnt_widget_set_size(widget, width, height); + gnt_widget_draw(widget); + + g_signal_emit(wm, signals[SIG_RESIZED], 0, node); + + show_panel(node->panel); + update_screen(wm); +} + +void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y) +{ + gboolean ret = TRUE; + GntNode *node; + + while (widget->parent) + widget = widget->parent; + node = g_hash_table_lookup(wm->nodes, widget); + if (!node) + return; + + g_signal_emit(wm, signals[SIG_CONFIRM_MOVE], 0, widget, &x, &y, &ret); + if (!ret) + return; /* resize is not permitted */ + + gnt_widget_set_position(widget, x, y); + move_panel(node->panel, y, x); + + g_signal_emit(wm, signals[SIG_MOVED], 0, node); + + update_screen(wm); +} + +static void +gnt_wm_give_focus(GntWM *wm, GntWidget *widget) +{ + GntNode *node = g_hash_table_lookup(wm->nodes, widget); + + if (!node) + return; + + if (widget != wm->_list.window && !GNT_IS_MENU(widget) && + wm->ordered->data != widget) { + GntWidget *w = wm->ordered->data; + wm->ordered = g_list_bring_to_front(wm->ordered, widget); + gnt_widget_set_focus(w, FALSE); + } + + gnt_widget_set_focus(widget, TRUE); + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_URGENT); + gnt_widget_draw(widget); + top_panel(node->panel); + + if (wm->_list.window) { + GntNode *nd = g_hash_table_lookup(wm->nodes, wm->_list.window); + top_panel(nd->panel); + } + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +void gnt_wm_update_window(GntWM *wm, GntWidget *widget) +{ + GntNode *node; + + while (widget->parent) + widget = widget->parent; + if (!GNT_IS_MENU(widget)) + gnt_box_sync_children(GNT_BOX(widget)); + + node = g_hash_table_lookup(wm->nodes, widget); + if (node == NULL) { + gnt_wm_new_window(wm, widget); + } else + g_signal_emit(wm, signals[SIG_UPDATE_WIN], 0, node); + + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget) +{ + gboolean ret = TRUE; + g_signal_emit(wm, signals[SIG_MOUSE_CLICK], 0, event, x, y, widget, &ret); + return ret; +} + +void gnt_wm_raise_window(GntWM *wm, GntWidget *widget) +{ + g_signal_emit(wm, signals[SIG_GIVE_FOCUS], 0, widget); +} +