configure: Add detection of library kvm that is used under OpenBSD
[wmaker-crm.git] / src / workspace.c
blob2f94427f5ecf8049add95dc06412b84d8320df67
1 /* workspace.c- Workspace management
3 * Window Maker window manager
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.
21 #include "wconfig.h"
23 #include <X11/Xlib.h>
24 #include <X11/Xutil.h>
25 #ifdef SHAPE
26 #include <X11/extensions/shape.h>
27 #endif
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <time.h>
36 #include <sys/time.h>
38 #include "WindowMaker.h"
39 #include "framewin.h"
40 #include "window.h"
41 #include "icon.h"
42 #include "funcs.h"
43 #include "menu.h"
44 #include "application.h"
45 #include "dock.h"
46 #include "actions.h"
47 #include "workspace.h"
48 #include "appicon.h"
49 #include "wmspec.h"
50 #include "xinerama.h"
52 #define MC_NEW 0
53 #define MC_DESTROY_LAST 1
54 #define MC_LAST_USED 2
55 /* index of the first workspace menu entry */
56 #define MC_WORKSPACE1 3
58 #define MAX_SHORTCUT_LENGTH 32
59 #define WORKSPACE_NAME_DISPLAY_PADDING 32
61 extern int ignore_wks_change;
62 extern WPreferences wPreferences;
63 extern XContext wVEdgeContext;
64 extern void ProcessPendingEvents();
65 extern WShortKey wKeyBindings[WKBD_LAST];
67 static WMPropList *dWorkspaces = NULL;
68 static WMPropList *dClip, *dName;
70 static void make_keys(void)
72 if (dWorkspaces != NULL)
73 return;
75 dWorkspaces = WMCreatePLString("Workspaces");
76 dName = WMCreatePLString("Name");
77 dClip = WMCreatePLString("Clip");
80 void wWorkspaceMake(WScreen * scr, int count)
82 while (count > 0) {
83 wWorkspaceNew(scr);
84 count--;
88 int wWorkspaceNew(WScreen *scr)
90 WWorkspace *wspace, **list;
91 int i;
93 if (scr->workspace_count < MAX_WORKSPACES) {
94 scr->workspace_count++;
96 wspace = wmalloc(sizeof(WWorkspace));
97 wspace->name = NULL;
98 wspace->clip = NULL;
100 if (!wspace->name) {
101 wspace->name = wmalloc(strlen(_("Workspace %i")) + 8);
102 sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
105 if (!wPreferences.flags.noclip)
106 wspace->clip = wDockCreate(scr, WM_CLIP);
108 list = wmalloc(sizeof(WWorkspace *) * scr->workspace_count);
110 for (i = 0; i < scr->workspace_count - 1; i++)
111 list[i] = scr->workspaces[i];
113 list[i] = wspace;
114 if (scr->workspaces)
115 wfree(scr->workspaces);
117 scr->workspaces = list;
119 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
120 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
121 wNETWMUpdateDesktop(scr);
122 WMPostNotificationName(WMNWorkspaceCreated, scr, (void *)(uintptr_t) (scr->workspace_count - 1));
123 XFlush(dpy);
125 return scr->workspace_count - 1;
128 return -1;
131 Bool wWorkspaceDelete(WScreen * scr, int workspace)
133 WWindow *tmp;
134 WWorkspace **list;
135 int i, j;
137 if (workspace <= 0)
138 return False;
140 /* verify if workspace is in use by some window */
141 tmp = scr->focused_window;
142 while (tmp) {
143 if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace == workspace)
144 return False;
145 tmp = tmp->prev;
148 if (!wPreferences.flags.noclip) {
149 wDockDestroy(scr->workspaces[workspace]->clip);
150 scr->workspaces[workspace]->clip = NULL;
153 list = wmalloc(sizeof(WWorkspace *) * (scr->workspace_count - 1));
154 j = 0;
155 for (i = 0; i < scr->workspace_count; i++) {
156 if (i != workspace) {
157 list[j++] = scr->workspaces[i];
158 } else {
159 if (scr->workspaces[i]->name)
160 wfree(scr->workspaces[i]->name);
161 wfree(scr->workspaces[i]);
164 wfree(scr->workspaces);
165 scr->workspaces = list;
167 scr->workspace_count--;
169 /* update menu */
170 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
171 /* clip workspace menu */
172 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
174 /* update also window menu */
175 if (scr->workspace_submenu) {
176 WMenu *menu = scr->workspace_submenu;
178 i = menu->entry_no;
179 while (i > scr->workspace_count)
180 wMenuRemoveItem(menu, --i);
181 wMenuRealize(menu);
183 /* and clip menu */
184 if (scr->clip_submenu) {
185 WMenu *menu = scr->clip_submenu;
187 i = menu->entry_no;
188 while (i > scr->workspace_count)
189 wMenuRemoveItem(menu, --i);
190 wMenuRealize(menu);
192 wNETWMUpdateDesktop(scr);
193 WMPostNotificationName(WMNWorkspaceDestroyed, scr, (void *)(uintptr_t) (scr->workspace_count - 1));
195 if (scr->current_workspace >= scr->workspace_count)
196 wWorkspaceChange(scr, scr->workspace_count - 1);
197 if (scr->last_workspace >= scr->workspace_count)
198 scr->last_workspace = 0;
200 return True;
203 typedef struct WorkspaceNameData {
204 int count;
205 RImage *back;
206 RImage *text;
207 time_t timeout;
208 } WorkspaceNameData;
210 static void hideWorkspaceName(void *data)
212 WScreen *scr = (WScreen *) data;
214 if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
215 || time(NULL) > scr->workspace_name_data->timeout) {
216 XUnmapWindow(dpy, scr->workspace_name);
218 if (scr->workspace_name_data) {
219 RReleaseImage(scr->workspace_name_data->back);
220 RReleaseImage(scr->workspace_name_data->text);
221 wfree(scr->workspace_name_data);
223 scr->workspace_name_data = NULL;
225 scr->workspace_name_timer = NULL;
226 } else {
227 RImage *img = RCloneImage(scr->workspace_name_data->back);
228 Pixmap pix;
230 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
232 RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
233 scr->workspace_name_data->count * 255 / 10);
235 RConvertImage(scr->rcontext, img, &pix);
237 RReleaseImage(img);
239 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
240 XClearWindow(dpy, scr->workspace_name);
241 XFreePixmap(dpy, pix);
242 XFlush(dpy);
244 scr->workspace_name_data->count--;
248 static void showWorkspaceName(WScreen * scr, int workspace)
250 WorkspaceNameData *data;
251 RXImage *ximg;
252 Pixmap text, mask;
253 int w, h;
254 int px, py;
255 char *name = scr->workspaces[workspace]->name;
256 int len = strlen(name);
257 int x, y;
258 #ifdef XINERAMA
259 int head;
260 WMRect rect;
261 int xx, yy;
262 #endif
264 if (wPreferences.workspace_name_display_position == WD_NONE || scr->workspace_count < 2) {
265 return;
268 if (scr->workspace_name_timer) {
269 WMDeleteTimerHandler(scr->workspace_name_timer);
270 XUnmapWindow(dpy, scr->workspace_name);
271 XFlush(dpy);
273 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY, hideWorkspaceName, scr);
275 if (scr->workspace_name_data) {
276 RReleaseImage(scr->workspace_name_data->back);
277 RReleaseImage(scr->workspace_name_data->text);
278 wfree(scr->workspace_name_data);
281 data = wmalloc(sizeof(WorkspaceNameData));
282 data->back = NULL;
284 w = WMWidthOfString(scr->workspace_name_font, name, len);
285 h = WMFontHeight(scr->workspace_name_font);
287 #ifdef XINERAMA
288 head = wGetHeadForPointerLocation(scr);
289 rect = wGetRectForHead(scr, head);
290 if (scr->xine_info.count) {
291 xx = rect.pos.x + (scr->xine_info.screens[head].size.width - (w + 4)) / 2;
292 yy = rect.pos.y + (scr->xine_info.screens[head].size.height - (h + 4)) / 2;
294 else {
295 xx = (scr->scr_width - (w + 4)) / 2;
296 yy = (scr->scr_height - (h + 4)) / 2;
298 #endif
300 switch (wPreferences.workspace_name_display_position) {
301 case WD_TOP:
302 #ifdef XINERAMA
303 px = xx;
304 #else
305 px = (scr->scr_width - (w + 4)) / 2;
306 #endif
307 py = WORKSPACE_NAME_DISPLAY_PADDING;
308 break;
309 case WD_BOTTOM:
310 #ifdef XINERAMA
311 px = xx;
312 #else
313 px = (scr->scr_width - (w + 4)) / 2;
314 #endif
315 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
316 break;
317 case WD_TOPLEFT:
318 px = WORKSPACE_NAME_DISPLAY_PADDING;
319 py = WORKSPACE_NAME_DISPLAY_PADDING;
320 break;
321 case WD_TOPRIGHT:
322 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
323 py = WORKSPACE_NAME_DISPLAY_PADDING;
324 break;
325 case WD_BOTTOMLEFT:
326 px = WORKSPACE_NAME_DISPLAY_PADDING;
327 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
328 break;
329 case WD_BOTTOMRIGHT:
330 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
331 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
332 break;
333 case WD_CENTER:
334 default:
335 #ifdef XINERAMA
336 px = xx;
337 py = yy;
338 #else
339 px = (scr->scr_width - (w + 4)) / 2;
340 py = (scr->scr_height - (h + 4)) / 2;
341 #endif
342 break;
344 XResizeWindow(dpy, scr->workspace_name, w + 4, h + 4);
345 XMoveWindow(dpy, scr->workspace_name, px, py);
347 text = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, scr->w_depth);
348 mask = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, 1);
350 /*XSetForeground(dpy, scr->mono_gc, 0);
351 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4); */
353 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
355 for (x = 0; x <= 4; x++) {
356 for (y = 0; y <= 4; y++) {
357 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, x, y, name, len);
361 XSetForeground(dpy, scr->mono_gc, 1);
362 XSetBackground(dpy, scr->mono_gc, 0);
364 XCopyPlane(dpy, text, mask, scr->mono_gc, 0, 0, w + 4, h + 4, 0, 0, 1 << (scr->w_depth - 1));
366 /*XSetForeground(dpy, scr->mono_gc, 1); */
367 XSetBackground(dpy, scr->mono_gc, 1);
369 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
371 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, 2, 2, name, len);
373 #ifdef SHAPE
374 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask, ShapeSet);
375 #endif
376 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
377 XClearWindow(dpy, scr->workspace_name);
379 data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
381 XFreePixmap(dpy, text);
382 XFreePixmap(dpy, mask);
384 if (!data->text) {
385 XMapRaised(dpy, scr->workspace_name);
386 XFlush(dpy);
388 goto erro;
391 ximg = RGetXImage(scr->rcontext, scr->root_win, px, py, data->text->width, data->text->height);
393 if (!ximg || !ximg->image) {
394 goto erro;
397 XMapRaised(dpy, scr->workspace_name);
398 XFlush(dpy);
400 data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
401 RDestroyXImage(scr->rcontext, ximg);
403 if (!data->back) {
404 goto erro;
407 data->count = 10;
409 /* set a timeout for the effect */
410 data->timeout = time(NULL) + 2 + (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY * data->count) / 1000;
412 scr->workspace_name_data = data;
414 return;
416 erro:
417 if (scr->workspace_name_timer)
418 WMDeleteTimerHandler(scr->workspace_name_timer);
420 if (data->text)
421 RReleaseImage(data->text);
422 if (data->back)
423 RReleaseImage(data->back);
424 wfree(data);
426 scr->workspace_name_data = NULL;
428 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
429 10 * WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
432 void wWorkspaceChange(WScreen *scr, int workspace)
434 if (scr->flags.startup || scr->flags.startup2 || scr->flags.ignore_focus_events)
435 return;
437 if (workspace != scr->current_workspace)
438 wWorkspaceForceChange(scr, workspace);
441 void wWorkspaceRelativeChange(WScreen * scr, int amount)
443 int w;
445 /* While the deiconify animation is going on the window is
446 * still "flying" to its final position and we don't want to
447 * change workspace before the animation finishes, otherwise
448 * the window will land in the new workspace */
449 if (ignore_wks_change)
450 return;
452 w = scr->current_workspace + amount;
454 if (amount < 0) {
455 if (w >= 0) {
456 wWorkspaceChange(scr, w);
457 } else if (wPreferences.ws_cycle) {
458 wWorkspaceChange(scr, scr->workspace_count + w);
460 } else if (amount > 0) {
461 if (w < scr->workspace_count) {
462 wWorkspaceChange(scr, w);
463 } else if (wPreferences.ws_advance) {
464 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES - 1));
465 } else if (wPreferences.ws_cycle) {
466 wWorkspaceChange(scr, w % scr->workspace_count);
471 void wWorkspaceForceChange(WScreen * scr, int workspace)
473 WWindow *tmp, *foc = NULL, *foc2 = NULL;
475 if (workspace >= MAX_WORKSPACES || workspace < 0)
476 return;
478 SendHelperMessage(scr, 'C', workspace + 1, NULL);
480 if (workspace > scr->workspace_count - 1) {
481 wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
484 wClipUpdateForWorkspaceChange(scr, workspace);
486 scr->last_workspace = scr->current_workspace;
487 scr->current_workspace = workspace;
489 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
491 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
493 if ((tmp = scr->focused_window) != NULL) {
494 if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded) &&
495 !WFLAGP(tmp, no_focusable)) || tmp->flags.changing_workspace) {
496 foc = tmp;
499 /* foc2 = tmp; will fix annoyance with gnome panel
500 * but will create annoyance for every other application
502 while (tmp) {
503 if (tmp->frame->workspace != workspace && !tmp->flags.selected) {
504 /* unmap windows not on this workspace */
505 if ((tmp->flags.mapped || tmp->flags.shaded) &&
506 !IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) {
507 wWindowUnmap(tmp);
509 /* also unmap miniwindows not on this workspace */
510 if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
511 tmp->icon && !IS_OMNIPRESENT(tmp)) {
512 XUnmapWindow(dpy, tmp->icon->core->window);
513 tmp->icon->mapped = 0;
515 /* update current workspace of omnipresent windows */
516 if (IS_OMNIPRESENT(tmp)) {
517 WApplication *wapp = wApplicationOf(tmp->main_window);
519 tmp->frame->workspace = workspace;
521 if (wapp) {
522 wapp->last_workspace = workspace;
524 if (!foc2 && (tmp->flags.mapped || tmp->flags.shaded)) {
525 foc2 = tmp;
528 } else {
529 /* change selected windows' workspace */
530 if (tmp->flags.selected) {
531 wWindowChangeWorkspace(tmp, workspace);
532 if (!tmp->flags.miniaturized && !foc) {
533 foc = tmp;
535 } else {
536 if (!tmp->flags.hidden) {
537 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
538 /* remap windows that are on this workspace */
539 wWindowMap(tmp);
540 if (!foc && !WFLAGP(tmp, no_focusable)) {
541 foc = tmp;
544 /* Also map miniwindow if not omnipresent */
545 if (!wPreferences.sticky_icons &&
546 tmp->flags.miniaturized && !IS_OMNIPRESENT(tmp) && tmp->icon) {
547 tmp->icon->mapped = 1;
548 XMapWindow(dpy, tmp->icon->core->window);
553 tmp = tmp->prev;
556 /* Gobble up events unleashed by our mapping & unmapping.
557 * These may trigger various grab-initiated focus &
558 * crossing events. However, we don't care about them,
559 * and ignore their focus implications altogether to avoid
560 * flicker.
562 scr->flags.ignore_focus_events = 1;
563 ProcessPendingEvents();
564 scr->flags.ignore_focus_events = 0;
566 if (!foc)
567 foc = foc2;
569 if (scr->focused_window->flags.mapped && !foc) {
570 foc = scr->focused_window;
572 if (wPreferences.focus_mode == WKF_CLICK) {
573 wSetFocusTo(scr, foc);
574 } else {
575 unsigned int mask;
576 int foo;
577 Window bar, win;
578 WWindow *tmp;
580 tmp = NULL;
581 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask)) {
582 tmp = wWindowFor(win);
585 /* If there's a window under the pointer, focus it.
586 * (we ate all other focus events above, so it's
587 * certainly not focused). Otherwise focus last
588 * focused, or the root (depending on sloppiness)
590 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
591 wSetFocusTo(scr, foc);
592 } else {
593 wSetFocusTo(scr, tmp);
598 /* We need to always arrange icons when changing workspace, even if
599 * no autoarrange icons, because else the icons in different workspaces
600 * can be superposed.
601 * This can be avoided if appicons are also workspace specific.
603 if (!wPreferences.sticky_icons)
604 wArrangeIcons(scr, False);
606 if (scr->dock)
607 wAppIconPaint(scr->dock->icon_array[0]);
609 if (scr->clip_icon) {
610 if (scr->workspaces[workspace]->clip->auto_collapse ||
611 scr->workspaces[workspace]->clip->auto_raise_lower) {
612 /* to handle enter notify. This will also */
613 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
614 XMapWindow(dpy, scr->clip_icon->icon->core->window);
615 } else {
616 wClipIconPaint(scr->clip_icon);
619 wScreenUpdateUsableArea(scr);
620 wNETWMUpdateDesktop(scr);
621 showWorkspaceName(scr, workspace);
623 WMPostNotificationName(WMNWorkspaceChanged, scr, (void *)(uintptr_t) workspace);
625 /* XSync(dpy, False); */
628 static void switchWSCommand(WMenu * menu, WMenuEntry * entry)
630 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
633 static void lastWSCommand(WMenu * menu, WMenuEntry * entry)
635 wWorkspaceChange(menu->frame->screen_ptr, menu->frame->screen_ptr->last_workspace);
638 static void deleteWSCommand(WMenu * menu, WMenuEntry * entry)
640 wWorkspaceDelete(menu->frame->screen_ptr, menu->frame->screen_ptr->workspace_count - 1);
643 static void newWSCommand(WMenu *menu, WMenuEntry *foo)
645 int ws;
647 ws = wWorkspaceNew(menu->frame->screen_ptr);
649 /* autochange workspace */
650 if (ws >= 0)
651 wWorkspaceChange(menu->frame->screen_ptr, ws);
654 void wWorkspaceRename(WScreen * scr, int workspace, char *name)
656 char buf[MAX_WORKSPACENAME_WIDTH + 1];
657 char *tmp;
659 if (workspace >= scr->workspace_count)
660 return;
662 /* trim white spaces */
663 tmp = wtrimspace(name);
665 if (strlen(tmp) == 0) {
666 snprintf(buf, sizeof(buf), _("Workspace %i"), workspace + 1);
667 } else {
668 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
670 buf[MAX_WORKSPACENAME_WIDTH] = 0;
671 wfree(tmp);
673 /* update workspace */
674 wfree(scr->workspaces[workspace]->name);
675 scr->workspaces[workspace]->name = wstrdup(buf);
677 if (scr->clip_ws_menu) {
678 if (strcmp(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
679 wfree(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text);
680 scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
681 wMenuRealize(scr->clip_ws_menu);
684 if (scr->workspace_menu) {
685 if (strcmp(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
686 wfree(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text);
687 scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
688 wMenuRealize(scr->workspace_menu);
692 if (scr->clip_icon)
693 wClipIconPaint(scr->clip_icon);
695 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) workspace);
698 /* callback for when menu entry is edited */
699 static void onMenuEntryEdited(WMenu * menu, WMenuEntry * entry)
701 char *tmp;
703 tmp = entry->text;
704 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
707 WMenu *wWorkspaceMenuMake(WScreen * scr, Bool titled)
709 WMenu *wsmenu;
710 WMenuEntry *entry;
712 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
713 if (!wsmenu) {
714 wwarning(_("could not create Workspace menu"));
715 return NULL;
718 /* callback to be called when an entry is edited */
719 wsmenu->on_edit = onMenuEntryEdited;
721 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
722 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
724 entry = wMenuAddCallback(wsmenu, _("Last Used"), lastWSCommand, NULL);
725 entry->rtext = GetShortcutKey(wKeyBindings[WKBD_LASTWORKSPACE]);
727 return wsmenu;
730 void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
732 int i;
733 long ws;
734 char title[MAX_WORKSPACENAME_WIDTH + 1];
735 WMenuEntry *entry;
736 int tmp;
738 if (!menu)
739 return;
741 if (menu->entry_no < scr->workspace_count + MC_WORKSPACE1) {
742 /* new workspace(s) added */
743 i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
744 ws = menu->entry_no - MC_WORKSPACE1;
745 while (i > 0) {
746 wstrlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
748 entry = wMenuAddCallback(menu, title, switchWSCommand, (void *)ws);
749 entry->flags.indicator = 1;
750 entry->flags.editable = 1;
752 i--;
753 ws++;
755 } else if (menu->entry_no > scr->workspace_count + MC_WORKSPACE1) {
756 /* removed workspace(s) */
757 for (i = menu->entry_no - 1; i >= scr->workspace_count + MC_WORKSPACE1; i--) {
758 wMenuRemoveItem(menu, i);
762 for (i = 0; i < scr->workspace_count; i++) {
763 /* workspace shortcut labels */
764 if (i / 10 == scr->current_workspace / 10)
765 menu->entries[i + MC_WORKSPACE1]->rtext = GetShortcutKey(wKeyBindings[WKBD_WORKSPACE1 + (i % 10)]);
766 else
767 menu->entries[i + MC_WORKSPACE1]->rtext = NULL;
769 menu->entries[i + MC_WORKSPACE1]->flags.indicator_on = 0;
771 menu->entries[scr->current_workspace + MC_WORKSPACE1]->flags.indicator_on = 1;
772 wMenuRealize(menu);
774 /* don't let user destroy current workspace */
775 if (scr->current_workspace == scr->workspace_count - 1) {
776 wMenuSetEnabled(menu, MC_DESTROY_LAST, False);
777 } else {
778 wMenuSetEnabled(menu, MC_DESTROY_LAST, True);
781 /* back to last workspace */
782 if (scr->workspace_count && scr->last_workspace != scr->current_workspace)
783 wMenuSetEnabled(menu, MC_LAST_USED, True);
784 else
785 wMenuSetEnabled(menu, MC_LAST_USED, False);
787 tmp = menu->frame->top_width + 5;
788 /* if menu got unreachable, bring it to a visible place */
789 if (menu->frame_x < tmp - (int)menu->frame->core->width)
790 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
792 wMenuPaint(menu);
795 void wWorkspaceSaveState(WScreen * scr, WMPropList * old_state)
797 WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
798 int i;
800 make_keys();
802 old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
803 parr = WMCreatePLArray(NULL);
804 for (i = 0; i < scr->workspace_count; i++) {
805 pstr = WMCreatePLString(scr->workspaces[i]->name);
806 wks_state = WMCreatePLDictionary(dName, pstr, NULL);
807 WMReleasePropList(pstr);
808 if (!wPreferences.flags.noclip) {
809 pstr = wClipSaveWorkspaceState(scr, i);
810 WMPutInPLDictionary(wks_state, dClip, pstr);
811 WMReleasePropList(pstr);
812 } else if (old_wks_state != NULL) {
813 if ((foo = WMGetFromPLArray(old_wks_state, i)) != NULL) {
814 if ((bar = WMGetFromPLDictionary(foo, dClip)) != NULL) {
815 WMPutInPLDictionary(wks_state, dClip, bar);
819 WMAddToPLArray(parr, wks_state);
820 WMReleasePropList(wks_state);
822 WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
823 WMReleasePropList(parr);
826 void wWorkspaceRestoreState(WScreen *scr)
828 WMPropList *parr, *pstr, *wks_state, *clip_state;
829 int i, j;
831 make_keys();
833 if (scr->session_state == NULL)
834 return;
836 parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
838 if (!parr)
839 return;
841 for (i = 0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
842 wks_state = WMGetFromPLArray(parr, i);
843 if (WMIsPLDictionary(wks_state))
844 pstr = WMGetFromPLDictionary(wks_state, dName);
845 else
846 pstr = wks_state;
848 if (i >= scr->workspace_count)
849 wWorkspaceNew(scr);
851 if (scr->workspace_menu) {
852 wfree(scr->workspace_menu->entries[i + MC_WORKSPACE1]->text);
853 scr->workspace_menu->entries[i + MC_WORKSPACE1]->text = wstrdup(WMGetFromPLString(pstr));
854 scr->workspace_menu->flags.realized = 0;
857 wfree(scr->workspaces[i]->name);
858 scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
859 if (!wPreferences.flags.noclip) {
860 int added_omnipresent_icons = 0;
862 clip_state = WMGetFromPLDictionary(wks_state, dClip);
863 if (scr->workspaces[i]->clip)
864 wDockDestroy(scr->workspaces[i]->clip);
866 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state, WM_CLIP);
867 if (i > 0)
868 wDockHideIcons(scr->workspaces[i]->clip);
870 /* We set the global icons here, because scr->workspaces[i]->clip
871 * was not valid in wDockRestoreState().
872 * There we only set icon->omnipresent to know which icons we
873 * need to set here.
875 for (j = 0; j < scr->workspaces[i]->clip->max_icons; j++) {
876 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
877 int k;
879 if (!aicon || !aicon->omnipresent)
880 continue;
881 aicon->omnipresent = 0;
882 if (wClipMakeIconOmnipresent(aicon, True) != WO_SUCCESS)
883 continue;
884 if (i == 0)
885 continue;
887 /* Move this appicon from workspace i to workspace 0 */
888 scr->workspaces[i]->clip->icon_array[j] = NULL;
889 scr->workspaces[i]->clip->icon_count--;
891 added_omnipresent_icons++;
892 /* If there are too many omnipresent appicons, we are in trouble */
893 assert(scr->workspaces[0]->clip->icon_count + added_omnipresent_icons
894 <= scr->workspaces[0]->clip->max_icons);
895 /* Find first free spot on workspace 0 */
896 for (k = 0; k < scr->workspaces[0]->clip->max_icons; k++)
897 if (scr->workspaces[0]->clip->icon_array[k] == NULL)
898 break;
899 scr->workspaces[0]->clip->icon_array[k] = aicon;
900 aicon->dock = scr->workspaces[0]->clip;
902 scr->workspaces[0]->clip->icon_count += added_omnipresent_icons;
905 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) i);
909 /* Returns the workspace number for a given workspace name */
910 int wGetWorkspaceNumber(WScreen * scr, char * value)
912 int w, i;
914 if (sscanf(value, "%i", &w) != 1) {
915 w = -1;
916 for (i = 0; i < scr->workspace_count; i++) {
917 if (strcmp(scr->workspaces[i]->name, value) == 0) {
918 w = i;
919 break;
922 } else {
923 w--;
926 return w;