Eliminate Workspaces menu magic numbers.
[wmaker-crm.git] / src / workspace.c
blob438fe55f33ad71a80947c9b4ec74f0ed698045b2
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 /* index of the first workspace menu entry */
55 #define MC_WORKSPACE1 2
57 #define MAX_SHORTCUT_LENGTH 32
58 #define WORKSPACE_NAME_DISPLAY_PADDING 32
60 extern int ignore_wks_change;
61 extern WPreferences wPreferences;
62 extern XContext wVEdgeContext;
63 extern void ProcessPendingEvents();
65 static WMPropList *dWorkspaces = NULL;
66 static WMPropList *dClip, *dName;
68 static void make_keys(void)
70 if (dWorkspaces != NULL)
71 return;
73 dWorkspaces = WMCreatePLString("Workspaces");
74 dName = WMCreatePLString("Name");
75 dClip = WMCreatePLString("Clip");
78 void wWorkspaceMake(WScreen * scr, int count)
80 while (count > 0) {
81 wWorkspaceNew(scr);
82 count--;
86 int wWorkspaceNew(WScreen *scr)
88 WWorkspace *wspace, **list;
89 int i;
91 if (scr->workspace_count < MAX_WORKSPACES) {
92 scr->workspace_count++;
94 wspace = wmalloc(sizeof(WWorkspace));
95 wspace->name = NULL;
96 wspace->clip = NULL;
98 if (!wspace->name) {
99 wspace->name = wmalloc(strlen(_("Workspace %i")) + 8);
100 sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
103 if (!wPreferences.flags.noclip)
104 wspace->clip = wDockCreate(scr, WM_CLIP);
106 list = wmalloc(sizeof(WWorkspace *) * scr->workspace_count);
108 for (i = 0; i < scr->workspace_count - 1; i++)
109 list[i] = scr->workspaces[i];
111 list[i] = wspace;
112 if (scr->workspaces)
113 wfree(scr->workspaces);
115 scr->workspaces = list;
117 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
118 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
119 wNETWMUpdateDesktop(scr);
120 WMPostNotificationName(WMNWorkspaceCreated, scr, (void *)(uintptr_t) (scr->workspace_count - 1));
121 XFlush(dpy);
123 return scr->workspace_count - 1;
126 return -1;
129 Bool wWorkspaceDelete(WScreen * scr, int workspace)
131 WWindow *tmp;
132 WWorkspace **list;
133 int i, j;
135 if (workspace <= 0)
136 return False;
138 /* verify if workspace is in use by some window */
139 tmp = scr->focused_window;
140 while (tmp) {
141 if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace == workspace)
142 return False;
143 tmp = tmp->prev;
146 if (!wPreferences.flags.noclip) {
147 wDockDestroy(scr->workspaces[workspace]->clip);
148 scr->workspaces[workspace]->clip = NULL;
151 list = wmalloc(sizeof(WWorkspace *) * (scr->workspace_count - 1));
152 j = 0;
153 for (i = 0; i < scr->workspace_count; i++) {
154 if (i != workspace) {
155 list[j++] = scr->workspaces[i];
156 } else {
157 if (scr->workspaces[i]->name)
158 wfree(scr->workspaces[i]->name);
159 wfree(scr->workspaces[i]);
162 wfree(scr->workspaces);
163 scr->workspaces = list;
165 scr->workspace_count--;
167 /* update menu */
168 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
169 /* clip workspace menu */
170 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
172 /* update also window menu */
173 if (scr->workspace_submenu) {
174 WMenu *menu = scr->workspace_submenu;
176 i = menu->entry_no;
177 while (i > scr->workspace_count)
178 wMenuRemoveItem(menu, --i);
179 wMenuRealize(menu);
181 /* and clip menu */
182 if (scr->clip_submenu) {
183 WMenu *menu = scr->clip_submenu;
185 i = menu->entry_no;
186 while (i > scr->workspace_count)
187 wMenuRemoveItem(menu, --i);
188 wMenuRealize(menu);
190 wNETWMUpdateDesktop(scr);
191 WMPostNotificationName(WMNWorkspaceDestroyed, scr, (void *)(uintptr_t) (scr->workspace_count - 1));
193 if (scr->current_workspace >= scr->workspace_count)
194 wWorkspaceChange(scr, scr->workspace_count - 1);
196 return True;
199 typedef struct WorkspaceNameData {
200 int count;
201 RImage *back;
202 RImage *text;
203 time_t timeout;
204 } WorkspaceNameData;
206 static void hideWorkspaceName(void *data)
208 WScreen *scr = (WScreen *) data;
210 if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
211 || time(NULL) > scr->workspace_name_data->timeout) {
212 XUnmapWindow(dpy, scr->workspace_name);
214 if (scr->workspace_name_data) {
215 RReleaseImage(scr->workspace_name_data->back);
216 RReleaseImage(scr->workspace_name_data->text);
217 wfree(scr->workspace_name_data);
219 scr->workspace_name_data = NULL;
221 scr->workspace_name_timer = NULL;
222 } else {
223 RImage *img = RCloneImage(scr->workspace_name_data->back);
224 Pixmap pix;
226 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
228 RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
229 scr->workspace_name_data->count * 255 / 10);
231 RConvertImage(scr->rcontext, img, &pix);
233 RReleaseImage(img);
235 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
236 XClearWindow(dpy, scr->workspace_name);
237 XFreePixmap(dpy, pix);
238 XFlush(dpy);
240 scr->workspace_name_data->count--;
244 static void showWorkspaceName(WScreen * scr, int workspace)
246 WorkspaceNameData *data;
247 RXImage *ximg;
248 Pixmap text, mask;
249 int w, h;
250 int px, py;
251 char *name = scr->workspaces[workspace]->name;
252 int len = strlen(name);
253 int x, y;
254 #ifdef XINERAMA
255 int head;
256 WMRect rect;
257 int xx, yy;
258 #endif
260 if (wPreferences.workspace_name_display_position == WD_NONE || scr->workspace_count < 2) {
261 return;
264 if (scr->workspace_name_timer) {
265 WMDeleteTimerHandler(scr->workspace_name_timer);
266 XUnmapWindow(dpy, scr->workspace_name);
267 XFlush(dpy);
269 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY, hideWorkspaceName, scr);
271 if (scr->workspace_name_data) {
272 RReleaseImage(scr->workspace_name_data->back);
273 RReleaseImage(scr->workspace_name_data->text);
274 wfree(scr->workspace_name_data);
277 data = wmalloc(sizeof(WorkspaceNameData));
278 data->back = NULL;
280 w = WMWidthOfString(scr->workspace_name_font, name, len);
281 h = WMFontHeight(scr->workspace_name_font);
283 #ifdef XINERAMA
284 head = wGetHeadForPointerLocation(scr);
285 rect = wGetRectForHead(scr, head);
286 if (scr->xine_info.count) {
287 xx = rect.pos.x + (scr->xine_info.screens[head].size.width - (w + 4)) / 2;
288 yy = rect.pos.y + (scr->xine_info.screens[head].size.height - (h + 4)) / 2;
290 else {
291 xx = (scr->scr_width - (w + 4)) / 2;
292 yy = (scr->scr_height - (h + 4)) / 2;
294 #endif
296 switch (wPreferences.workspace_name_display_position) {
297 case WD_TOP:
298 #ifdef XINERAMA
299 px = xx;
300 #else
301 px = (scr->scr_width - (w + 4)) / 2;
302 #endif
303 py = WORKSPACE_NAME_DISPLAY_PADDING;
304 break;
305 case WD_BOTTOM:
306 #ifdef XINERAMA
307 px = xx;
308 #else
309 px = (scr->scr_width - (w + 4)) / 2;
310 #endif
311 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
312 break;
313 case WD_TOPLEFT:
314 px = WORKSPACE_NAME_DISPLAY_PADDING;
315 py = WORKSPACE_NAME_DISPLAY_PADDING;
316 break;
317 case WD_TOPRIGHT:
318 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
319 py = WORKSPACE_NAME_DISPLAY_PADDING;
320 break;
321 case WD_BOTTOMLEFT:
322 px = WORKSPACE_NAME_DISPLAY_PADDING;
323 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
324 break;
325 case WD_BOTTOMRIGHT:
326 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
327 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
328 break;
329 case WD_CENTER:
330 default:
331 #ifdef XINERAMA
332 px = xx;
333 py = yy;
334 #else
335 px = (scr->scr_width - (w + 4)) / 2;
336 py = (scr->scr_height - (h + 4)) / 2;
337 #endif
338 break;
340 XResizeWindow(dpy, scr->workspace_name, w + 4, h + 4);
341 XMoveWindow(dpy, scr->workspace_name, px, py);
343 text = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, scr->w_depth);
344 mask = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, 1);
346 /*XSetForeground(dpy, scr->mono_gc, 0);
347 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4); */
349 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
351 for (x = 0; x <= 4; x++) {
352 for (y = 0; y <= 4; y++) {
353 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, x, y, name, len);
357 XSetForeground(dpy, scr->mono_gc, 1);
358 XSetBackground(dpy, scr->mono_gc, 0);
360 XCopyPlane(dpy, text, mask, scr->mono_gc, 0, 0, w + 4, h + 4, 0, 0, 1 << (scr->w_depth - 1));
362 /*XSetForeground(dpy, scr->mono_gc, 1); */
363 XSetBackground(dpy, scr->mono_gc, 1);
365 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
367 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, 2, 2, name, len);
369 #ifdef SHAPE
370 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask, ShapeSet);
371 #endif
372 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
373 XClearWindow(dpy, scr->workspace_name);
375 data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
377 XFreePixmap(dpy, text);
378 XFreePixmap(dpy, mask);
380 if (!data->text) {
381 XMapRaised(dpy, scr->workspace_name);
382 XFlush(dpy);
384 goto erro;
387 ximg = RGetXImage(scr->rcontext, scr->root_win, px, py, data->text->width, data->text->height);
389 if (!ximg || !ximg->image) {
390 goto erro;
393 XMapRaised(dpy, scr->workspace_name);
394 XFlush(dpy);
396 data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
397 RDestroyXImage(scr->rcontext, ximg);
399 if (!data->back) {
400 goto erro;
403 data->count = 10;
405 /* set a timeout for the effect */
406 data->timeout = time(NULL) + 2 + (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY * data->count) / 1000;
408 scr->workspace_name_data = data;
410 return;
412 erro:
413 if (scr->workspace_name_timer)
414 WMDeleteTimerHandler(scr->workspace_name_timer);
416 if (data->text)
417 RReleaseImage(data->text);
418 if (data->back)
419 RReleaseImage(data->back);
420 wfree(data);
422 scr->workspace_name_data = NULL;
424 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
425 10 * WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
428 void wWorkspaceChange(WScreen *scr, int workspace)
430 if (scr->flags.startup || scr->flags.startup2 || scr->flags.ignore_focus_events)
431 return;
433 if (workspace != scr->current_workspace)
434 wWorkspaceForceChange(scr, workspace);
437 void wWorkspaceRelativeChange(WScreen * scr, int amount)
439 int w;
441 /* While the deiconify animation is going on the window is
442 * still "flying" to its final position and we don't want to
443 * change workspace before the animation finishes, otherwise
444 * the window will land in the new workspace */
445 if (ignore_wks_change)
446 return;
448 w = scr->current_workspace + amount;
450 if (amount < 0) {
451 if (w >= 0) {
452 wWorkspaceChange(scr, w);
453 } else if (wPreferences.ws_cycle) {
454 wWorkspaceChange(scr, scr->workspace_count + w);
456 } else if (amount > 0) {
457 if (w < scr->workspace_count) {
458 wWorkspaceChange(scr, w);
459 } else if (wPreferences.ws_advance) {
460 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES - 1));
461 } else if (wPreferences.ws_cycle) {
462 wWorkspaceChange(scr, w % scr->workspace_count);
467 void wWorkspaceForceChange(WScreen * scr, int workspace)
469 WWindow *tmp, *foc = NULL, *foc2 = NULL;
471 if (workspace >= MAX_WORKSPACES || workspace < 0)
472 return;
474 SendHelperMessage(scr, 'C', workspace + 1, NULL);
476 if (workspace > scr->workspace_count - 1) {
477 wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
480 wClipUpdateForWorkspaceChange(scr, workspace);
482 scr->current_workspace = workspace;
484 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
486 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
488 if ((tmp = scr->focused_window) != NULL) {
489 if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded) &&
490 !WFLAGP(tmp, no_focusable)) || tmp->flags.changing_workspace) {
491 foc = tmp;
494 /* foc2 = tmp; will fix annoyance with gnome panel
495 * but will create annoyance for every other application
497 while (tmp) {
498 if (tmp->frame->workspace != workspace && !tmp->flags.selected) {
499 /* unmap windows not on this workspace */
500 if ((tmp->flags.mapped || tmp->flags.shaded) &&
501 !IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) {
502 wWindowUnmap(tmp);
504 /* also unmap miniwindows not on this workspace */
505 if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
506 tmp->icon && !IS_OMNIPRESENT(tmp)) {
507 XUnmapWindow(dpy, tmp->icon->core->window);
508 tmp->icon->mapped = 0;
510 /* update current workspace of omnipresent windows */
511 if (IS_OMNIPRESENT(tmp)) {
512 WApplication *wapp = wApplicationOf(tmp->main_window);
514 tmp->frame->workspace = workspace;
516 if (wapp) {
517 wapp->last_workspace = workspace;
519 if (!foc2 && (tmp->flags.mapped || tmp->flags.shaded)) {
520 foc2 = tmp;
523 } else {
524 /* change selected windows' workspace */
525 if (tmp->flags.selected) {
526 wWindowChangeWorkspace(tmp, workspace);
527 if (!tmp->flags.miniaturized && !foc) {
528 foc = tmp;
530 } else {
531 if (!tmp->flags.hidden) {
532 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
533 /* remap windows that are on this workspace */
534 wWindowMap(tmp);
535 if (!foc && !WFLAGP(tmp, no_focusable)) {
536 foc = tmp;
539 /* Also map miniwindow if not omnipresent */
540 if (!wPreferences.sticky_icons &&
541 tmp->flags.miniaturized && !IS_OMNIPRESENT(tmp) && tmp->icon) {
542 tmp->icon->mapped = 1;
543 XMapWindow(dpy, tmp->icon->core->window);
548 tmp = tmp->prev;
551 /* Gobble up events unleashed by our mapping & unmapping.
552 * These may trigger various grab-initiated focus &
553 * crossing events. However, we don't care about them,
554 * and ignore their focus implications altogether to avoid
555 * flicker.
557 scr->flags.ignore_focus_events = 1;
558 ProcessPendingEvents();
559 scr->flags.ignore_focus_events = 0;
561 if (!foc)
562 foc = foc2;
564 if (scr->focused_window->flags.mapped && !foc) {
565 foc = scr->focused_window;
567 if (wPreferences.focus_mode == WKF_CLICK) {
568 wSetFocusTo(scr, foc);
569 } else {
570 unsigned int mask;
571 int foo;
572 Window bar, win;
573 WWindow *tmp;
575 tmp = NULL;
576 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask)) {
577 tmp = wWindowFor(win);
580 /* If there's a window under the pointer, focus it.
581 * (we ate all other focus events above, so it's
582 * certainly not focused). Otherwise focus last
583 * focused, or the root (depending on sloppiness)
585 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
586 wSetFocusTo(scr, foc);
587 } else {
588 wSetFocusTo(scr, tmp);
593 /* We need to always arrange icons when changing workspace, even if
594 * no autoarrange icons, because else the icons in different workspaces
595 * can be superposed.
596 * This can be avoided if appicons are also workspace specific.
598 if (!wPreferences.sticky_icons)
599 wArrangeIcons(scr, False);
601 if (scr->dock)
602 wAppIconPaint(scr->dock->icon_array[0]);
604 if (scr->clip_icon) {
605 if (scr->workspaces[workspace]->clip->auto_collapse ||
606 scr->workspaces[workspace]->clip->auto_raise_lower) {
607 /* to handle enter notify. This will also */
608 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
609 XMapWindow(dpy, scr->clip_icon->icon->core->window);
610 } else {
611 wClipIconPaint(scr->clip_icon);
614 wScreenUpdateUsableArea(scr);
615 wNETWMUpdateDesktop(scr);
616 showWorkspaceName(scr, workspace);
618 WMPostNotificationName(WMNWorkspaceChanged, scr, (void *)(uintptr_t) workspace);
620 /* XSync(dpy, False); */
623 static void switchWSCommand(WMenu * menu, WMenuEntry * entry)
625 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
628 static void deleteWSCommand(WMenu * menu, WMenuEntry * entry)
630 wWorkspaceDelete(menu->frame->screen_ptr, menu->frame->screen_ptr->workspace_count - 1);
633 static void newWSCommand(WMenu *menu, WMenuEntry *foo)
635 int ws;
637 ws = wWorkspaceNew(menu->frame->screen_ptr);
639 /* autochange workspace */
640 if (ws >= 0)
641 wWorkspaceChange(menu->frame->screen_ptr, ws);
644 void wWorkspaceRename(WScreen * scr, int workspace, char *name)
646 char buf[MAX_WORKSPACENAME_WIDTH + 1];
647 char *tmp;
649 if (workspace >= scr->workspace_count)
650 return;
652 /* trim white spaces */
653 tmp = wtrimspace(name);
655 if (strlen(tmp) == 0) {
656 snprintf(buf, sizeof(buf), _("Workspace %i"), workspace + 1);
657 } else {
658 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
660 buf[MAX_WORKSPACENAME_WIDTH] = 0;
661 wfree(tmp);
663 /* update workspace */
664 wfree(scr->workspaces[workspace]->name);
665 scr->workspaces[workspace]->name = wstrdup(buf);
667 if (scr->clip_ws_menu) {
668 if (strcmp(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
669 wfree(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text);
670 scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
671 wMenuRealize(scr->clip_ws_menu);
674 if (scr->workspace_menu) {
675 if (strcmp(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
676 wfree(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text);
677 scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
678 wMenuRealize(scr->workspace_menu);
682 if (scr->clip_icon)
683 wClipIconPaint(scr->clip_icon);
685 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) workspace);
688 /* callback for when menu entry is edited */
689 static void onMenuEntryEdited(WMenu * menu, WMenuEntry * entry)
691 char *tmp;
693 tmp = entry->text;
694 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
697 WMenu *wWorkspaceMenuMake(WScreen * scr, Bool titled)
699 WMenu *wsmenu;
701 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
702 if (!wsmenu) {
703 wwarning(_("could not create Workspace menu"));
704 return NULL;
707 /* callback to be called when an entry is edited */
708 wsmenu->on_edit = onMenuEntryEdited;
710 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
711 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
713 return wsmenu;
716 void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
718 int i;
719 long ws;
720 char title[MAX_WORKSPACENAME_WIDTH + 1];
721 WMenuEntry *entry;
722 int tmp;
724 if (!menu)
725 return;
727 if (menu->entry_no < scr->workspace_count + MC_WORKSPACE1) {
728 /* new workspace(s) added */
729 i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
730 ws = menu->entry_no - MC_WORKSPACE1;
731 while (i > 0) {
732 wstrlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
734 entry = wMenuAddCallback(menu, title, switchWSCommand, (void *)ws);
735 entry->flags.indicator = 1;
736 entry->flags.editable = 1;
738 i--;
739 ws++;
741 } else if (menu->entry_no > scr->workspace_count + MC_WORKSPACE1) {
742 /* removed workspace(s) */
743 for (i = menu->entry_no - 1; i >= scr->workspace_count + MC_WORKSPACE1; i--) {
744 wMenuRemoveItem(menu, i);
747 wMenuRealize(menu);
749 for (i = 0; i < scr->workspace_count; i++) {
750 menu->entries[i + MC_WORKSPACE1]->flags.indicator_on = 0;
752 menu->entries[scr->current_workspace + MC_WORKSPACE1]->flags.indicator_on = 1;
754 /* don't let user destroy current workspace */
755 if (scr->current_workspace == scr->workspace_count - 1) {
756 wMenuSetEnabled(menu, MC_DESTROY_LAST, False);
757 } else {
758 wMenuSetEnabled(menu, MC_DESTROY_LAST, True);
761 tmp = menu->frame->top_width + 5;
762 /* if menu got unreachable, bring it to a visible place */
763 if (menu->frame_x < tmp - (int)menu->frame->core->width)
764 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
766 wMenuPaint(menu);
769 void wWorkspaceSaveState(WScreen * scr, WMPropList * old_state)
771 WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
772 int i;
774 make_keys();
776 old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
777 parr = WMCreatePLArray(NULL);
778 for (i = 0; i < scr->workspace_count; i++) {
779 pstr = WMCreatePLString(scr->workspaces[i]->name);
780 wks_state = WMCreatePLDictionary(dName, pstr, NULL);
781 WMReleasePropList(pstr);
782 if (!wPreferences.flags.noclip) {
783 pstr = wClipSaveWorkspaceState(scr, i);
784 WMPutInPLDictionary(wks_state, dClip, pstr);
785 WMReleasePropList(pstr);
786 } else if (old_wks_state != NULL) {
787 if ((foo = WMGetFromPLArray(old_wks_state, i)) != NULL) {
788 if ((bar = WMGetFromPLDictionary(foo, dClip)) != NULL) {
789 WMPutInPLDictionary(wks_state, dClip, bar);
793 WMAddToPLArray(parr, wks_state);
794 WMReleasePropList(wks_state);
796 WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
797 WMReleasePropList(parr);
800 void wWorkspaceRestoreState(WScreen *scr)
802 WMPropList *parr, *pstr, *wks_state, *clip_state;
803 int i, j;
805 make_keys();
807 if (scr->session_state == NULL)
808 return;
810 parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
812 if (!parr)
813 return;
815 for (i = 0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
816 wks_state = WMGetFromPLArray(parr, i);
817 if (WMIsPLDictionary(wks_state))
818 pstr = WMGetFromPLDictionary(wks_state, dName);
819 else
820 pstr = wks_state;
822 if (i >= scr->workspace_count)
823 wWorkspaceNew(scr);
825 if (scr->workspace_menu) {
826 wfree(scr->workspace_menu->entries[i + MC_WORKSPACE1]->text);
827 scr->workspace_menu->entries[i + MC_WORKSPACE1]->text = wstrdup(WMGetFromPLString(pstr));
828 scr->workspace_menu->flags.realized = 0;
831 wfree(scr->workspaces[i]->name);
832 scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
833 if (!wPreferences.flags.noclip) {
834 int added_omnipresent_icons = 0;
836 clip_state = WMGetFromPLDictionary(wks_state, dClip);
837 if (scr->workspaces[i]->clip)
838 wDockDestroy(scr->workspaces[i]->clip);
840 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state, WM_CLIP);
841 if (i > 0)
842 wDockHideIcons(scr->workspaces[i]->clip);
844 /* We set the global icons here, because scr->workspaces[i]->clip
845 * was not valid in wDockRestoreState().
846 * There we only set icon->omnipresent to know which icons we
847 * need to set here.
849 for (j = 0; j < scr->workspaces[i]->clip->max_icons; j++) {
850 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
851 int k;
853 if (!aicon || !aicon->omnipresent)
854 continue;
855 aicon->omnipresent = 0;
856 if (wClipMakeIconOmnipresent(aicon, True) != WO_SUCCESS)
857 continue;
858 if (i == 0)
859 continue;
861 /* Move this appicon from workspace i to workspace 0 */
862 scr->workspaces[i]->clip->icon_array[j] = NULL;
863 scr->workspaces[i]->clip->icon_count--;
865 added_omnipresent_icons++;
866 /* If there are too many omnipresent appicons, we are in trouble */
867 assert(scr->workspaces[0]->clip->icon_count + added_omnipresent_icons
868 <= scr->workspaces[0]->clip->max_icons);
869 /* Find first free spot on workspace 0 */
870 for (k = 0; k < scr->workspaces[0]->clip->max_icons; k++)
871 if (scr->workspaces[0]->clip->icon_array[k] == NULL)
872 break;
873 scr->workspaces[0]->clip->icon_array[k] = aicon;
874 aicon->dock = scr->workspaces[0]->clip;
876 scr->workspaces[0]->clip->icon_count += added_omnipresent_icons;
879 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) i);
883 /* Returns the workspace number for a given workspace name */
884 int wGetWorkspaceNumber(WScreen * scr, char * value)
886 int w, i;
888 if (sscanf(value, "%i", &w) != 1) {
889 w = -1;
890 for (i = 0; i < scr->workspace_count; i++) {
891 if (strcmp(scr->workspaces[i]->name, value) == 0) {
892 w = i;
893 break;
896 } else {
897 w--;
900 return w;