fixed many bugs, removed linked list
[wmaker-crm.git] / src / workspace.c
blob486aa41fdc51f22f0f89a8c34525490c3919021c
1 /* workspace.c- Workspace management
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 *
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
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
22 #include "wconfig.h"
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 #ifdef SHAPE
27 #include <X11/extensions/shape.h>
28 #endif
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <time.h>
37 #include "WindowMaker.h"
38 #include "wcore.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 #ifdef GNOME_STUFF
50 #include "gnome.h"
51 #endif
52 #ifdef KWM_HINTS
53 #include "kwm.h"
54 #endif
56 #include <proplist.h>
59 extern WPreferences wPreferences;
60 extern XContext wWinContext;
63 static proplist_t dWorkspaces=NULL;
64 static proplist_t dClip, dName;
67 static void
68 make_keys()
70 if (dWorkspaces!=NULL)
71 return;
73 dWorkspaces = PLMakeString("Workspaces");
74 dName = PLMakeString("Name");
75 dClip = PLMakeString("Clip");
79 void
80 wWorkspaceMake(WScreen *scr, int count)
82 while (count>0) {
83 wWorkspaceNew(scr);
84 count--;
89 int
90 wWorkspaceNew(WScreen *scr)
92 WWorkspace *wspace, **list;
93 int i;
95 if (scr->workspace_count < MAX_WORKSPACES) {
96 scr->workspace_count++;
98 wspace = wmalloc(sizeof(WWorkspace));
99 wspace->name = NULL;
101 #ifdef KWM_HINTS
102 if (scr->flags.kwm_syncing_count) {
103 wspace->name = wKWMGetWorkspaceName(scr, scr->workspace_count-1);
105 #endif
106 if (!wspace->name) {
107 wspace->name = wmalloc(strlen(_("Workspace %i"))+8);
108 sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
112 if (!wPreferences.flags.noclip) {
113 wspace->clip = wDockCreate(scr, WM_CLIP);
114 } else
115 wspace->clip = NULL;
117 list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
119 for (i=0; i<scr->workspace_count-1; i++) {
120 list[i] = scr->workspaces[i];
122 list[i] = wspace;
123 if (scr->workspaces)
124 free(scr->workspaces);
125 scr->workspaces = list;
127 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
128 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
129 #ifdef GNOME_STUFF
130 wGNOMEUpdateWorkspaceHints(scr);
131 #endif
132 #ifdef KWM_HINTS
133 if (!scr->flags.kwm_syncing_count) {
134 wKWMUpdateWorkspaceCountHint(scr);
135 wKWMUpdateWorkspaceNameHint(scr, scr->workspace_count-1);
137 #ifdef not_used
138 wKWMSetUsableAreaHint(scr, scr->workspace_count-1);
139 #endif
140 #endif
141 XFlush(dpy);
143 return scr->workspace_count-1;
145 return -1;
150 Bool
151 wWorkspaceDelete(WScreen *scr, int workspace)
153 WWindow *tmp;
154 WWorkspace **list;
155 int i, j;
158 if (workspace<=0)
159 return False;
161 /* verify if workspace is in use by some window */
162 tmp = scr->focused_window;
163 while (tmp) {
164 if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace==workspace)
165 return False;
166 tmp = tmp->prev;
169 if (!wPreferences.flags.noclip) {
170 wDockDestroy(scr->workspaces[workspace]->clip);
171 scr->workspaces[workspace]->clip = NULL;
174 list = wmalloc(sizeof(WWorkspace*)*(scr->workspace_count-1));
175 j = 0;
176 for (i=0; i<scr->workspace_count; i++) {
177 if (i!=workspace)
178 list[j++] = scr->workspaces[i];
179 else {
180 if (scr->workspaces[i]->name)
181 free(scr->workspaces[i]->name);
182 free(scr->workspaces[i]);
185 free(scr->workspaces);
186 scr->workspaces = list;
188 scr->workspace_count--;
191 /* update menu */
192 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
193 /* clip workspace menu */
194 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
196 /* update also window menu */
197 if (scr->workspace_submenu) {
198 WMenu *menu = scr->workspace_submenu;
200 i = menu->entry_no;
201 while (i>scr->workspace_count)
202 wMenuRemoveItem(menu, --i);
203 wMenuRealize(menu);
205 /* and clip menu */
206 if (scr->clip_submenu) {
207 WMenu *menu = scr->clip_submenu;
209 i = menu->entry_no;
210 while (i>scr->workspace_count)
211 wMenuRemoveItem(menu, --i);
212 wMenuRealize(menu);
215 #ifdef GNOME_STUFF
216 wGNOMEUpdateWorkspaceHints(scr);
217 #endif
218 #ifdef KWM_HINTS
219 wKWMUpdateWorkspaceCountHint(scr);
220 #endif
222 if (scr->current_workspace >= scr->workspace_count)
223 wWorkspaceChange(scr, scr->workspace_count-1);
225 return True;
229 typedef struct WorkspaceNameData {
230 int count;
231 RImage *back;
232 RImage *text;
233 time_t timeout;
234 } WorkspaceNameData;
238 static void
239 hideWorkpaceName(void *data)
241 WScreen *scr = (WScreen*)data;
243 if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
244 || time(NULL) > scr->workspace_name_data->timeout) {
245 XUnmapWindow(dpy, scr->workspace_name);
247 if (scr->workspace_name_data) {
248 RDestroyImage(scr->workspace_name_data->back);
249 RDestroyImage(scr->workspace_name_data->text);
250 free(scr->workspace_name_data);
252 scr->workspace_name_data = NULL;
254 scr->workspace_name_timer = NULL;
255 } else {
256 RImage *img = RCloneImage(scr->workspace_name_data->back);
257 Pixmap pix;
259 scr->workspace_name_timer =
260 WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkpaceName,
261 scr);
263 RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
264 scr->workspace_name_data->count*255/10);
266 RConvertImage(scr->rcontext, img, &pix);
268 RDestroyImage(img);
270 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
271 XClearWindow(dpy, scr->workspace_name);
272 XFreePixmap(dpy, pix);
273 XFlush(dpy);
275 scr->workspace_name_data->count--;
281 static void
282 showWorkspaceName(WScreen *scr, int workspace)
284 WorkspaceNameData *data;
285 RXImage *ximg;
286 Pixmap text, mask;
287 int w, h;
288 int px, py;
289 char *name = scr->workspaces[workspace]->name;
290 int len = strlen(name);
291 int x, y;
293 if (wPreferences.workspace_name_display_position == WD_NONE
294 || scr->workspace_count < 2)
295 return;
297 if (scr->workspace_name_timer) {
298 WMDeleteTimerHandler(scr->workspace_name_timer);
299 XUnmapWindow(dpy, scr->workspace_name);
300 XFlush(dpy);
302 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY,
303 hideWorkpaceName, scr);
305 if (scr->workspace_name_data) {
306 RDestroyImage(scr->workspace_name_data->back);
307 RDestroyImage(scr->workspace_name_data->text);
308 free(scr->workspace_name_data);
311 data = wmalloc(sizeof(WorkspaceNameData));
313 w = WMWidthOfString(scr->workspace_name_font, name, len);
314 h = WMFontHeight(scr->workspace_name_font);
316 switch (wPreferences.workspace_name_display_position) {
317 case WD_TOP:
318 px = (scr->scr_width - (w+4))/2;
319 py = 0;
320 break;
321 case WD_BOTTOM:
322 px = (scr->scr_width - (w+4))/2;
323 py = scr->scr_height - (h+4);
324 break;
325 case WD_TOPLEFT:
326 px = 0;
327 py = 0;
328 break;
329 case WD_TOPRIGHT:
330 px = scr->scr_width - (w+4);
331 py = 0;
332 break;
333 case WD_BOTTOMLEFT:
334 px = 0;
335 py = scr->scr_height - (h+4);
336 break;
337 case WD_BOTTOMRIGHT:
338 px = scr->scr_width - (w+4);
339 py = scr->scr_height - (h+4);
340 break;
341 case WD_CENTER:
342 default:
343 px = (scr->scr_width - (w+4))/2;
344 py = (scr->scr_height - (h+4))/2;
345 break;
347 XResizeWindow(dpy, scr->workspace_name, w+4, h+4);
348 XMoveWindow(dpy, scr->workspace_name, px, py);
350 text = XCreatePixmap(dpy, scr->w_win, w+4, h+4, scr->w_depth);
351 mask = XCreatePixmap(dpy, scr->w_win, w+4, h+4, 1);
353 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
354 XFillRectangle(dpy, text, scr->draw_gc, 0, 0, w+4, h+4);
356 XSetForeground(dpy, scr->mono_gc, 0);
357 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4);
359 XSetForeground(dpy, scr->mono_gc, 1);
360 for (x = 0; x <= 4; x++) {
361 for (y = 0; y <= 4; y++) {
362 WMDrawString(scr->wmscreen, mask, scr->mono_gc,
363 scr->workspace_name_font, x, y, name, len);
367 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
368 WMDrawString(scr->wmscreen, text, scr->draw_gc, scr->workspace_name_font,
369 2, 2, scr->workspaces[workspace]->name,
370 strlen(scr->workspaces[workspace]->name));
371 #ifdef SHAPE
372 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask,
373 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,
391 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 +
411 (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY*data->count)/1000;
413 scr->workspace_name_data = data;
416 return;
418 erro:
419 if (scr->workspace_name_timer)
420 WMDeleteTimerHandler(scr->workspace_name_timer);
422 if (data->text)
423 RDestroyImage(data->text);
424 if (data->back)
425 RDestroyImage(data->back);
426 free(data);
428 scr->workspace_name_data = NULL;
430 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
431 10*WORKSPACE_NAME_FADE_DELAY,
432 hideWorkpaceName, scr);
436 void
437 wWorkspaceChange(WScreen *scr, int workspace)
439 if (scr->flags.startup || scr->flags.startup2) {
440 return;
443 if (workspace != scr->current_workspace) {
444 wWorkspaceForceChange(scr, workspace);
445 } /*else {
446 showWorkspaceName(scr, workspace);
451 void
452 wWorkspaceRelativeChange(WScreen *scr, int amount)
454 int w;
456 w = scr->current_workspace + amount;
458 if (amount < 0) {
460 if (w >= 0)
461 wWorkspaceChange(scr, w);
462 else if (wPreferences.ws_cycle)
463 wWorkspaceChange(scr, scr->workspace_count + w);
465 } else if (amount > 0) {
467 if (w < scr->workspace_count)
468 wWorkspaceChange(scr, w);
469 else if (wPreferences.ws_advance)
470 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES-1));
471 else if (wPreferences.ws_cycle)
472 wWorkspaceChange(scr, w % scr->workspace_count);
478 void
479 wWorkspaceForceChange(WScreen *scr, int workspace)
481 WWindow *tmp, *foc=NULL, *foc2=NULL;
483 if (workspace >= MAX_WORKSPACES || workspace < 0)
484 return;
486 SendHelperMessage(scr, 'C', workspace+1, NULL);
488 if (workspace > scr->workspace_count-1) {
489 wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
492 wClipUpdateForWorkspaceChange(scr, workspace);
494 scr->current_workspace = workspace;
496 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
498 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
500 if ((tmp = scr->focused_window)!= NULL) {
501 if ((IS_OMNIPRESENT(tmp) && !WFLAGP(tmp, skip_window_list))
502 || tmp->flags.changing_workspace) {
503 foc = tmp;
506 /* foc2 = tmp; will fix annoyance with gnome panel
507 * but will create annoyance for every other application
510 while (tmp) {
511 if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
512 /* unmap windows not on this workspace */
513 if ((tmp->flags.mapped||tmp->flags.shaded)
514 && !IS_OMNIPRESENT(tmp)
515 && !tmp->flags.changing_workspace) {
517 wWindowUnmap(tmp);
519 /* also unmap miniwindows not on this workspace */
520 if (tmp->flags.miniaturized && !IS_OMNIPRESENT(tmp)
521 && tmp->icon) {
522 if (!wPreferences.sticky_icons) {
523 XUnmapWindow(dpy, tmp->icon->core->window);
524 tmp->icon->mapped = 0;
526 #if 0
527 else {
528 tmp->icon->mapped = 1;
529 /* Why is this here? -Alfredo */
530 XMapWindow(dpy, tmp->icon->core->window);
532 #endif
534 /* update current workspace of omnipresent windows */
535 if (IS_OMNIPRESENT(tmp)) {
536 WApplication *wapp = wApplicationOf(tmp->main_window);
538 tmp->frame->workspace = workspace;
540 if (wapp) {
541 wapp->last_workspace = workspace;
543 if (!foc2)
544 foc2 = tmp;
546 } else {
547 /* change selected windows' workspace */
548 if (tmp->flags.selected) {
549 wWindowChangeWorkspace(tmp, workspace);
550 if (!tmp->flags.miniaturized && !foc) {
551 foc = tmp;
553 } else {
554 if (!tmp->flags.hidden) {
555 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
556 /* remap windows that are on this workspace */
557 wWindowMap(tmp);
558 if (!foc && !WFLAGP(tmp, skip_window_list))
559 foc = tmp;
561 /* Also map miniwindow if not omnipresent */
562 if (!wPreferences.sticky_icons &&
563 tmp->flags.miniaturized &&
564 !IS_OMNIPRESENT(tmp) && tmp->icon) {
565 tmp->icon->mapped = 1;
566 XMapWindow(dpy, tmp->icon->core->window);
571 tmp = tmp->prev;
574 if (!foc)
575 foc = foc2;
577 if (scr->focused_window->flags.mapped && !foc) {
578 foc = scr->focused_window;
580 if (wPreferences.focus_mode == WKF_CLICK) {
581 wSetFocusTo(scr, foc);
582 } else {
583 unsigned int mask;
584 int foo;
585 Window bar, win;
586 WWindow *tmp;
588 tmp = NULL;
589 if (XQueryPointer(dpy, scr->root_win, &bar, &win,
590 &foo, &foo, &foo, &foo, &mask)) {
591 tmp = wWindowFor(win);
593 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
594 wSetFocusTo(scr, foc);
595 } else {
596 wSetFocusTo(scr, tmp);
601 /* We need to always arrange icons when changing workspace, even if
602 * no autoarrange icons, because else the icons in different workspaces
603 * can be superposed.
604 * This can be avoided if appicons are also workspace specific.
606 if (!wPreferences.sticky_icons)
607 wArrangeIcons(scr, False);
609 if (scr->dock)
610 wAppIconPaint(scr->dock->icon_array[0]);
612 if (scr->clip_icon) {
613 if (scr->workspaces[workspace]->clip->auto_collapse ||
614 scr->workspaces[workspace]->clip->auto_raise_lower) {
615 /* to handle enter notify. This will also */
616 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
617 XMapWindow(dpy, scr->clip_icon->icon->core->window);
618 } else {
619 wClipIconPaint(scr->clip_icon);
623 showWorkspaceName(scr, workspace);
625 #ifdef GNOME_STUFF
626 wGNOMEUpdateCurrentWorkspaceHint(scr);
627 #endif
628 #ifdef KWM_HINTS
629 wKWMUpdateCurrentWorkspaceHint(scr);
630 #endif
631 /* XSync(dpy, False); */
635 static void
636 switchWSCommand(WMenu *menu, WMenuEntry *entry)
638 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
643 static void
644 deleteWSCommand(WMenu *menu, WMenuEntry *entry)
646 wWorkspaceDelete(menu->frame->screen_ptr,
647 menu->frame->screen_ptr->workspace_count-1);
652 static void
653 newWSCommand(WMenu *menu, WMenuEntry *foo)
655 int ws;
657 ws = wWorkspaceNew(menu->frame->screen_ptr);
658 /* autochange workspace*/
659 if (ws>=0)
660 wWorkspaceChange(menu->frame->screen_ptr, ws);
664 if (ws<9) {
665 int kcode;
666 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
667 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
668 entry->rtext =
669 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
675 static char*
676 cropline(char *line)
678 char *start, *end;
680 if (strlen(line)==0)
681 return line;
683 start = line;
684 end = &(line[strlen(line)])-1;
685 while (isspace(*line) && *line!=0) line++;
686 while (isspace(*end) && end!=line) {
687 *end=0;
688 end--;
690 return line;
694 void
695 wWorkspaceRename(WScreen *scr, int workspace, char *name)
697 char buf[MAX_WORKSPACENAME_WIDTH+1];
698 char *tmp;
700 if (workspace >= scr->workspace_count)
701 return;
703 /* trim white spaces */
704 tmp = cropline(name);
706 if (strlen(tmp)==0) {
707 sprintf(buf, _("Workspace %i"), workspace+1);
708 } else {
709 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
711 buf[MAX_WORKSPACENAME_WIDTH] = 0;
713 /* update workspace */
714 free(scr->workspaces[workspace]->name);
715 scr->workspaces[workspace]->name = wstrdup(buf);
717 if (scr->clip_ws_menu) {
718 if (strcmp(scr->clip_ws_menu->entries[workspace+2]->text, buf)!=0) {
719 free(scr->clip_ws_menu->entries[workspace+2]->text);
720 scr->clip_ws_menu->entries[workspace+2]->text = wstrdup(buf);
721 wMenuRealize(scr->clip_ws_menu);
724 if (scr->workspace_menu) {
725 if (strcmp(scr->workspace_menu->entries[workspace+2]->text, buf)!=0) {
726 free(scr->workspace_menu->entries[workspace+2]->text);
727 scr->workspace_menu->entries[workspace+2]->text = wstrdup(buf);
728 wMenuRealize(scr->workspace_menu);
732 UpdateSwitchMenuWorkspace(scr, workspace);
734 if (scr->clip_icon)
735 wClipIconPaint(scr->clip_icon);
737 #ifdef GNOME_STUFF
738 wGNOMEUpdateWorkspaceNamesHint(scr);
739 #endif
740 #ifdef KWM_HINTS
741 wKWMUpdateWorkspaceNameHint(scr, workspace);
742 #endif
748 /* callback for when menu entry is edited */
749 static void
750 onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
752 char *tmp;
754 tmp = entry->text;
755 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
759 WMenu*
760 wWorkspaceMenuMake(WScreen *scr, Bool titled)
762 WMenu *wsmenu;
764 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
765 if (!wsmenu) {
766 wwarning(_("could not create Workspace menu"));
767 return NULL;
770 /* callback to be called when an entry is edited */
771 wsmenu->on_edit = onMenuEntryEdited;
773 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
774 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
776 return wsmenu;
781 void
782 wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
784 int i;
785 long ws;
786 char title[MAX_WORKSPACENAME_WIDTH+1];
787 WMenuEntry *entry;
788 int tmp;
790 if (!menu)
791 return;
793 if (menu->entry_no < scr->workspace_count+2) {
794 /* new workspace(s) added */
795 i = scr->workspace_count-(menu->entry_no-2);
796 ws = menu->entry_no - 2;
797 while (i>0) {
798 strcpy(title, scr->workspaces[ws]->name);
800 entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
801 entry->flags.indicator = 1;
802 entry->flags.editable = 1;
804 i--;
805 ws++;
807 } else if (menu->entry_no > scr->workspace_count+2) {
808 /* removed workspace(s) */
809 for (i = menu->entry_no-1; i >= scr->workspace_count+2; i--) {
810 wMenuRemoveItem(menu, i);
813 wMenuRealize(menu);
815 for (i=0; i<scr->workspace_count; i++) {
816 menu->entries[i+2]->flags.indicator_on = 0;
818 menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;
820 /* don't let user destroy current workspace */
821 if (scr->current_workspace == scr->workspace_count-1) {
822 wMenuSetEnabled(menu, 1, False);
823 } else {
824 wMenuSetEnabled(menu, 1, True);
827 tmp = menu->frame->top_width + 5;
828 /* if menu got unreachable, bring it to a visible place */
829 if (menu->frame_x < tmp - (int)menu->frame->core->width)
830 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
832 wMenuPaint(menu);
836 void
837 wWorkspaceSaveState(WScreen *scr, proplist_t old_state)
839 proplist_t parr, pstr;
840 proplist_t wks_state, old_wks_state;
841 proplist_t foo, bar;
842 int i;
844 make_keys();
846 old_wks_state = PLGetDictionaryEntry(old_state, dWorkspaces);
847 parr = PLMakeArrayFromElements(NULL);
848 for (i=0; i < scr->workspace_count; i++) {
849 pstr = PLMakeString(scr->workspaces[i]->name);
850 wks_state = PLMakeDictionaryFromEntries(dName, pstr, NULL);
851 PLRelease(pstr);
852 if (!wPreferences.flags.noclip) {
853 pstr = wClipSaveWorkspaceState(scr, i);
854 PLInsertDictionaryEntry(wks_state, dClip, pstr);
855 PLRelease(pstr);
856 } else if (old_wks_state!=NULL) {
857 if ((foo = PLGetArrayElement(old_wks_state, i))!=NULL) {
858 if ((bar = PLGetDictionaryEntry(foo, dClip))!=NULL) {
859 PLInsertDictionaryEntry(wks_state, dClip, bar);
863 PLAppendArrayElement(parr, wks_state);
864 PLRelease(wks_state);
866 PLInsertDictionaryEntry(scr->session_state, dWorkspaces, parr);
867 PLRelease(parr);
871 void
872 wWorkspaceRestoreState(WScreen *scr)
874 proplist_t parr, pstr, wks_state;
875 proplist_t clip_state;
876 int i, j, wscount;
878 make_keys();
880 parr = PLGetDictionaryEntry(scr->session_state, dWorkspaces);
882 if (!parr)
883 return;
885 wscount = scr->workspace_count;
886 for (i=0; i < WMIN(PLGetNumberOfElements(parr), MAX_WORKSPACES); i++) {
887 wks_state = PLGetArrayElement(parr, i);
888 if (PLIsDictionary(wks_state))
889 pstr = PLGetDictionaryEntry(wks_state, dName);
890 else
891 pstr = wks_state;
892 if (i >= scr->workspace_count)
893 wWorkspaceNew(scr);
894 if (scr->workspace_menu) {
895 free(scr->workspace_menu->entries[i+2]->text);
896 scr->workspace_menu->entries[i+2]->text = wstrdup(PLGetString(pstr));
897 scr->workspace_menu->flags.realized = 0;
899 free(scr->workspaces[i]->name);
900 scr->workspaces[i]->name = wstrdup(PLGetString(pstr));
901 if (!wPreferences.flags.noclip) {
902 clip_state = PLGetDictionaryEntry(wks_state, dClip);
903 if (scr->workspaces[i]->clip)
904 wDockDestroy(scr->workspaces[i]->clip);
905 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state,
906 WM_CLIP);
907 if (i>0)
908 wDockHideIcons(scr->workspaces[i]->clip);
910 /* We set the global icons here, because scr->workspaces[i]->clip
911 * was not valid in wDockRestoreState().
912 * There we only set icon->omnipresent to know which icons we
913 * need to set here.
915 for (j=0; j<scr->workspaces[i]->clip->max_icons; j++) {
916 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
918 if (aicon && aicon->omnipresent) {
919 aicon->omnipresent = 0;
920 wClipMakeIconOmnipresent(aicon, True);
921 XMapWindow(dpy, aicon->icon->core->window);
925 #ifdef KWM_HINTS
926 wKWMUpdateWorkspaceNameHint(scr, i);
927 #endif
929 #ifdef GNOME_STUFF
930 wGNOMEUpdateWorkspaceNamesHint(scr);
931 #endif