wmaker: Removed variable for X Context that is not used
[wmaker-crm.git] / src / workspace.c
blob0b3d3d0f69a3e1dc35073aabd3af16d3efbcb29a
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"
51 #include "event.h"
53 #define MC_NEW 0
54 #define MC_DESTROY_LAST 1
55 #define MC_LAST_USED 2
56 /* index of the first workspace menu entry */
57 #define MC_WORKSPACE1 3
59 #define MAX_SHORTCUT_LENGTH 32
60 #define WORKSPACE_NAME_DISPLAY_PADDING 32
62 extern int ignore_wks_change;
63 extern WShortKey wKeyBindings[WKBD_LAST];
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, NULL);
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);
195 if (scr->last_workspace >= scr->workspace_count)
196 scr->last_workspace = 0;
198 return True;
201 typedef struct WorkspaceNameData {
202 int count;
203 RImage *back;
204 RImage *text;
205 time_t timeout;
206 } WorkspaceNameData;
208 static void hideWorkspaceName(void *data)
210 WScreen *scr = (WScreen *) data;
212 if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
213 || time(NULL) > scr->workspace_name_data->timeout) {
214 XUnmapWindow(dpy, scr->workspace_name);
216 if (scr->workspace_name_data) {
217 RReleaseImage(scr->workspace_name_data->back);
218 RReleaseImage(scr->workspace_name_data->text);
219 wfree(scr->workspace_name_data);
221 scr->workspace_name_data = NULL;
223 scr->workspace_name_timer = NULL;
224 } else {
225 RImage *img = RCloneImage(scr->workspace_name_data->back);
226 Pixmap pix;
228 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
230 RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
231 scr->workspace_name_data->count * 255 / 10);
233 RConvertImage(scr->rcontext, img, &pix);
235 RReleaseImage(img);
237 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
238 XClearWindow(dpy, scr->workspace_name);
239 XFreePixmap(dpy, pix);
240 XFlush(dpy);
242 scr->workspace_name_data->count--;
246 static void showWorkspaceName(WScreen * scr, int workspace)
248 WorkspaceNameData *data;
249 RXImage *ximg;
250 Pixmap text, mask;
251 int w, h;
252 int px, py;
253 char *name = scr->workspaces[workspace]->name;
254 int len = strlen(name);
255 int x, y;
256 #ifdef XINERAMA
257 int head;
258 WMRect rect;
259 int xx, yy;
260 #endif
262 if (wPreferences.workspace_name_display_position == WD_NONE || scr->workspace_count < 2) {
263 return;
266 if (scr->workspace_name_timer) {
267 WMDeleteTimerHandler(scr->workspace_name_timer);
268 XUnmapWindow(dpy, scr->workspace_name);
269 XFlush(dpy);
271 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY, hideWorkspaceName, scr);
273 if (scr->workspace_name_data) {
274 RReleaseImage(scr->workspace_name_data->back);
275 RReleaseImage(scr->workspace_name_data->text);
276 wfree(scr->workspace_name_data);
279 data = wmalloc(sizeof(WorkspaceNameData));
280 data->back = NULL;
282 w = WMWidthOfString(scr->workspace_name_font, name, len);
283 h = WMFontHeight(scr->workspace_name_font);
285 #ifdef XINERAMA
286 head = wGetHeadForPointerLocation(scr);
287 rect = wGetRectForHead(scr, head);
288 if (scr->xine_info.count) {
289 xx = rect.pos.x + (scr->xine_info.screens[head].size.width - (w + 4)) / 2;
290 yy = rect.pos.y + (scr->xine_info.screens[head].size.height - (h + 4)) / 2;
292 else {
293 xx = (scr->scr_width - (w + 4)) / 2;
294 yy = (scr->scr_height - (h + 4)) / 2;
296 #endif
298 switch (wPreferences.workspace_name_display_position) {
299 case WD_TOP:
300 #ifdef XINERAMA
301 px = xx;
302 #else
303 px = (scr->scr_width - (w + 4)) / 2;
304 #endif
305 py = WORKSPACE_NAME_DISPLAY_PADDING;
306 break;
307 case WD_BOTTOM:
308 #ifdef XINERAMA
309 px = xx;
310 #else
311 px = (scr->scr_width - (w + 4)) / 2;
312 #endif
313 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
314 break;
315 case WD_TOPLEFT:
316 px = WORKSPACE_NAME_DISPLAY_PADDING;
317 py = WORKSPACE_NAME_DISPLAY_PADDING;
318 break;
319 case WD_TOPRIGHT:
320 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
321 py = WORKSPACE_NAME_DISPLAY_PADDING;
322 break;
323 case WD_BOTTOMLEFT:
324 px = WORKSPACE_NAME_DISPLAY_PADDING;
325 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
326 break;
327 case WD_BOTTOMRIGHT:
328 px = scr->scr_width - (w + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
329 py = scr->scr_height - (h + 4 + WORKSPACE_NAME_DISPLAY_PADDING);
330 break;
331 case WD_CENTER:
332 default:
333 #ifdef XINERAMA
334 px = xx;
335 py = yy;
336 #else
337 px = (scr->scr_width - (w + 4)) / 2;
338 py = (scr->scr_height - (h + 4)) / 2;
339 #endif
340 break;
342 XResizeWindow(dpy, scr->workspace_name, w + 4, h + 4);
343 XMoveWindow(dpy, scr->workspace_name, px, py);
345 text = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, scr->w_depth);
346 mask = XCreatePixmap(dpy, scr->w_win, w + 4, h + 4, 1);
348 /*XSetForeground(dpy, scr->mono_gc, 0);
349 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4); */
351 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
353 for (x = 0; x <= 4; x++) {
354 for (y = 0; y <= 4; y++) {
355 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, x, y, name, len);
359 XSetForeground(dpy, scr->mono_gc, 1);
360 XSetBackground(dpy, scr->mono_gc, 0);
362 XCopyPlane(dpy, text, mask, scr->mono_gc, 0, 0, w + 4, h + 4, 0, 0, 1 << (scr->w_depth - 1));
364 /*XSetForeground(dpy, scr->mono_gc, 1); */
365 XSetBackground(dpy, scr->mono_gc, 1);
367 XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w + 4, h + 4);
369 WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font, 2, 2, name, len);
371 #ifdef SHAPE
372 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask, ShapeSet);
373 #endif
374 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
375 XClearWindow(dpy, scr->workspace_name);
377 data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
379 XFreePixmap(dpy, text);
380 XFreePixmap(dpy, mask);
382 if (!data->text) {
383 XMapRaised(dpy, scr->workspace_name);
384 XFlush(dpy);
386 goto erro;
389 ximg = RGetXImage(scr->rcontext, scr->root_win, px, py, data->text->width, data->text->height);
391 if (!ximg || !ximg->image) {
392 goto erro;
395 XMapRaised(dpy, scr->workspace_name);
396 XFlush(dpy);
398 data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
399 RDestroyXImage(scr->rcontext, ximg);
401 if (!data->back) {
402 goto erro;
405 data->count = 10;
407 /* set a timeout for the effect */
408 data->timeout = time(NULL) + 2 + (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY * data->count) / 1000;
410 scr->workspace_name_data = data;
412 return;
414 erro:
415 if (scr->workspace_name_timer)
416 WMDeleteTimerHandler(scr->workspace_name_timer);
418 if (data->text)
419 RReleaseImage(data->text);
420 if (data->back)
421 RReleaseImage(data->back);
422 wfree(data);
424 scr->workspace_name_data = NULL;
426 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
427 10 * WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
430 void wWorkspaceChange(WScreen *scr, int workspace)
432 if (scr->flags.startup || scr->flags.startup2 || scr->flags.ignore_focus_events)
433 return;
435 if (workspace != scr->current_workspace)
436 wWorkspaceForceChange(scr, workspace);
439 void wWorkspaceRelativeChange(WScreen * scr, int amount)
441 int w;
443 /* While the deiconify animation is going on the window is
444 * still "flying" to its final position and we don't want to
445 * change workspace before the animation finishes, otherwise
446 * the window will land in the new workspace */
447 if (ignore_wks_change)
448 return;
450 w = scr->current_workspace + amount;
452 if (amount < 0) {
453 if (w >= 0) {
454 wWorkspaceChange(scr, w);
455 } else if (wPreferences.ws_cycle) {
456 wWorkspaceChange(scr, scr->workspace_count + w);
458 } else if (amount > 0) {
459 if (w < scr->workspace_count) {
460 wWorkspaceChange(scr, w);
461 } else if (wPreferences.ws_advance) {
462 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES - 1));
463 } else if (wPreferences.ws_cycle) {
464 wWorkspaceChange(scr, w % scr->workspace_count);
469 void wWorkspaceForceChange(WScreen * scr, int workspace)
471 WWindow *tmp, *foc = NULL, *foc2 = NULL;
472 WWindow **toUnmap;
473 int toUnmapSize, toUnmapCount;
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 toUnmapSize = 16;
494 toUnmapCount = 0;
495 toUnmap = wmalloc(toUnmapSize * sizeof(WWindow *));
497 if ((tmp = scr->focused_window) != NULL) {
498 if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded) &&
499 !WFLAGP(tmp, no_focusable)) || tmp->flags.changing_workspace) {
500 foc = tmp;
503 /* foc2 = tmp; will fix annoyance with gnome panel
504 * but will create annoyance for every other application
506 while (tmp) {
507 if (tmp->frame->workspace != workspace && !tmp->flags.selected) {
508 /* unmap windows not on this workspace */
509 if ((tmp->flags.mapped || tmp->flags.shaded) &&
510 !IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) {
511 if (toUnmapCount == toUnmapSize)
513 toUnmapSize *= 2;
514 toUnmap = wrealloc(toUnmap, toUnmapSize * sizeof(WWindow *));
516 toUnmap[toUnmapCount++] = tmp;
518 /* also unmap miniwindows not on this workspace */
519 if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
520 tmp->icon && !IS_OMNIPRESENT(tmp)) {
521 XUnmapWindow(dpy, tmp->icon->core->window);
522 tmp->icon->mapped = 0;
524 /* update current workspace of omnipresent windows */
525 if (IS_OMNIPRESENT(tmp)) {
526 WApplication *wapp = wApplicationOf(tmp->main_window);
528 tmp->frame->workspace = workspace;
530 if (wapp) {
531 wapp->last_workspace = workspace;
533 if (!foc2 && (tmp->flags.mapped || tmp->flags.shaded)) {
534 foc2 = tmp;
537 } else {
538 /* change selected windows' workspace */
539 if (tmp->flags.selected) {
540 wWindowChangeWorkspace(tmp, workspace);
541 if (!tmp->flags.miniaturized && !foc) {
542 foc = tmp;
544 } else {
545 if (!tmp->flags.hidden) {
546 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
547 /* remap windows that are on this workspace */
548 wWindowMap(tmp);
549 if (!foc && !WFLAGP(tmp, no_focusable)) {
550 foc = tmp;
553 /* Also map miniwindow if not omnipresent */
554 if (!wPreferences.sticky_icons &&
555 tmp->flags.miniaturized && !IS_OMNIPRESENT(tmp) && tmp->icon) {
556 tmp->icon->mapped = 1;
557 XMapWindow(dpy, tmp->icon->core->window);
562 tmp = tmp->prev;
565 while (toUnmapCount > 0)
567 wWindowUnmap(toUnmap[--toUnmapCount]);
569 wfree(toUnmap);
571 /* Gobble up events unleashed by our mapping & unmapping.
572 * These may trigger various grab-initiated focus &
573 * crossing events. However, we don't care about them,
574 * and ignore their focus implications altogether to avoid
575 * flicker.
577 scr->flags.ignore_focus_events = 1;
578 ProcessPendingEvents();
579 scr->flags.ignore_focus_events = 0;
581 if (!foc)
582 foc = foc2;
584 if (scr->focused_window->flags.mapped && !foc) {
585 foc = scr->focused_window;
587 if (wPreferences.focus_mode == WKF_CLICK) {
588 wSetFocusTo(scr, foc);
589 } else {
590 unsigned int mask;
591 int foo;
592 Window bar, win;
593 WWindow *tmp;
595 tmp = NULL;
596 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask)) {
597 tmp = wWindowFor(win);
600 /* If there's a window under the pointer, focus it.
601 * (we ate all other focus events above, so it's
602 * certainly not focused). Otherwise focus last
603 * focused, or the root (depending on sloppiness)
605 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
606 wSetFocusTo(scr, foc);
607 } else {
608 wSetFocusTo(scr, tmp);
613 /* We need to always arrange icons when changing workspace, even if
614 * no autoarrange icons, because else the icons in different workspaces
615 * can be superposed.
616 * This can be avoided if appicons are also workspace specific.
618 if (!wPreferences.sticky_icons)
619 wArrangeIcons(scr, False);
621 if (scr->dock)
622 wAppIconPaint(scr->dock->icon_array[0]);
624 if (!wPreferences.flags.noclip && (scr->workspaces[workspace]->clip->auto_collapse ||
625 scr->workspaces[workspace]->clip->auto_raise_lower)) {
626 /* to handle enter notify. This will also */
627 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
628 XMapWindow(dpy, scr->clip_icon->icon->core->window);
630 else if (scr->clip_icon != NULL) {
631 wClipIconPaint(scr->clip_icon);
633 wScreenUpdateUsableArea(scr);
634 wNETWMUpdateDesktop(scr);
635 showWorkspaceName(scr, workspace);
637 WMPostNotificationName(WMNWorkspaceChanged, scr, (void *)(uintptr_t) workspace);
639 /* XSync(dpy, False); */
642 static void switchWSCommand(WMenu * menu, WMenuEntry * entry)
644 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
647 static void lastWSCommand(WMenu * menu, WMenuEntry * entry)
649 wWorkspaceChange(menu->frame->screen_ptr, menu->frame->screen_ptr->last_workspace);
652 static void deleteWSCommand(WMenu * menu, WMenuEntry * entry)
654 wWorkspaceDelete(menu->frame->screen_ptr, menu->frame->screen_ptr->workspace_count - 1);
657 static void newWSCommand(WMenu *menu, WMenuEntry *foo)
659 int ws;
661 ws = wWorkspaceNew(menu->frame->screen_ptr);
663 /* autochange workspace */
664 if (ws >= 0)
665 wWorkspaceChange(menu->frame->screen_ptr, ws);
668 void wWorkspaceRename(WScreen *scr, int workspace, const char *name)
670 char buf[MAX_WORKSPACENAME_WIDTH + 1];
671 char *tmp;
673 if (workspace >= scr->workspace_count)
674 return;
676 /* trim white spaces */
677 tmp = wtrimspace(name);
679 if (strlen(tmp) == 0) {
680 snprintf(buf, sizeof(buf), _("Workspace %i"), workspace + 1);
681 } else {
682 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
684 buf[MAX_WORKSPACENAME_WIDTH] = 0;
685 wfree(tmp);
687 /* update workspace */
688 wfree(scr->workspaces[workspace]->name);
689 scr->workspaces[workspace]->name = wstrdup(buf);
691 if (scr->clip_ws_menu) {
692 if (strcmp(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
693 wfree(scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text);
694 scr->clip_ws_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
695 wMenuRealize(scr->clip_ws_menu);
698 if (scr->workspace_menu) {
699 if (strcmp(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text, buf) != 0) {
700 wfree(scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text);
701 scr->workspace_menu->entries[workspace + MC_WORKSPACE1]->text = wstrdup(buf);
702 wMenuRealize(scr->workspace_menu);
706 if (scr->clip_icon)
707 wClipIconPaint(scr->clip_icon);
709 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) workspace);
712 /* callback for when menu entry is edited */
713 static void onMenuEntryEdited(WMenu * menu, WMenuEntry * entry)
715 char *tmp;
717 tmp = entry->text;
718 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
721 WMenu *wWorkspaceMenuMake(WScreen * scr, Bool titled)
723 WMenu *wsmenu;
724 WMenuEntry *entry;
726 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
727 if (!wsmenu) {
728 wwarning(_("could not create Workspace menu"));
729 return NULL;
732 /* callback to be called when an entry is edited */
733 wsmenu->on_edit = onMenuEntryEdited;
735 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
736 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
738 entry = wMenuAddCallback(wsmenu, _("Last Used"), lastWSCommand, NULL);
739 entry->rtext = GetShortcutKey(wKeyBindings[WKBD_LASTWORKSPACE]);
741 return wsmenu;
744 void wWorkspaceMenuUpdate(WScreen * scr, WMenu * menu)
746 int i;
747 long ws;
748 char title[MAX_WORKSPACENAME_WIDTH + 1];
749 WMenuEntry *entry;
750 int tmp;
752 if (!menu)
753 return;
755 if (menu->entry_no < scr->workspace_count + MC_WORKSPACE1) {
756 /* new workspace(s) added */
757 i = scr->workspace_count - (menu->entry_no - MC_WORKSPACE1);
758 ws = menu->entry_no - MC_WORKSPACE1;
759 while (i > 0) {
760 wstrlcpy(title, scr->workspaces[ws]->name, MAX_WORKSPACENAME_WIDTH);
762 entry = wMenuAddCallback(menu, title, switchWSCommand, (void *)ws);
763 entry->flags.indicator = 1;
764 entry->flags.editable = 1;
766 i--;
767 ws++;
769 } else if (menu->entry_no > scr->workspace_count + MC_WORKSPACE1) {
770 /* removed workspace(s) */
771 for (i = menu->entry_no - 1; i >= scr->workspace_count + MC_WORKSPACE1; i--) {
772 wMenuRemoveItem(menu, i);
776 for (i = 0; i < scr->workspace_count; i++) {
777 /* workspace shortcut labels */
778 if (i / 10 == scr->current_workspace / 10)
779 menu->entries[i + MC_WORKSPACE1]->rtext = GetShortcutKey(wKeyBindings[WKBD_WORKSPACE1 + (i % 10)]);
780 else
781 menu->entries[i + MC_WORKSPACE1]->rtext = NULL;
783 menu->entries[i + MC_WORKSPACE1]->flags.indicator_on = 0;
785 menu->entries[scr->current_workspace + MC_WORKSPACE1]->flags.indicator_on = 1;
786 wMenuRealize(menu);
788 /* don't let user destroy current workspace */
789 if (scr->current_workspace == scr->workspace_count - 1) {
790 wMenuSetEnabled(menu, MC_DESTROY_LAST, False);
791 } else {
792 wMenuSetEnabled(menu, MC_DESTROY_LAST, True);
795 /* back to last workspace */
796 if (scr->workspace_count && scr->last_workspace != scr->current_workspace)
797 wMenuSetEnabled(menu, MC_LAST_USED, True);
798 else
799 wMenuSetEnabled(menu, MC_LAST_USED, False);
801 tmp = menu->frame->top_width + 5;
802 /* if menu got unreachable, bring it to a visible place */
803 if (menu->frame_x < tmp - (int)menu->frame->core->width)
804 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
806 wMenuPaint(menu);
809 void wWorkspaceSaveState(WScreen * scr, WMPropList * old_state)
811 WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
812 int i;
814 make_keys();
816 old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
817 parr = WMCreatePLArray(NULL);
818 for (i = 0; i < scr->workspace_count; i++) {
819 pstr = WMCreatePLString(scr->workspaces[i]->name);
820 wks_state = WMCreatePLDictionary(dName, pstr, NULL);
821 WMReleasePropList(pstr);
822 if (!wPreferences.flags.noclip) {
823 pstr = wClipSaveWorkspaceState(scr, i);
824 WMPutInPLDictionary(wks_state, dClip, pstr);
825 WMReleasePropList(pstr);
826 } else if (old_wks_state != NULL) {
827 if ((foo = WMGetFromPLArray(old_wks_state, i)) != NULL) {
828 if ((bar = WMGetFromPLDictionary(foo, dClip)) != NULL) {
829 WMPutInPLDictionary(wks_state, dClip, bar);
833 WMAddToPLArray(parr, wks_state);
834 WMReleasePropList(wks_state);
836 WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
837 WMReleasePropList(parr);
840 void wWorkspaceRestoreState(WScreen *scr)
842 WMPropList *parr, *pstr, *wks_state, *clip_state;
843 int i, j;
845 make_keys();
847 if (scr->session_state == NULL)
848 return;
850 parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
852 if (!parr)
853 return;
855 for (i = 0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
856 wks_state = WMGetFromPLArray(parr, i);
857 if (WMIsPLDictionary(wks_state))
858 pstr = WMGetFromPLDictionary(wks_state, dName);
859 else
860 pstr = wks_state;
862 if (i >= scr->workspace_count)
863 wWorkspaceNew(scr);
865 if (scr->workspace_menu) {
866 wfree(scr->workspace_menu->entries[i + MC_WORKSPACE1]->text);
867 scr->workspace_menu->entries[i + MC_WORKSPACE1]->text = wstrdup(WMGetFromPLString(pstr));
868 scr->workspace_menu->flags.realized = 0;
871 wfree(scr->workspaces[i]->name);
872 scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
873 if (!wPreferences.flags.noclip) {
874 int added_omnipresent_icons = 0;
876 clip_state = WMGetFromPLDictionary(wks_state, dClip);
877 if (scr->workspaces[i]->clip)
878 wDockDestroy(scr->workspaces[i]->clip);
880 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state, WM_CLIP);
881 if (i > 0)
882 wDockHideIcons(scr->workspaces[i]->clip);
884 /* We set the global icons here, because scr->workspaces[i]->clip
885 * was not valid in wDockRestoreState().
886 * There we only set icon->omnipresent to know which icons we
887 * need to set here.
889 for (j = 0; j < scr->workspaces[i]->clip->max_icons; j++) {
890 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
891 int k;
893 if (!aicon || !aicon->omnipresent)
894 continue;
895 aicon->omnipresent = 0;
896 if (wClipMakeIconOmnipresent(aicon, True) != WO_SUCCESS)
897 continue;
898 if (i == 0)
899 continue;
901 /* Move this appicon from workspace i to workspace 0 */
902 scr->workspaces[i]->clip->icon_array[j] = NULL;
903 scr->workspaces[i]->clip->icon_count--;
905 added_omnipresent_icons++;
906 /* If there are too many omnipresent appicons, we are in trouble */
907 assert(scr->workspaces[0]->clip->icon_count + added_omnipresent_icons
908 <= scr->workspaces[0]->clip->max_icons);
909 /* Find first free spot on workspace 0 */
910 for (k = 0; k < scr->workspaces[0]->clip->max_icons; k++)
911 if (scr->workspaces[0]->clip->icon_array[k] == NULL)
912 break;
913 scr->workspaces[0]->clip->icon_array[k] = aicon;
914 aicon->dock = scr->workspaces[0]->clip;
916 scr->workspaces[0]->clip->icon_count += added_omnipresent_icons;
919 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void *)(uintptr_t) i);
923 /* Returns the workspace number for a given workspace name */
924 int wGetWorkspaceNumber(WScreen *scr, const char *value)
926 int w, i;
928 if (sscanf(value, "%i", &w) != 1) {
929 w = -1;
930 for (i = 0; i < scr->workspace_count; i++) {
931 if (strcmp(scr->workspaces[i]->name, value) == 0) {
932 w = i;
933 break;
936 } else {
937 w--;
940 return w;