support moving window between heads using keyboard
[wmaker-crm.git] / src / switchmenu.c
blob3106595cbe508aa543ae8f762ba10e12e59a3f99
1 /*
2 * Window Maker window manager
4 * Copyright (c) 1997 Shige Abe
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "wconfig.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdint.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
32 #include "WindowMaker.h"
33 #include "window.h"
34 #include "actions.h"
35 #include "client.h"
36 #include "misc.h"
37 #include "stacking.h"
38 #include "workspace.h"
39 #include "framewin.h"
40 #include "switchmenu.h"
42 #define IS_GNUSTEP_MENU(w) ((w)->wm_gnustep_attr && \
43 ((w)->wm_gnustep_attr->flags & GSWindowLevelAttr) && \
44 ((w)->wm_gnustep_attr->window_level == WMMainMenuWindowLevel || \
45 (w)->wm_gnustep_attr->window_level == WMSubmenuWindowLevel))
47 static int initialized = 0;
48 static void observer(void *self, WMNotification * notif);
49 static void wsobserver(void *self, WMNotification * notif);
52 * FocusWindow
54 * - Needs to check if already in the right workspace before
55 * calling wChangeWorkspace?
57 * Order:
58 * Switch to correct workspace
59 * Unshade if shaded
60 * If iconified then deiconify else focus/raise.
62 static void focusWindow(WMenu * menu, WMenuEntry * entry)
64 WWindow *wwin;
66 /* Parameter not used, but tell the compiler that it is ok */
67 (void) menu;
69 wwin = (WWindow *) entry->clientdata;
70 wWindowSingleFocus(wwin);
73 void InitializeSwitchMenu(void)
75 if (!initialized) {
76 initialized = 1;
78 WMAddNotificationObserver(observer, NULL, WMNManaged, NULL);
79 WMAddNotificationObserver(observer, NULL, WMNUnmanaged, NULL);
80 WMAddNotificationObserver(observer, NULL, WMNChangedWorkspace, NULL);
81 WMAddNotificationObserver(observer, NULL, WMNChangedState, NULL);
82 WMAddNotificationObserver(observer, NULL, WMNChangedFocus, NULL);
83 WMAddNotificationObserver(observer, NULL, WMNChangedStacking, NULL);
84 WMAddNotificationObserver(observer, NULL, WMNChangedName, NULL);
86 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceChanged, NULL);
87 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceNameChanged, NULL);
93 * Open switch menu
96 void OpenSwitchMenu(WScreen * scr, int x, int y, int keyboard)
98 WMenu *switchmenu = scr->switch_menu;
99 WWindow *wwin;
101 if (switchmenu) {
102 if (switchmenu->flags.mapped) {
103 if (!switchmenu->flags.buttoned) {
104 wMenuUnmap(switchmenu);
105 } else {
106 wRaiseFrame(switchmenu->frame->core);
108 if (keyboard)
109 wMenuMapAt(switchmenu, 0, 0, True);
110 else
111 wMenuMapCopyAt(switchmenu, x - switchmenu->frame->core->width / 2, y);
113 } else {
114 if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
115 y = y - switchmenu->frame->core->height / 2;
117 wMenuMapAt(switchmenu, x - switchmenu->frame->core->width / 2, y, keyboard);
119 return;
121 switchmenu = wMenuCreate(scr, _("Windows"), True);
122 scr->switch_menu = switchmenu;
124 wwin = scr->focused_window;
125 while (wwin) {
126 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
128 wwin = wwin->prev;
131 if (switchmenu) {
132 int newx, newy;
134 if (!switchmenu->flags.realized)
135 wMenuRealize(switchmenu);
137 if (keyboard && x == 0 && y == 0) {
138 newx = newy = 0;
139 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
140 newx = x - switchmenu->frame->core->width / 2;
141 newy = y - switchmenu->frame->core->height / 2;
142 } else {
143 newx = x - switchmenu->frame->core->width / 2;
144 newy = y;
146 wMenuMapAt(switchmenu, newx, newy, keyboard);
150 static int menuIndexForWindow(WMenu * menu, WWindow * wwin, int old_pos)
152 int idx, move_down;
154 if (menu->entry_no <= old_pos)
155 return -1;
157 for (idx = 0, move_down = 0; idx < menu->entry_no; idx++) {
158 WWindow *tw = (WWindow *) menu->entries[idx]->clientdata;
160 /* Is the window moving down in the menu? If so, we'll need to
161 adjust its new index by 1. */
162 if (tw == wwin)
163 move_down = 1;
165 if (IS_OMNIPRESENT(tw) || (tw != wwin &&
166 tw->frame->workspace >= wwin->frame->workspace))
167 return idx - move_down;
170 return idx;
174 * Update switch menu
176 void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
178 WMenu *switchmenu = scr->switch_menu;
179 WMenuEntry *entry;
180 char title[MAX_MENU_TEXT_LENGTH + 6];
181 int len = sizeof(title);
182 int i;
183 int checkVisibility = 0;
185 if (!wwin->screen_ptr->switch_menu)
186 return;
188 * This menu is updated under the following conditions:
190 * 1. When a window is created.
191 * 2. When a window is destroyed.
193 * 3. When a window changes it's title.
194 * 4. When a window changes its workspace.
196 if (action == ACTION_ADD) {
197 char *t;
198 int idx;
200 if (wwin->flags.internal_window || WFLAGP(wwin, skip_window_list) || IS_GNUSTEP_MENU(wwin)) {
201 return;
204 if (wwin->frame->title)
205 snprintf(title, len, "%s", wwin->frame->title);
206 else
207 snprintf(title, len, "%s", DEF_WINDOW_TITLE);
208 t = ShrinkString(scr->menu_entry_font, title, MAX_WINDOWLIST_WIDTH);
210 if (IS_OMNIPRESENT(wwin))
211 idx = -1;
212 else {
213 idx = menuIndexForWindow(switchmenu, wwin, 0);
216 entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
217 wfree(t);
219 entry->flags.indicator = 1;
220 entry->rtext = wmalloc(MAX_WORKSPACENAME_WIDTH + 8);
221 if (IS_OMNIPRESENT(wwin))
222 snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[*]");
223 else
224 snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]",
225 scr->workspaces[wwin->frame->workspace]->name);
227 if (wwin->flags.hidden) {
228 entry->flags.indicator_type = MI_HIDDEN;
229 entry->flags.indicator_on = 1;
230 } else if (wwin->flags.miniaturized) {
231 entry->flags.indicator_type = MI_MINIWINDOW;
232 entry->flags.indicator_on = 1;
233 } else if (wwin->flags.focused) {
234 entry->flags.indicator_type = MI_DIAMOND;
235 entry->flags.indicator_on = 1;
236 } else if (wwin->flags.shaded) {
237 entry->flags.indicator_type = MI_SHADED;
238 entry->flags.indicator_on = 1;
241 wMenuRealize(switchmenu);
242 checkVisibility = 1;
243 } else {
244 char *t;
245 for (i = 0; i < switchmenu->entry_no; i++) {
246 entry = switchmenu->entries[i];
247 /* this is the entry that was changed */
248 if (entry->clientdata == wwin) {
249 switch (action) {
250 case ACTION_REMOVE:
251 wMenuRemoveItem(switchmenu, i);
252 wMenuRealize(switchmenu);
253 checkVisibility = 1;
254 break;
256 case ACTION_CHANGE:
257 if (entry->text)
258 wfree(entry->text);
260 if (wwin->frame->title)
261 snprintf(title, MAX_MENU_TEXT_LENGTH, "%s", wwin->frame->title);
262 else
263 snprintf(title, MAX_MENU_TEXT_LENGTH, "%s", DEF_WINDOW_TITLE);
265 t = ShrinkString(scr->menu_entry_font, title, MAX_WINDOWLIST_WIDTH);
266 entry->text = t;
268 wMenuRealize(switchmenu);
269 checkVisibility = 1;
270 break;
272 case ACTION_CHANGE_WORKSPACE:
273 if (entry->rtext) {
274 int idx = -1;
275 char *t, *rt;
276 int it, ion;
278 if (IS_OMNIPRESENT(wwin)) {
279 snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[*]");
280 } else {
281 snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH,
282 "[%s]",
283 scr->workspaces[wwin->frame->workspace]->name);
286 rt = entry->rtext;
287 entry->rtext = NULL;
288 t = entry->text;
289 entry->text = NULL;
291 it = entry->flags.indicator_type;
292 ion = entry->flags.indicator_on;
294 if (!IS_OMNIPRESENT(wwin) && idx < 0) {
295 idx = menuIndexForWindow(switchmenu, wwin, i);
298 wMenuRemoveItem(switchmenu, i);
300 entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
301 wfree(t);
302 entry->rtext = rt;
303 entry->flags.indicator = 1;
304 entry->flags.indicator_type = it;
305 entry->flags.indicator_on = ion;
307 wMenuRealize(switchmenu);
308 checkVisibility = 1;
309 break;
311 case ACTION_CHANGE_STATE:
312 if (wwin->flags.hidden) {
313 entry->flags.indicator_type = MI_HIDDEN;
314 entry->flags.indicator_on = 1;
315 } else if (wwin->flags.miniaturized) {
316 entry->flags.indicator_type = MI_MINIWINDOW;
317 entry->flags.indicator_on = 1;
318 } else if (wwin->flags.shaded && !wwin->flags.focused) {
319 entry->flags.indicator_type = MI_SHADED;
320 entry->flags.indicator_on = 1;
321 } else {
322 entry->flags.indicator_on = wwin->flags.focused;
323 entry->flags.indicator_type = MI_DIAMOND;
325 break;
327 break;
331 if (checkVisibility) {
332 int tmp;
334 tmp = switchmenu->frame->top_width + 5;
335 /* if menu got unreachable, bring it to a visible place */
336 if (switchmenu->frame_x < tmp - (int)switchmenu->frame->core->width) {
337 wMenuMove(switchmenu, tmp - (int)switchmenu->frame->core->width,
338 switchmenu->frame_y, False);
341 wMenuPaint(switchmenu);
344 static void UpdateSwitchMenuWorkspace(WScreen *scr, int workspace)
346 WMenu *menu = scr->switch_menu;
347 int i;
348 WWindow *wwin;
350 if (!menu)
351 return;
353 for (i = 0; i < menu->entry_no; i++) {
354 wwin = (WWindow *) menu->entries[i]->clientdata;
356 if (wwin->frame->workspace == workspace && !IS_OMNIPRESENT(wwin)) {
357 if (IS_OMNIPRESENT(wwin))
358 snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[*]");
359 else
360 snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]",
361 scr->workspaces[wwin->frame->workspace]->name);
362 menu->flags.realized = 0;
365 if (!menu->flags.realized)
366 wMenuRealize(menu);
369 static void observer(void *self, WMNotification * notif)
371 WWindow *wwin = (WWindow *) WMGetNotificationObject(notif);
372 const char *name = WMGetNotificationName(notif);
373 void *data = WMGetNotificationClientData(notif);
375 /* Parameter not used, but tell the compiler that it is ok */
376 (void) self;
378 if (!wwin)
379 return;
381 if (strcmp(name, WMNManaged) == 0)
382 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_ADD);
383 else if (strcmp(name, WMNUnmanaged) == 0)
384 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_REMOVE);
385 else if (strcmp(name, WMNChangedWorkspace) == 0)
386 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_WORKSPACE);
387 else if (strcmp(name, WMNChangedFocus) == 0)
388 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
389 else if (strcmp(name, WMNChangedName) == 0)
390 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE);
391 else if (strcmp(name, WMNChangedState) == 0) {
392 if (strcmp((char *)data, "omnipresent") == 0) {
393 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_WORKSPACE);
394 } else {
395 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
400 static void wsobserver(void *self, WMNotification * notif)
402 WScreen *scr = (WScreen *) WMGetNotificationObject(notif);
403 const char *name = WMGetNotificationName(notif);
404 void *data = WMGetNotificationClientData(notif);
406 /* Parameter not used, but tell the compiler that it is ok */
407 (void) self;
409 if (strcmp(name, WMNWorkspaceNameChanged) == 0) {
410 UpdateSwitchMenuWorkspace(scr, (uintptr_t)data);
411 } else if (strcmp(name, WMNWorkspaceChanged) == 0) {