- Finished moving to the new proplist handling code in WINGs.
[wmaker-crm.git] / src / workspace.c
blob26cc2a421e5c69ad38dbcbc5956919bf5d56698e
1 /* workspace.c- Workspace management
3 * Window Maker window manager
5 * Copyright (c) 1997, 1998 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
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 KWM_HINTS
50 #include "kwm.h"
51 #endif
53 #include <WINGs/WUtil.h>
56 extern WPreferences wPreferences;
57 extern XContext wWinContext;
60 static WMPropList *dWorkspaces=NULL;
61 static WMPropList *dClip, *dName;
63 #ifdef VIRTUAL_DESKTOP
64 static BOOL initVDesk = False;
65 #endif
67 static void
68 make_keys()
70 if (dWorkspaces!=NULL)
71 return;
73 dWorkspaces = WMCreatePLString("Workspaces");
74 dName = WMCreatePLString("Name");
75 dClip = WMCreatePLString("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 wfree(scr->workspaces);
125 scr->workspaces = list;
127 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
128 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
129 #ifdef VIRTUAL_DESKTOP
130 wspace->view_x = wspace->view_y = 0;
131 wspace->height = scr->scr_height;
132 wspace->width = scr->scr_width;
133 #endif
134 WMPostNotificationName(WMNWorkspaceCreated, scr,
135 (void*)(scr->workspace_count-1));
136 XFlush(dpy);
138 return scr->workspace_count-1;
140 return -1;
145 Bool
146 wWorkspaceDelete(WScreen *scr, int workspace)
148 WWindow *tmp;
149 WWorkspace **list;
150 int i, j;
153 if (workspace<=0)
154 return False;
156 /* verify if workspace is in use by some window */
157 tmp = scr->focused_window;
158 while (tmp) {
159 if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace==workspace)
160 return False;
161 tmp = tmp->prev;
164 if (!wPreferences.flags.noclip) {
165 wDockDestroy(scr->workspaces[workspace]->clip);
166 scr->workspaces[workspace]->clip = NULL;
169 list = wmalloc(sizeof(WWorkspace*)*(scr->workspace_count-1));
170 j = 0;
171 for (i=0; i<scr->workspace_count; i++) {
172 if (i!=workspace)
173 list[j++] = scr->workspaces[i];
174 else {
175 if (scr->workspaces[i]->name)
176 wfree(scr->workspaces[i]->name);
177 wfree(scr->workspaces[i]);
180 wfree(scr->workspaces);
181 scr->workspaces = list;
183 scr->workspace_count--;
186 /* update menu */
187 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
188 /* clip workspace menu */
189 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
191 /* update also window menu */
192 if (scr->workspace_submenu) {
193 WMenu *menu = scr->workspace_submenu;
195 i = menu->entry_no;
196 while (i>scr->workspace_count)
197 wMenuRemoveItem(menu, --i);
198 wMenuRealize(menu);
200 /* and clip menu */
201 if (scr->clip_submenu) {
202 WMenu *menu = scr->clip_submenu;
204 i = menu->entry_no;
205 while (i>scr->workspace_count)
206 wMenuRemoveItem(menu, --i);
207 wMenuRealize(menu);
210 WMPostNotificationName(WMNWorkspaceDestroyed, scr,
211 (void*)(scr->workspace_count-1));
213 if (scr->current_workspace >= scr->workspace_count)
214 wWorkspaceChange(scr, scr->workspace_count-1);
216 return True;
220 typedef struct WorkspaceNameData {
221 int count;
222 RImage *back;
223 RImage *text;
224 time_t timeout;
225 } WorkspaceNameData;
229 static void
230 hideWorkpaceName(void *data)
232 WScreen *scr = (WScreen*)data;
234 if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
235 || time(NULL) > scr->workspace_name_data->timeout) {
236 XUnmapWindow(dpy, scr->workspace_name);
238 if (scr->workspace_name_data) {
239 RReleaseImage(scr->workspace_name_data->back);
240 RReleaseImage(scr->workspace_name_data->text);
241 wfree(scr->workspace_name_data);
243 scr->workspace_name_data = NULL;
245 scr->workspace_name_timer = NULL;
246 } else {
247 RImage *img = RCloneImage(scr->workspace_name_data->back);
248 Pixmap pix;
250 scr->workspace_name_timer =
251 WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkpaceName,
252 scr);
254 RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
255 scr->workspace_name_data->count*255/10);
257 RConvertImage(scr->rcontext, img, &pix);
259 RReleaseImage(img);
261 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
262 XClearWindow(dpy, scr->workspace_name);
263 XFreePixmap(dpy, pix);
264 XFlush(dpy);
266 scr->workspace_name_data->count--;
272 static void
273 showWorkspaceName(WScreen *scr, int workspace)
275 WorkspaceNameData *data;
276 RXImage *ximg;
277 Pixmap text, mask;
278 int w, h;
279 int px, py;
280 char *name = scr->workspaces[workspace]->name;
281 int len = strlen(name);
282 int x, y;
284 if (wPreferences.workspace_name_display_position == WD_NONE
285 || scr->workspace_count < 2)
286 return;
288 if (scr->workspace_name_timer) {
289 WMDeleteTimerHandler(scr->workspace_name_timer);
290 XUnmapWindow(dpy, scr->workspace_name);
291 XFlush(dpy);
293 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY,
294 hideWorkpaceName, scr);
296 if (scr->workspace_name_data) {
297 RReleaseImage(scr->workspace_name_data->back);
298 RReleaseImage(scr->workspace_name_data->text);
299 wfree(scr->workspace_name_data);
302 data = wmalloc(sizeof(WorkspaceNameData));
304 w = WMWidthOfString(scr->workspace_name_font, name, len);
305 h = WMFontHeight(scr->workspace_name_font);
307 switch (wPreferences.workspace_name_display_position) {
308 case WD_TOP:
309 px = (scr->scr_width - (w+4))/2;
310 py = 0;
311 break;
312 case WD_BOTTOM:
313 px = (scr->scr_width - (w+4))/2;
314 py = scr->scr_height - (h+4);
315 break;
316 case WD_TOPLEFT:
317 px = 0;
318 py = 0;
319 break;
320 case WD_TOPRIGHT:
321 px = scr->scr_width - (w+4);
322 py = 0;
323 break;
324 case WD_BOTTOMLEFT:
325 px = 0;
326 py = scr->scr_height - (h+4);
327 break;
328 case WD_BOTTOMRIGHT:
329 px = scr->scr_width - (w+4);
330 py = scr->scr_height - (h+4);
331 break;
332 case WD_CENTER:
333 default:
334 px = (scr->scr_width - (w+4))/2;
335 py = (scr->scr_height - (h+4))/2;
336 break;
338 XResizeWindow(dpy, scr->workspace_name, w+4, h+4);
339 XMoveWindow(dpy, scr->workspace_name, px, py);
341 text = XCreatePixmap(dpy, scr->w_win, w+4, h+4, scr->w_depth);
342 mask = XCreatePixmap(dpy, scr->w_win, w+4, h+4, 1);
344 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
345 XFillRectangle(dpy, text, scr->draw_gc, 0, 0, w+4, h+4);
347 XSetForeground(dpy, scr->mono_gc, 0);
348 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4);
350 XSetForeground(dpy, scr->mono_gc, 1);
351 for (x = 0; x <= 4; x++) {
352 for (y = 0; y <= 4; y++) {
353 WMDrawString(scr->wmscreen, mask, scr->mono_gc,
354 scr->workspace_name_font, x, y, name, len);
358 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
359 WMDrawString(scr->wmscreen, text, scr->draw_gc, scr->workspace_name_font,
360 2, 2, scr->workspaces[workspace]->name,
361 strlen(scr->workspaces[workspace]->name));
362 #ifdef SHAPE
363 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask,
364 ShapeSet);
365 #endif
366 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
367 XClearWindow(dpy, scr->workspace_name);
369 data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
371 XFreePixmap(dpy, text);
372 XFreePixmap(dpy, mask);
374 if (!data->text) {
375 XMapRaised(dpy, scr->workspace_name);
376 XFlush(dpy);
378 goto erro;
381 ximg = RGetXImage(scr->rcontext, scr->root_win, px, py,
382 data->text->width, data->text->height);
384 if (!ximg || !ximg->image) {
385 goto erro;
388 XMapRaised(dpy, scr->workspace_name);
389 XFlush(dpy);
391 data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
392 RDestroyXImage(scr->rcontext, ximg);
394 if (!data->back) {
395 goto erro;
398 data->count = 10;
400 /* set a timeout for the effect */
401 data->timeout = time(NULL) + 2 +
402 (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY*data->count)/1000;
404 scr->workspace_name_data = data;
407 return;
409 erro:
410 if (scr->workspace_name_timer)
411 WMDeleteTimerHandler(scr->workspace_name_timer);
413 if (data->text)
414 RReleaseImage(data->text);
415 if (data->back)
416 RReleaseImage(data->back);
417 wfree(data);
419 scr->workspace_name_data = NULL;
421 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
422 10*WORKSPACE_NAME_FADE_DELAY,
423 hideWorkpaceName, scr);
427 void
428 wWorkspaceChange(WScreen *scr, int workspace)
430 if (scr->flags.startup || scr->flags.startup2) {
431 return;
434 if (workspace != scr->current_workspace) {
435 wWorkspaceForceChange(scr, workspace);
436 } /*else {
437 showWorkspaceName(scr, workspace);
442 void
443 wWorkspaceRelativeChange(WScreen *scr, int amount)
445 int w;
447 w = scr->current_workspace + amount;
449 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) {
458 if (w < scr->workspace_count)
459 wWorkspaceChange(scr, w);
460 else if (wPreferences.ws_advance)
461 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES-1));
462 else if (wPreferences.ws_cycle)
463 wWorkspaceChange(scr, w % scr->workspace_count);
469 void
470 wWorkspaceForceChange(WScreen *scr, int workspace)
472 WWindow *tmp, *foc=NULL, *foc2=NULL;
474 if (workspace >= MAX_WORKSPACES || workspace < 0)
475 return;
477 SendHelperMessage(scr, 'C', workspace+1, NULL);
479 if (workspace > scr->workspace_count-1) {
480 wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
483 wClipUpdateForWorkspaceChange(scr, workspace);
485 scr->current_workspace = workspace;
487 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
489 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
491 if ((tmp = scr->focused_window)!= NULL) {
492 if ((IS_OMNIPRESENT(tmp) && !WFLAGP(tmp, skip_window_list))
493 || tmp->flags.changing_workspace) {
494 foc = tmp;
497 /* foc2 = tmp; will fix annoyance with gnome panel
498 * but will create annoyance for every other application
501 while (tmp) {
502 if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
503 /* unmap windows not on this workspace */
504 if ((tmp->flags.mapped||tmp->flags.shaded)
505 && !IS_OMNIPRESENT(tmp)
506 && !tmp->flags.changing_workspace) {
508 wWindowUnmap(tmp);
510 /* also unmap miniwindows not on this workspace */
511 if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
512 tmp->icon && !IS_OMNIPRESENT(tmp)) {
514 XUnmapWindow(dpy, tmp->icon->core->window);
515 tmp->icon->mapped = 0;
517 /* update current workspace of omnipresent windows */
518 if (IS_OMNIPRESENT(tmp)) {
519 WApplication *wapp = wApplicationOf(tmp->main_window);
521 tmp->frame->workspace = workspace;
523 if (wapp) {
524 wapp->last_workspace = workspace;
526 if (!foc2)
527 foc2 = tmp;
529 } else {
530 /* change selected windows' workspace */
531 if (tmp->flags.selected) {
532 wWindowChangeWorkspace(tmp, workspace);
533 if (!tmp->flags.miniaturized && !foc) {
534 foc = tmp;
536 } else {
537 if (!tmp->flags.hidden) {
538 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
539 /* remap windows that are on this workspace */
540 wWindowMap(tmp);
541 if (!foc && !WFLAGP(tmp, skip_window_list))
542 foc = tmp;
544 /* Also map miniwindow if not omnipresent */
545 if (!wPreferences.sticky_icons &&
546 tmp->flags.miniaturized &&
547 !IS_OMNIPRESENT(tmp) && tmp->icon) {
548 tmp->icon->mapped = 1;
549 XMapWindow(dpy, tmp->icon->core->window);
554 tmp = tmp->prev;
557 if (!foc)
558 foc = foc2;
560 if (scr->focused_window->flags.mapped && !foc) {
561 foc = scr->focused_window;
563 if (wPreferences.focus_mode == WKF_CLICK) {
564 wSetFocusTo(scr, foc);
565 } else {
566 unsigned int mask;
567 int foo;
568 Window bar, win;
569 WWindow *tmp;
571 tmp = NULL;
572 if (XQueryPointer(dpy, scr->root_win, &bar, &win,
573 &foo, &foo, &foo, &foo, &mask)) {
574 tmp = wWindowFor(win);
576 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
577 wSetFocusTo(scr, foc);
578 } else {
579 wSetFocusTo(scr, tmp);
584 /* We need to always arrange icons when changing workspace, even if
585 * no autoarrange icons, because else the icons in different workspaces
586 * can be superposed.
587 * This can be avoided if appicons are also workspace specific.
589 if (!wPreferences.sticky_icons)
590 wArrangeIcons(scr, False);
592 if (scr->dock)
593 wAppIconPaint(scr->dock->icon_array[0]);
595 if (scr->clip_icon) {
596 if (scr->workspaces[workspace]->clip->auto_collapse ||
597 scr->workspaces[workspace]->clip->auto_raise_lower) {
598 /* to handle enter notify. This will also */
599 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
600 XMapWindow(dpy, scr->clip_icon->icon->core->window);
601 } else {
602 wClipIconPaint(scr->clip_icon);
606 showWorkspaceName(scr, workspace);
608 WMPostNotificationName(WMNWorkspaceChanged, scr, (void*)workspace);
610 /* XSync(dpy, False); */
613 #ifdef VIRTUAL_DESKTOP
614 /* TODO:
615 * 1) Allow border around each window so the scrolling
616 * won't just stop at the border.
617 * 2) Make pager.
620 void wWorkspaceManageEdge(WScreen *scr)
622 int w;
623 int vmask;
624 XSetWindowAttributes attribs;
626 /* puts("wWorkspaceManageEdge()"); */
627 if (wPreferences.vedge_thickness) {
628 initVDesk = True;
629 for (w = 0; w < scr->workspace_count; w++) {
630 /* puts("reset workspace"); */
631 wWorkspaceSetViewPort(scr, w, 0, 0);
634 vmask = CWEventMask|CWOverrideRedirect;
635 attribs.event_mask = (EnterWindowMask | LeaveWindowMask | VisibilityChangeMask);
636 attribs.override_redirect = True;
637 scr->virtual_edge_u =
638 XCreateWindow(dpy, scr->root_win, 0, 0,
639 scr->scr_width, wPreferences.vedge_thickness, 0,
640 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
641 scr->virtual_edge_d =
642 XCreateWindow(dpy, scr->root_win, 0, scr->scr_height-wPreferences.vedge_thickness,
643 scr->scr_width, wPreferences.vedge_thickness, 0,
644 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
645 scr->virtual_edge_l =
646 XCreateWindow(dpy, scr->root_win, 0, 0,
647 wPreferences.vedge_thickness, scr->scr_height, 0,
648 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
649 scr->virtual_edge_r =
650 XCreateWindow(dpy, scr->root_win, scr->scr_width-wPreferences.vedge_thickness, 0,
651 wPreferences.vedge_thickness, scr->scr_height, 0,
652 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
653 XMapWindow(dpy, scr->virtual_edge_u);
654 XMapWindow(dpy, scr->virtual_edge_d);
655 XMapWindow(dpy, scr->virtual_edge_l);
656 XMapWindow(dpy, scr->virtual_edge_r);
657 wWorkspaceRaiseEdge(scr);
661 void wWorkspaceRaiseEdge(WScreen *scr)
663 puts("raise edge");
664 if (wPreferences.vedge_thickness && initVDesk) {
665 XRaiseWindow(dpy, scr->virtual_edge_u);
666 XRaiseWindow(dpy, scr->virtual_edge_d);
667 XRaiseWindow(dpy, scr->virtual_edge_l);
668 XRaiseWindow(dpy, scr->virtual_edge_r);
672 void wWorkspaceLowerEdge(WScreen *scr)
674 puts("lower edge");
675 if (wPreferences.vedge_thickness && initVDesk) {
676 XLowerWindow(dpy, scr->virtual_edge_u);
677 XLowerWindow(dpy, scr->virtual_edge_d);
678 XLowerWindow(dpy, scr->virtual_edge_l);
679 XLowerWindow(dpy, scr->virtual_edge_r);
683 void wWorkspaceResizeViewPort(WScreen *scr, int workspace, int width, int height)
685 scr->workspaces[workspace]->width = WMAX(width,scr->scr_width);
686 scr->workspaces[workspace]->height = WMAX(height,scr->scr_height);
689 void updateWorkspaceGeometry(WScreen *scr, int workspace, int *view_x, int *view_y) {
690 int most_left, most_right, most_top, most_bottom;
691 WWindow *wwin;
693 /* adjust workspace layout */
694 wwin = scr->focused_window;
695 most_right = scr->scr_width;
696 most_bottom = scr->scr_height;
697 most_left = 0;
698 most_top = 0;
699 for(;wwin; wwin = wwin->prev) {
700 if (wwin->frame->workspace == workspace) {
701 if (!wwin->flags.miniaturized
702 && !wwin->flags.hidden) {
703 if (wwin->frame_x < most_left) { /* record positions, should this be cached? */
704 most_left = wwin->frame_x;
706 if ((int)wwin->frame_x + (int)wwin->frame->core->width > most_right) {
707 most_right = wwin->frame_x + wwin->frame->core->width;
709 if (wwin->frame_y < most_top) {
710 most_top = wwin->frame_y;
712 if (wwin->frame_y + wwin->frame->core->height > most_bottom) {
713 most_bottom = wwin->frame_y + wwin->frame->core->height;
719 scr->workspaces[workspace]->width = WMAX(most_right, scr->scr_width) - WMIN(most_left, 0);
720 scr->workspaces[workspace]->height = WMAX(most_bottom, scr->scr_height) - WMIN(most_top, 0);
722 *view_x += -most_left - scr->workspaces[workspace]->view_x;
723 scr->workspaces[workspace]->view_x = -most_left;
725 *view_y += -most_top - scr->workspaces[workspace]->view_y;
726 scr->workspaces[workspace]->view_y = -most_top;
730 typedef struct _delay_configure {
731 WWindow *wwin;
732 int delay_count;
733 } _delay_configure;
735 void _sendConfigureNotify (_delay_configure *delay) {
736 WWindow *wwin;
738 delay->delay_count--;
739 if (!delay->delay_count) {
740 for (wwin = delay->wwin; wwin; wwin = wwin->prev) {
741 wWindowSynthConfigureNotify(wwin);
746 Bool wWorkspaceSetViewPort(WScreen *scr, int workspace, int view_x, int view_y)
748 Bool adjust_flag = False;
749 int diff_x, diff_y;
750 static _delay_configure delay_configure = {NULL, 0};
751 WWindow *wwin;
753 /*printf("wWorkspaceSetViewPort %d %d\n", view_x, view_y);*/
755 updateWorkspaceGeometry(scr, workspace, &view_x, &view_y);
757 if (view_x + scr->scr_width > scr->workspaces[workspace]->width) {
758 /* puts("right edge of vdesk"); */
759 view_x = scr->workspaces[workspace]->width - scr->scr_width;
761 if (view_x < 0) {
762 /* puts("left edge of vdesk"); */
763 view_x = 0;
765 if (view_y + scr->scr_height > scr->workspaces[workspace]->height) {
766 /* puts("right edge of vdesk"); */
767 view_y = scr->workspaces[workspace]->height - scr->scr_height;
769 if (view_y < 0) {
770 /* puts("left edge of vdesk"); */
771 view_y = 0;
774 diff_x = scr->workspaces[workspace]->view_x - view_x;
775 diff_y = scr->workspaces[workspace]->view_y - view_y;
776 if (!diff_x && !diff_y)
777 return False;
779 scr->workspaces[workspace]->view_x = view_x;
780 scr->workspaces[workspace]->view_y = view_y;
783 for (wwin = scr->focused_window; wwin; wwin = wwin->prev) {
784 if (wwin->frame->workspace == workspace) {
785 wWindowMove(wwin, wwin->frame_x + diff_x, wwin->frame_y + diff_y);
788 if (1) { /* if delay*/
789 delay_configure.delay_count++;
790 delay_configure.wwin = scr->focused_window;
791 WMAddTimerHandler(200, (WMCallback *)_sendConfigureNotify, &delay_configure);
794 return adjust_flag;
797 void wWorkspaceGetViewPosition(WScreen *scr, int workspace, int *view_x, int *view_y) {
798 if (view_x) *view_x = scr->workspaces[workspace]->view_x;
799 if (view_y) *view_y = scr->workspaces[workspace]->view_y;
802 #endif
804 static void
805 switchWSCommand(WMenu *menu, WMenuEntry *entry)
807 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
812 static void
813 deleteWSCommand(WMenu *menu, WMenuEntry *entry)
815 wWorkspaceDelete(menu->frame->screen_ptr,
816 menu->frame->screen_ptr->workspace_count-1);
821 static void
822 newWSCommand(WMenu *menu, WMenuEntry *foo)
824 int ws;
826 ws = wWorkspaceNew(menu->frame->screen_ptr);
827 /* autochange workspace*/
828 if (ws>=0)
829 wWorkspaceChange(menu->frame->screen_ptr, ws);
833 if (ws<9) {
834 int kcode;
835 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
836 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
837 entry->rtext =
838 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
844 static char*
845 cropline(char *line)
847 char *start, *end;
849 if (strlen(line)==0)
850 return line;
852 start = line;
853 end = &(line[strlen(line)])-1;
854 while (isspace(*line) && *line!=0) line++;
855 while (isspace(*end) && end!=line) {
856 *end=0;
857 end--;
859 return line;
863 void
864 wWorkspaceRename(WScreen *scr, int workspace, char *name)
866 char buf[MAX_WORKSPACENAME_WIDTH+1];
867 char *tmp;
869 if (workspace >= scr->workspace_count)
870 return;
872 /* trim white spaces */
873 tmp = cropline(name);
875 if (strlen(tmp)==0) {
876 sprintf(buf, _("Workspace %i"), workspace+1);
877 } else {
878 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
880 buf[MAX_WORKSPACENAME_WIDTH] = 0;
882 /* update workspace */
883 wfree(scr->workspaces[workspace]->name);
884 scr->workspaces[workspace]->name = wstrdup(buf);
886 if (scr->clip_ws_menu) {
887 if (strcmp(scr->clip_ws_menu->entries[workspace+2]->text, buf)!=0) {
888 wfree(scr->clip_ws_menu->entries[workspace+2]->text);
889 scr->clip_ws_menu->entries[workspace+2]->text = wstrdup(buf);
890 wMenuRealize(scr->clip_ws_menu);
893 if (scr->workspace_menu) {
894 if (strcmp(scr->workspace_menu->entries[workspace+2]->text, buf)!=0) {
895 wfree(scr->workspace_menu->entries[workspace+2]->text);
896 scr->workspace_menu->entries[workspace+2]->text = wstrdup(buf);
897 wMenuRealize(scr->workspace_menu);
901 if (scr->clip_icon)
902 wClipIconPaint(scr->clip_icon);
904 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)workspace);
910 /* callback for when menu entry is edited */
911 static void
912 onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
914 char *tmp;
916 tmp = entry->text;
917 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
921 WMenu*
922 wWorkspaceMenuMake(WScreen *scr, Bool titled)
924 WMenu *wsmenu;
926 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
927 if (!wsmenu) {
928 wwarning(_("could not create Workspace menu"));
929 return NULL;
932 /* callback to be called when an entry is edited */
933 wsmenu->on_edit = onMenuEntryEdited;
935 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
936 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
938 return wsmenu;
943 void
944 wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
946 int i;
947 long ws;
948 char title[MAX_WORKSPACENAME_WIDTH+1];
949 WMenuEntry *entry;
950 int tmp;
952 if (!menu)
953 return;
955 if (menu->entry_no < scr->workspace_count+2) {
956 /* new workspace(s) added */
957 i = scr->workspace_count-(menu->entry_no-2);
958 ws = menu->entry_no - 2;
959 while (i>0) {
960 strcpy(title, scr->workspaces[ws]->name);
962 entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
963 entry->flags.indicator = 1;
964 entry->flags.editable = 1;
966 i--;
967 ws++;
969 } else if (menu->entry_no > scr->workspace_count+2) {
970 /* removed workspace(s) */
971 for (i = menu->entry_no-1; i >= scr->workspace_count+2; i--) {
972 wMenuRemoveItem(menu, i);
975 wMenuRealize(menu);
977 for (i=0; i<scr->workspace_count; i++) {
978 menu->entries[i+2]->flags.indicator_on = 0;
980 menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;
982 /* don't let user destroy current workspace */
983 if (scr->current_workspace == scr->workspace_count-1) {
984 wMenuSetEnabled(menu, 1, False);
985 } else {
986 wMenuSetEnabled(menu, 1, True);
989 tmp = menu->frame->top_width + 5;
990 /* if menu got unreachable, bring it to a visible place */
991 if (menu->frame_x < tmp - (int)menu->frame->core->width)
992 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
994 wMenuPaint(menu);
998 void
999 wWorkspaceSaveState(WScreen *scr, WMPropList *old_state)
1001 WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
1002 int i;
1004 make_keys();
1006 old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
1007 parr = WMCreatePLArray(NULL);
1008 for (i=0; i < scr->workspace_count; i++) {
1009 pstr = WMCreatePLString(scr->workspaces[i]->name);
1010 wks_state = WMCreatePLDictionary(dName, pstr, NULL);
1011 WMReleasePropList(pstr);
1012 if (!wPreferences.flags.noclip) {
1013 pstr = wClipSaveWorkspaceState(scr, i);
1014 WMPutInPLDictionary(wks_state, dClip, pstr);
1015 WMReleasePropList(pstr);
1016 } else if (old_wks_state!=NULL) {
1017 if ((foo = WMGetFromPLArray(old_wks_state, i))!=NULL) {
1018 if ((bar = WMGetFromPLDictionary(foo, dClip))!=NULL) {
1019 WMPutInPLDictionary(wks_state, dClip, bar);
1023 WMAddToPLArray(parr, wks_state);
1024 WMReleasePropList(wks_state);
1026 WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
1027 WMReleasePropList(parr);
1031 void
1032 wWorkspaceRestoreState(WScreen *scr)
1034 WMPropList *parr, *pstr, *wks_state, *clip_state;
1035 int i, j, wscount;
1037 make_keys();
1039 parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
1041 if (!parr)
1042 return;
1044 wscount = scr->workspace_count;
1045 for (i=0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
1046 wks_state = WMGetFromPLArray(parr, i);
1047 if (WMIsPLDictionary(wks_state))
1048 pstr = WMGetFromPLDictionary(wks_state, dName);
1049 else
1050 pstr = wks_state;
1051 if (i >= scr->workspace_count)
1052 wWorkspaceNew(scr);
1053 if (scr->workspace_menu) {
1054 wfree(scr->workspace_menu->entries[i+2]->text);
1055 scr->workspace_menu->entries[i+2]->text = wstrdup(WMGetFromPLString(pstr));
1056 scr->workspace_menu->flags.realized = 0;
1058 wfree(scr->workspaces[i]->name);
1059 scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
1060 if (!wPreferences.flags.noclip) {
1061 clip_state = WMGetFromPLDictionary(wks_state, dClip);
1062 if (scr->workspaces[i]->clip)
1063 wDockDestroy(scr->workspaces[i]->clip);
1064 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state,
1065 WM_CLIP);
1066 if (i>0)
1067 wDockHideIcons(scr->workspaces[i]->clip);
1069 /* We set the global icons here, because scr->workspaces[i]->clip
1070 * was not valid in wDockRestoreState().
1071 * There we only set icon->omnipresent to know which icons we
1072 * need to set here.
1074 for (j=0; j<scr->workspaces[i]->clip->max_icons; j++) {
1075 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
1077 if (aicon && aicon->omnipresent) {
1078 aicon->omnipresent = 0;
1079 wClipMakeIconOmnipresent(aicon, True);
1080 XMapWindow(dpy, aicon->icon->core->window);
1085 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)i);