Prevent crash when switchpanel is not initialised.
[wmaker-crm.git] / src / workspace.c
blobab79e6809d8d3deda839319d70f8faaab7f0a4bf
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 "misc.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 WShortKey wKeyBindings[WKBD_LAST];
66 static WMPropList *dWorkspaces = NULL;
67 static WMPropList *dClip, *dName;
69 static void make_keys(void)
71 if (dWorkspaces != NULL)
72 return;
74 dWorkspaces = WMCreatePLString("Workspaces");
75 dName = WMCreatePLString("Name");
76 dClip = WMCreatePLString("Clip");
79 void wWorkspaceMake(WScreen * scr, int count)
81 while (count > 0) {
82 wWorkspaceNew(scr);
83 count--;
87 int wWorkspaceNew(WScreen *scr)
89 WWorkspace *wspace, **list;
90 int i;
92 if (scr->workspace_count < MAX_WORKSPACES) {
93 scr->workspace_count++;
95 wspace = wmalloc(sizeof(WWorkspace));
96 wspace->name = NULL;
97 wspace->clip = NULL;
99 if (!wspace->name) {
100 wspace->name = wmalloc(strlen(_("Workspace %i")) + 8);
101 sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
104 if (!wPreferences.flags.noclip)
105 wspace->clip = wDockCreate(scr, WM_CLIP, NULL);
107 list = wmalloc(sizeof(WWorkspace *) * scr->workspace_count);
109 for (i = 0; i < scr->workspace_count - 1; i++)
110 list[i] = scr->workspaces[i];
112 list[i] = wspace;
113 if (scr->workspaces)
114 wfree(scr->workspaces);
116 scr->workspaces = list;
118 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
119 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
120 wNETWMUpdateDesktop(scr);
121 WMPostNotificationName(WMNWorkspaceCreated, scr, (void *)(uintptr_t) (scr->workspace_count - 1));
122 XFlush(dpy);
124 return scr->workspace_count - 1;
127 return -1;
130 Bool wWorkspaceDelete(WScreen * scr, int workspace)
132 WWindow *tmp;
133 WWorkspace **list;
134 int i, j;
136 if (workspace <= 0)
137 return False;
139 /* verify if workspace is in use by some window */
140 tmp = scr->focused_window;
141 while (tmp) {
142 if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace == workspace)
143 return False;
144 tmp = tmp->prev;
147 if (!wPreferences.flags.noclip) {
148 wDockDestroy(scr->workspaces[workspace]->clip);
149 scr->workspaces[workspace]->clip = NULL;
152 list = wmalloc(sizeof(WWorkspace *) * (scr->workspace_count - 1));
153 j = 0;
154 for (i = 0; i < scr->workspace_count; i++) {
155 if (i != workspace) {
156 list[j++] = scr->workspaces[i];
157 } else {
158 if (scr->workspaces[i]->name)
159 wfree(scr->workspaces[i]->name);
160 wfree(scr->workspaces[i]);
163 wfree(scr->workspaces);
164 scr->workspaces = list;
166 scr->workspace_count--;
168 /* update menu */
169 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
170 /* clip workspace menu */
171 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
173 /* update also window menu */
174 if (scr->workspace_submenu) {
175 WMenu *menu = scr->workspace_submenu;
177 i = menu->entry_no;
178 while (i > scr->workspace_count)
179 wMenuRemoveItem(menu, --i);
180 wMenuRealize(menu);
182 /* and clip menu */
183 if (scr->clip_submenu) {
184 WMenu *menu = scr->clip_submenu;
186 i = menu->entry_no;
187 while (i > scr->workspace_count)
188 wMenuRemoveItem(menu, --i);
189 wMenuRealize(menu);
191 wNETWMUpdateDesktop(scr);
192 WMPostNotificationName(WMNWorkspaceDestroyed, scr, (void *)(uintptr_t) (scr->workspace_count - 1));
194 if (scr->current_workspace >= scr->workspace_count)
195 wWorkspaceChange(scr, scr->workspace_count - 1);
196 if (scr->last_workspace >= scr->workspace_count)
197 scr->last_workspace = 0;
199 return True;
202 typedef struct WorkspaceNameData {
203 int count;
204 RImage *back;
205 RImage *text;
206 time_t timeout;
207 } WorkspaceNameData;
209 static void hideWorkspaceName(void *data)
211 WScreen *scr = (WScreen *) data;
213 if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
214 || time(NULL) > scr->workspace_name_data->timeout) {
215 XUnmapWindow(dpy, scr->workspace_name);
217 if (scr->workspace_name_data) {
218 RReleaseImage(scr->workspace_name_data->back);
219 RReleaseImage(scr->workspace_name_data->text);
220 wfree(scr->workspace_name_data);
222 scr->workspace_name_data = NULL;
224 scr->workspace_name_timer = NULL;
225 } else {
226 RImage *img = RCloneImage(scr->workspace_name_data->back);
227 Pixmap pix;
229 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
231 RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
232 scr->workspace_name_data->count * 255 / 10);
234 RConvertImage(scr->rcontext, img, &pix);
236 RReleaseImage(img);
238 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
239 XClearWindow(dpy, scr->workspace_name);
240 XFreePixmap(dpy, pix);
241 XFlush(dpy);
243 scr->workspace_name_data->count--;
247 static void showWorkspaceName(WScreen * scr, int workspace)
249 WorkspaceNameData *data;
250 RXImage *ximg;
251 Pixmap text, mask;
252 int w, h;
253 int px, py;
254 char *name = scr->workspaces[workspace]->name;
255 int len = strlen(name);
256 int x, y;
257 #ifdef XINERAMA
258 int head;
259 WMRect rect;
260 int xx, yy;
261 #endif
263 if (wPreferences.workspace_name_display_position == WD_NONE || scr->workspace_count < 2) {
264 return;
267 if (scr->workspace_name_timer) {
268 WMDeleteTimerHandler(scr->workspace_name_timer);
269 XUnmapWindow(dpy, scr->workspace_name);
270 XFlush(dpy);
272 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY, hideWorkspaceName, scr);
274 if (scr->workspace_name_data) {
275 RReleaseImage(scr->workspace_name_data->back);
276 RReleaseImage(scr->workspace_name_data->text);
277 wfree(scr->workspace_name_data);
280 data = wmalloc(sizeof(WorkspaceNameData));
281 data->back = NULL;
283 w = WMWidthOfString(scr->workspace_name_font, name, len);
284 h = WMFontHeight(scr->workspace_name_font);
286 #ifdef XINERAMA
287 head = wGetHeadForPointerLocation(scr);
288 rect = wGetRectForHead(scr, head);
289 if (scr->xine_info.count) {
290 xx = rect.pos.x + (scr->xine_info.screens[head].size.width - (w + 4)) / 2;
291 yy = rect.pos.y + (scr->xine_info.screens[head].size.height - (h + 4)) / 2;
293 else {
294 xx = (scr->scr_width - (w + 4)) / 2;
295 yy = (scr->scr_height - (h + 4)) / 2;
297 #endif
299 switch (wPreferences.workspace_name_display_position) {
300 case WD_TOP:
301 #ifdef XINERAMA
302 px = xx;
303 #else
304 px = (scr->scr_width - (w + 4)) / 2;
305 #endif
306 py = WORKSPACE_NAME_DISPLAY_PADDING;
307 break;
308 case WD_BOTTOM:
309 #ifdef XINERAMA
310 px = xx;
311 #else
312 px = (scr->scr_width - (w + 4)) / 2;
313 #endif
314 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
315 break;
316 case WD_TOPLEFT:
317 px = WORKSPACE_NAME_DISPLAY_PADDING;
318 py = WORKSPACE_NAME_DISPLAY_PADDING;
319 break;
320 case WD_TOPRIGHT:
321 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
322 py = WORKSPACE_NAME_DISPLAY_PADDING;
323 break;
324 case WD_BOTTOMLEFT:
325 px = WORKSPACE_NAME_DISPLAY_PADDING;
326 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
327 break;
328 case WD_BOTTOMRIGHT:
329 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
330 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
331 break;
332 case WD_CENTER:
333 default:
334 #ifdef XINERAMA
335 px = xx;
336 py = yy;
337 #else
338 px = (scr->scr_width - (w + 4)) / 2;
339 py = (scr->scr_height - (h + 4)) / 2;
340 #endif
341 break;
343 XResizeWindow(dpy, scr->workspace_name, w + 4, h + 4);
344 XMoveWindow(dpy, scr->workspace_name, px, py);
346 text = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, scr->w_depth);
347 mask = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, 1);
349 /*XSetForeground(dpy, scr->mono_gc, 0);
350 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4); */
352 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
354 for (x = 0; x <= 4; x++) {
355 for (y = 0; y <= 4; y++) {
356 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, x, y, name, len);
360 XSetForeground(dpy, scr->mono_gc, 1);
361 XSetBackground(dpy, scr->mono_gc, 0);
363 XCopyPlane(dpy, text, mask, scr->mono_gc, 0, 0, w + 4, h + 4, 0, 0, 1 << (scr->w_depth - 1));
365 /*XSetForeground(dpy, scr->mono_gc, 1); */
366 XSetBackground(dpy, scr->mono_gc, 1);
368 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
370 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, 2, 2, name, len);
372 #ifdef SHAPE
373 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask, ShapeSet);
374 #endif
375 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
376 XClearWindow(dpy, scr->workspace_name);
378 data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
380 XFreePixmap(dpy, text);
381 XFreePixmap(dpy, mask);
383 if (!data->text) {
384 XMapRaised(dpy, scr->workspace_name);
385 XFlush(dpy);
387 goto erro;
390 ximg = RGetXImage(scr->rcontext, scr->root_win, px, py, data->text->width, data->text->height);
392 if (!ximg || !ximg->image) {
393 goto erro;
396 XMapRaised(dpy, scr->workspace_name);
397 XFlush(dpy);
399 data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
400 RDestroyXImage(scr->rcontext, ximg);
402 if (!data->back) {
403 goto erro;
406 data->count = 10;
408 /* set a timeout for the effect */
409 data->timeout = time(NULL) + 2 + (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY * data->count) / 1000;
411 scr->workspace_name_data = data;
413 return;
415 erro:
416 if (scr->workspace_name_timer)
417 WMDeleteTimerHandler(scr->workspace_name_timer);
419 if (data->text)
420 RReleaseImage(data->text);
421 if (data->back)
422 RReleaseImage(data->back);
423 wfree(data);
425 scr->workspace_name_data = NULL;
427 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
428 10 * WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
431 void wWorkspaceChange(WScreen *scr, int workspace)
433 if (scr->flags.startup || scr->flags.startup2 || scr->flags.ignore_focus_events)
434 return;
436 if (workspace != scr->current_workspace)
437 wWorkspaceForceChange(scr, workspace);
440 void wWorkspaceRelativeChange(WScreen * scr, int amount)
442 int w;
444 /* While the deiconify animation is going on the window is
445 * still "flying" to its final position and we don't want to
446 * change workspace before the animation finishes, otherwise
447 * the window will land in the new workspace */
448 if (ignore_wks_change)
449 return;
451 w = scr->current_workspace + amount;
453 if (amount < 0) {
454 if (w >= 0) {
455 wWorkspaceChange(scr, w);
456 } else if (wPreferences.ws_cycle) {
457 wWorkspaceChange(scr, scr->workspace_count + w);
459 } else if (amount > 0) {
460 if (w < scr->workspace_count) {
461 wWorkspaceChange(scr, w);
462 } else if (wPreferences.ws_advance) {
463 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES - 1));
464 } else if (wPreferences.ws_cycle) {
465 wWorkspaceChange(scr, w % scr->workspace_count);
470 void wWorkspaceForceChange(WScreen * scr, int workspace)
472 WWindow *tmp, *foc = NULL, *foc2 = NULL;
473 WWindow **toUnmap;
474 int toUnmapSize, toUnmapCount;
476 if (workspace >= MAX_WORKSPACES || workspace < 0)
477 return;
479 SendHelperMessage(scr, 'C', workspace + 1, NULL);
481 if (workspace > scr->workspace_count - 1) {
482 wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
485 wClipUpdateForWorkspaceChange(scr, workspace);
487 scr->last_workspace = scr->current_workspace;
488 scr->current_workspace = workspace;
490 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
492 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
494 toUnmapSize = 16;
495 toUnmapCount = 0;
496 toUnmap = wmalloc(toUnmapSize * sizeof(WWindow *));
498 if ((tmp = scr->focused_window) != NULL) {
499 if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded) &&
500 !WFLAGP(tmp, no_focusable)) || tmp->flags.changing_workspace) {
501 foc = tmp;
504 /* foc2 = tmp; will fix annoyance with gnome panel
505 * but will create annoyance for every other application
507 while (tmp) {
508 if (tmp->frame->workspace != workspace && !tmp->flags.selected) {
509 /* unmap windows not on this workspace */
510 if ((tmp->flags.mapped || tmp->flags.shaded) &&
511 !IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) {
512 if (toUnmapCount == toUnmapSize)
514 toUnmapSize *= 2;
515 toUnmap = wrealloc(toUnmap, toUnmapSize * sizeof(WWindow *));
517 toUnmap[toUnmapCount++] = tmp;
519 /* also unmap miniwindows not on this workspace */
520 if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
521 tmp->icon && !IS_OMNIPRESENT(tmp)) {
522 XUnmapWindow(dpy, tmp->icon->core->window);
523 tmp->icon->mapped = 0;
525 /* update current workspace of omnipresent windows */
526 if (IS_OMNIPRESENT(tmp)) {
527 WApplication *wapp = wApplicationOf(tmp->main_window);
529 tmp->frame->workspace = workspace;
531 if (wapp) {
532 wapp->last_workspace = workspace;
534 if (!foc2 && (tmp->flags.mapped || tmp->flags.shaded)) {
535 foc2 = tmp;
538 } else {
539 /* change selected windows' workspace */
540 if (tmp->flags.selected) {
541 wWindowChangeWorkspace(tmp, workspace);
542 if (!tmp->flags.miniaturized && !foc) {
543 foc = tmp;
545 } else {
546 if (!tmp->flags.hidden) {
547 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
548 /* remap windows that are on this workspace */
549 wWindowMap(tmp);
550 if (!foc && !WFLAGP(tmp, no_focusable)) {
551 foc = tmp;
554 /* Also map miniwindow if not omnipresent */
555 if (!wPreferences.sticky_icons &&
556 tmp->flags.miniaturized && !IS_OMNIPRESENT(tmp) && tmp->icon) {
557 tmp->icon->mapped = 1;
558 XMapWindow(dpy, tmp->icon->core->window);
563 tmp = tmp->prev;
566 while (toUnmapCount > 0)
568 wWindowUnmap(toUnmap[--toUnmapCount]);
570 wfree(toUnmap);
572 /* Gobble up events unleashed by our mapping & unmapping.
573 * These may trigger various grab-initiated focus &
574 * crossing events. However, we don't care about them,
575 * and ignore their focus implications altogether to avoid
576 * flicker.
578 scr->flags.ignore_focus_events = 1;
579 ProcessPendingEvents();
580 scr->flags.ignore_focus_events = 0;
582 if (!foc)
583 foc = foc2;
585 if (scr->focused_window->flags.mapped && !foc) {
586 foc = scr->focused_window;
588 if (wPreferences.focus_mode == WKF_CLICK) {
589 wSetFocusTo(scr, foc);
590 } else {
591 unsigned int mask;
592 int foo;
593 Window bar, win;
594 WWindow *tmp;
596 tmp = NULL;
597 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask)) {
598 tmp = wWindowFor(win);
601 /* If there's a window under the pointer, focus it.
602 * (we ate all other focus events above, so it's
603 * certainly not focused). Otherwise focus last
604 * focused, or the root (depending on sloppiness)
606 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
607 wSetFocusTo(scr, foc);
608 } else {
609 wSetFocusTo(scr, tmp);
614 /* We need to always arrange icons when changing workspace, even if
615 * no autoarrange icons, because else the icons in different workspaces
616 * can be superposed.
617 * This can be avoided if appicons are also workspace specific.
619 if (!wPreferences.sticky_icons)
620 wArrangeIcons(scr, False);
622 if (scr->dock)
623 wAppIconPaint(scr->dock->icon_array[0]);
625 if (!wPreferences.flags.noclip && (scr->workspaces[workspace]->clip->auto_collapse ||
626 scr->workspaces[workspace]->clip->auto_raise_lower)) {
627 /* to handle enter notify. This will also */
628 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
629 XMapWindow(dpy, scr->clip_icon->icon->core->window);
631 else if (scr->clip_icon != NULL) {
632 wClipIconPaint(scr->clip_icon);
634 wScreenUpdateUsableArea(scr);
635 wNETWMUpdateDesktop(scr);
636 showWorkspaceName(scr, workspace);
638 WMPostNotificationName(WMNWorkspaceChanged, scr, (void *)(uintptr_t) workspace);
640 /* XSync(dpy, False); */
643 static void switchWSCommand(WMenu * menu, WMenuEntry * entry)
645 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
648 static void lastWSCommand(WMenu * menu, WMenuEntry * entry)
650 wWorkspaceChange(menu->frame->screen_ptr, menu->frame->screen_ptr->last_workspace);
653 static void deleteWSCommand(WMenu * menu, WMenuEntry * entry)
655 wWorkspaceDelete(menu->frame->screen_ptr, menu->frame->screen_ptr->workspace_count - 1);
658 static void newWSCommand(WMenu *menu, WMenuEntry *foo)
660 int ws;
662 ws = wWorkspaceNew(menu->frame->screen_ptr);
664 /* autochange workspace */
665 if (ws >= 0)
666 wWorkspaceChange(menu->frame->screen_ptr, ws);
669 void wWorkspaceRename(WScreen * scr, int workspace, char *name)
671 char buf[MAX_WORKSPACENAME_WIDTH + 1];
672 char *tmp;
674 if (workspace >= scr->workspace_count)
675 return;
677 /* trim white spaces */
678 tmp = wtrimspace(name);
680 if (strlen(tmp) == 0) {
681 snprintf(buf, sizeof(buf), _("Workspace %i"), workspace + 1);
682 } else {
683 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
685 buf[MAX_WORKSPACENAME_WIDTH] = 0;
686 wfree(tmp);
688 /* update workspace */
689 wfree(scr->workspaces[workspace]->name);
690 scr->workspaces[workspace]->name = wstrdup(buf);
692 if (scr->clip_ws_menu) {
693 if (strcmp(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
694 wfree(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text);
695 scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
696 wMenuRealize(scr->clip_ws_menu);
699 if (scr->workspace_menu) {
700 if (strcmp(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
701 wfree(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text);
702 scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
703 wMenuRealize(scr->workspace_menu);
707 if (scr->clip_icon)
708 wClipIconPaint(scr->clip_icon);
710 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) workspace);
713 /* callback for when menu entry is edited */
714 static void onMenuEntryEdited(WMenu * menu, WMenuEntry * entry)
716 char *tmp;
718 tmp = entry->text;
719 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
722 WMenu *wWorkspaceMenuMake(WScreen * scr, Bool titled)
724 WMenu *wsmenu;
725 WMenuEntry *entry;
727 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
728 if (!wsmenu) {
729 wwarning(_("could not create Workspace menu"));
730 return NULL;
733 /* callback to be called when an entry is edited */
734 wsmenu->on_edit = onMenuEntryEdited;
736 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
737 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
739 entry = wMenuAddCallback(wsmenu, _("Last Used"), lastWSCommand, NULL);
740 entry->rtext = GetShortcutKey(wKeyBindings[WKBD_LASTWORKSPACE]);
742 return wsmenu;
745 void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
747 int i;
748 long ws;
749 char title[MAX_WORKSPACENAME_WIDTH + 1];
750 WMenuEntry *entry;
751 int tmp;
753 if (!menu)
754 return;
756 if (menu->entry_no < scr->workspace_count + MC_WORKSPACE1) {
757 /* new workspace(s) added */
758 i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
759 ws = menu->entry_no - MC_WORKSPACE1;
760 while (i > 0) {
761 wstrlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
763 entry = wMenuAddCallback(menu, title, switchWSCommand, (void *)ws);
764 entry->flags.indicator = 1;
765 entry->flags.editable = 1;
767 i--;
768 ws++;
770 } else if (menu->entry_no > scr->workspace_count + MC_WORKSPACE1) {
771 /* removed workspace(s) */
772 for (i = menu->entry_no - 1; i >= scr->workspace_count + MC_WORKSPACE1; i--) {
773 wMenuRemoveItem(menu, i);
777 for (i = 0; i < scr->workspace_count; i++) {
778 /* workspace shortcut labels */
779 if (i / 10 == scr->current_workspace / 10)
780 menu->entries[i + MC_WORKSPACE1]->rtext = GetShortcutKey(wKeyBindings[WKBD_WORKSPACE1 + (i % 10)]);
781 else
782 menu->entries[i + MC_WORKSPACE1]->rtext = NULL;
784 menu->entries[i + MC_WORKSPACE1]->flags.indicator_on = 0;
786 menu->entries[scr->current_workspace + MC_WORKSPACE1]->flags.indicator_on = 1;
787 wMenuRealize(menu);
789 /* don't let user destroy current workspace */
790 if (scr->current_workspace == scr->workspace_count - 1) {
791 wMenuSetEnabled(menu, MC_DESTROY_LAST, False);
792 } else {
793 wMenuSetEnabled(menu, MC_DESTROY_LAST, True);
796 /* back to last workspace */
797 if (scr->workspace_count && scr->last_workspace != scr->current_workspace)
798 wMenuSetEnabled(menu, MC_LAST_USED, True);
799 else
800 wMenuSetEnabled(menu, MC_LAST_USED, False);
802 tmp = menu->frame->top_width + 5;
803 /* if menu got unreachable, bring it to a visible place */
804 if (menu->frame_x < tmp - (int)menu->frame->core->width)
805 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
807 wMenuPaint(menu);
810 void wWorkspaceSaveState(WScreen * scr, WMPropList * old_state)
812 WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
813 int i;
815 make_keys();
817 old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
818 parr = WMCreatePLArray(NULL);
819 for (i = 0; i < scr->workspace_count; i++) {
820 pstr = WMCreatePLString(scr->workspaces[i]->name);
821 wks_state = WMCreatePLDictionary(dName, pstr, NULL);
822 WMReleasePropList(pstr);
823 if (!wPreferences.flags.noclip) {
824 pstr = wClipSaveWorkspaceState(scr, i);
825 WMPutInPLDictionary(wks_state, dClip, pstr);
826 WMReleasePropList(pstr);
827 } else if (old_wks_state != NULL) {
828 if ((foo = WMGetFromPLArray(old_wks_state, i)) != NULL) {
829 if ((bar = WMGetFromPLDictionary(foo, dClip)) != NULL) {
830 WMPutInPLDictionary(wks_state, dClip, bar);
834 WMAddToPLArray(parr, wks_state);
835 WMReleasePropList(wks_state);
837 WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
838 WMReleasePropList(parr);
841 void wWorkspaceRestoreState(WScreen *scr)
843 WMPropList *parr, *pstr, *wks_state, *clip_state;
844 int i, j;
846 make_keys();
848 if (scr->session_state == NULL)
849 return;
851 parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
853 if (!parr)
854 return;
856 for (i = 0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
857 wks_state = WMGetFromPLArray(parr, i);
858 if (WMIsPLDictionary(wks_state))
859 pstr = WMGetFromPLDictionary(wks_state, dName);
860 else
861 pstr = wks_state;
863 if (i >= scr->workspace_count)
864 wWorkspaceNew(scr);
866 if (scr->workspace_menu) {
867 wfree(scr->workspace_menu->entries[i + MC_WORKSPACE1]->text);
868 scr->workspace_menu->entries[i + MC_WORKSPACE1]->text = wstrdup(WMGetFromPLString(pstr));
869 scr->workspace_menu->flags.realized = 0;
872 wfree(scr->workspaces[i]->name);
873 scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
874 if (!wPreferences.flags.noclip) {
875 int added_omnipresent_icons = 0;
877 clip_state = WMGetFromPLDictionary(wks_state, dClip);
878 if (scr->workspaces[i]->clip)
879 wDockDestroy(scr->workspaces[i]->clip);
881 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state, WM_CLIP);
882 if (i > 0)
883 wDockHideIcons(scr->workspaces[i]->clip);
885 /* We set the global icons here, because scr->workspaces[i]->clip
886 * was not valid in wDockRestoreState().
887 * There we only set icon->omnipresent to know which icons we
888 * need to set here.
890 for (j = 0; j < scr->workspaces[i]->clip->max_icons; j++) {
891 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
892 int k;
894 if (!aicon || !aicon->omnipresent)
895 continue;
896 aicon->omnipresent = 0;
897 if (wClipMakeIconOmnipresent(aicon, True) != WO_SUCCESS)
898 continue;
899 if (i == 0)
900 continue;
902 /* Move this appicon from workspace i to workspace 0 */
903 scr->workspaces[i]->clip->icon_array[j] = NULL;
904 scr->workspaces[i]->clip->icon_count--;
906 added_omnipresent_icons++;
907 /* If there are too many omnipresent appicons, we are in trouble */
908 assert(scr->workspaces[0]->clip->icon_count + added_omnipresent_icons
909 <= scr->workspaces[0]->clip->max_icons);
910 /* Find first free spot on workspace 0 */
911 for (k = 0; k < scr->workspaces[0]->clip->max_icons; k++)
912 if (scr->workspaces[0]->clip->icon_array[k] == NULL)
913 break;
914 scr->workspaces[0]->clip->icon_array[k] = aicon;
915 aicon->dock = scr->workspaces[0]->clip;
917 scr->workspaces[0]->clip->icon_count += added_omnipresent_icons;
920 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) i);
924 /* Returns the workspace number for a given workspace name */
925 int wGetWorkspaceNumber(WScreen * scr, char * value)
927 int w, i;
929 if (sscanf(value, "%i", &w) != 1) {
930 w = -1;
931 for (i = 0; i < scr->workspace_count; i++) {
932 if (strcmp(scr->workspaces[i]->name, value) == 0) {
933 w = i;
934 break;
937 } else {
938 w--;
941 return w;