📄 gntwm.c
字号:
#define _GNU_SOURCE#if defined(__APPLE__)#define _XOPEN_SOURCE_EXTENDED#endif#include "config.h"#include <ctype.h>#include <stdlib.h>#include <string.h>#include <time.h>#include "gntwm.h"#include "gntstyle.h"#include "gntmarshal.h"#include "gnt.h"#include "gntbox.h"#include "gntlabel.h"#include "gntmenu.h"#include "gnttextview.h"#include "gnttree.h"#include "gntutils.h"#include "gntwindow.h"#define IDLE_CHECK_INTERVAL 5 /* 5 seconds */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 void shift_window(GntWM *wm, GntWidget *widget, int dir);#ifndef NO_WIDECHARstatic int widestringwidth(wchar_t *wide);#endifstatic gboolean write_already(gpointer data);static int write_timeout;static time_t last_active_time;static gboolean idle_update;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 voidfree_node(gpointer data){ GntNode *node = data; hide_panel(node->panel); del_panel(node->panel); g_free(node);}static voiddraw_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)); if (iter->next) mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width); else mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), getmaxx(stdscr) - width * i); 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 voidcopy_win(GntWidget *widget, GntNode *node){ WINDOW *src, *dst; int shadow; if (!node) return; src = widget->window; dst = node->window; shadow = gnt_widget_has_shadow(widget) ? 1 : 0; copywin(src, dst, node->scroll, 0, 0, 0, getmaxy(dst) - 1, getmaxx(dst) - 1, 0);}/** * The following is a workaround for a bug in most versions of ncursesw. * Read about it in: http://article.gmane.org/gmane.comp.lib.ncurses.bugs/2751 * * In short, if a panel hides one cell of a multi-cell character, then the rest * of the characters in that line get screwed. The workaround here is to erase * any such character preemptively. * * Caveat: If a wide character is erased, and the panel above it is moved enough * to expose the entire character, it is not always redrawn. */static voidwork_around_for_ncurses_bug(){#ifndef NO_WIDECHAR PANEL *panel = NULL; while ((panel = panel_below(panel)) != NULL) { int sx, ex, sy, ey, w, y; cchar_t ch; PANEL *below = panel; sx = panel->win->_begx; ex = panel->win->_maxx + sx; sy = panel->win->_begy; ey = panel->win->_maxy + sy; while ((below = panel_below(below)) != NULL) { if (sy > below->win->_begy + below->win->_maxy || ey < below->win->_begy) continue; if (sx > below->win->_begx + below->win->_maxx || ex < below->win->_begx) continue; for (y = MAX(sy, below->win->_begy); y <= MIN(ey, below->win->_begy + below->win->_maxy); y++) { if (mvwin_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch) != OK) goto right; w = widestringwidth(ch.chars); if (w > 1 && (ch.attr & 1)) { ch.chars[0] = ' '; ch.attr &= ~ A_CHARTEXT; mvwadd_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch); touchline(below->win, y - below->win->_begy, 1); }right: if (mvwin_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch) != OK) continue; w = widestringwidth(ch.chars); if (w > 1 && !(ch.attr & 1)) { ch.chars[0] = ' '; ch.attr &= ~ A_CHARTEXT; mvwadd_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch); touchline(below->win, y - below->win->_begy, 1); } } } }#endif}static gbooleanupdate_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; } } work_around_for_ncurses_bug(); update_panels(); doupdate(); return TRUE;}static gbooleansanitize_position(GntWidget *widget, int *x, int *y){ int X_MAX = getmaxx(stdscr); int Y_MAX = getmaxy(stdscr) - 1; int w, h; int nx, ny; gboolean changed = FALSE; gnt_widget_get_size(widget, &w, &h); if (x) { if (*x + w > X_MAX) { nx = MAX(0, X_MAX - w); if (nx != *x) { *x = nx; changed = TRUE; } } } if (y) { if (*y + h > Y_MAX) { ny = MAX(0, Y_MAX - h); if (ny != *y) { *y = ny; changed = TRUE; } } } return changed;}static voidrefresh_node(GntWidget *widget, GntNode *node, gpointer null){ int x, y, w, h; int nw, nh; 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); if (sanitize_position(widget, &x, &y)) gnt_screen_move_widget(widget, x, y); nw = MIN(w, X_MAX); nh = MIN(h, Y_MAX); if (nw != w || nh != h) gnt_screen_resize_widget(widget, nw, nh);}static voidread_window_positions(GntWM *wm){#if GLIB_CHECK_VERSION(2,6,0) GKeyFile *gfile = g_key_file_new(); char *filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL); GError *error = NULL; char **keys; gsize nk; if (!g_key_file_load_from_file(gfile, filename, G_KEY_FILE_NONE, &error)) { g_printerr("GntWM: %s\n", error->message); g_error_free(error); g_free(filename); return; } keys = g_key_file_get_keys(gfile, "positions", &nk, &error); if (error) { g_printerr("GntWM: %s\n", error->message); g_error_free(error); error = NULL; } else { while (nk--) { char *title = keys[nk]; gsize l; char **coords = g_key_file_get_string_list(gfile, "positions", title, &l, NULL); if (l == 2) { int x = atoi(coords[0]); int y = atoi(coords[1]); GntPosition *p = g_new0(GntPosition, 1); p->x = x; p->y = y; g_hash_table_replace(wm->positions, g_strdup(title + 1), p); } else { g_printerr("GntWM: Invalid number of arguments for positioing a window.\n"); } g_strfreev(coords); } g_strfreev(keys); } g_free(filename); g_key_file_free(gfile);#endif}static gboolean check_idle(gpointer n){ if (idle_update) { time(&last_active_time); idle_update = FALSE; } return TRUE;}static voidgnt_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); wm->positions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE)) read_window_positions(wm); g_timeout_add(IDLE_CHECK_INTERVAL * 1000, check_idle, NULL); time(&last_active_time);}static voidswitch_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 gbooleanwindow_next(GntBindable *bindable, GList *null){ GntWM *wm = GNT_WM(bindable); switch_window(wm, 1); return TRUE;}static gbooleanwindow_prev(GntBindable *bindable, GList *null){ GntWM *wm = GNT_WM(bindable); switch_window(wm, -1); return TRUE;}static gbooleanswitch_window_n(GntBindable *bind, GList *list){ GntWM *wm = GNT_WM(bind); GntWidget *w = NULL; GList *l; int n; if (!wm->ordered) return TRUE; if (list) n = GPOINTER_TO_INT(list->data); else n = 0; 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 gbooleanwindow_scroll_up(GntBindable *bindable, GList *null){ GntWM *wm = GNT_WM(bindable); GntWidget *window; GntNode *node; if (!wm->ordered) return TRUE; window = wm->ordered->data; node = g_hash_table_lookup(wm->nodes, window); if (!node) return TRUE; if (node->scroll) { node->scroll--; copy_win(window, node); update_screen(wm); } return TRUE;}static gbooleanwindow_scroll_down(GntBindable *bindable, GList *null){ GntWM *wm = GNT_WM(bindable); GntWidget *window; GntNode *node; int w, h; if (!wm->ordered) return TRUE; window = wm->ordered->data; node = g_hash_table_lookup(wm->nodes, window); if (!node) return TRUE; gnt_widget_get_size(window, &w, &h); if (h - node->scroll > getmaxy(node->window)) { node->scroll++; copy_win(window, node); update_screen(wm); } return TRUE;}static gbooleanwindow_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 gbooleanhelp_for_widget(GntBindable *bindable, GList *null){ GntWM *wm = GNT_WM(bindable); GntWidget *widget, *tree, *win, *active; char *title; if (!wm->ordered) return TRUE; widget = wm->ordered->data; if (!GNT_IS_BOX(widget)) return TRUE; active = GNT_BOX(widget)->active; tree = gnt_widget_bindings_view(active); win = gnt_window_new(); title = g_strdup_printf("Bindings for %s", g_type_name(G_OBJECT_TYPE(active))); gnt_box_set_title(GNT_BOX(win), title); if (tree) gnt_box_add_widget(GNT_BOX(win), tree); else gnt_box_add_widget(GNT_BOX(win), gnt_label_new("This widget has no customizable bindings.")); gnt_widget_show(win); return TRUE;}static voiddestroy__list(GntWidget *widget, GntWM *wm){ wm->_list.window = NULL; wm->_list.tree = NULL; wm->windows = NULL; wm->actions = NULL; update_screen(wm);}static voidsetup__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 voidwindow_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 void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -