- API change in WINGs for WMDraw*String().
[wmaker-crm.git] / src / workspace.c
blobbc93853963824cba8e3b94b9b29fc16f0b170c9b
1 /* workspace.c- Workspace management
3 * Window Maker window manager
5 * Copyright (c) 1997-2002 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/WINGsP.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 WMColor *color;
279 int w, h;
280 int px, py;
281 char *name = scr->workspaces[workspace]->name;
282 int len = strlen(name);
283 int x, y;
285 if (wPreferences.workspace_name_display_position == WD_NONE
286 || scr->workspace_count < 2)
287 return;
289 if (scr->workspace_name_timer) {
290 WMDeleteTimerHandler(scr->workspace_name_timer);
291 XUnmapWindow(dpy, scr->workspace_name);
292 XFlush(dpy);
294 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY,
295 hideWorkpaceName, scr);
297 if (scr->workspace_name_data) {
298 RReleaseImage(scr->workspace_name_data->back);
299 RReleaseImage(scr->workspace_name_data->text);
300 wfree(scr->workspace_name_data);
303 data = wmalloc(sizeof(WorkspaceNameData));
305 w = WMWidthOfString(scr->workspace_name_font, name, len);
306 h = WMFontHeight(scr->workspace_name_font);
308 switch (wPreferences.workspace_name_display_position) {
309 case WD_TOP:
310 px = (scr->scr_width - (w+4))/2;
311 py = 0;
312 break;
313 case WD_BOTTOM:
314 px = (scr->scr_width - (w+4))/2;
315 py = scr->scr_height - (h+4);
316 break;
317 case WD_TOPLEFT:
318 px = 0;
319 py = 0;
320 break;
321 case WD_TOPRIGHT:
322 px = scr->scr_width - (w+4);
323 py = 0;
324 break;
325 case WD_BOTTOMLEFT:
326 px = 0;
327 py = scr->scr_height - (h+4);
328 break;
329 case WD_BOTTOMRIGHT:
330 px = scr->scr_width - (w+4);
331 py = scr->scr_height - (h+4);
332 break;
333 case WD_CENTER:
334 default:
335 px = (scr->scr_width - (w+4))/2;
336 py = (scr->scr_height - (h+4))/2;
337 break;
339 XResizeWindow(dpy, scr->workspace_name, w+4, h+4);
340 XMoveWindow(dpy, scr->workspace_name, px, py);
342 text = XCreatePixmap(dpy, scr->w_win, w+4, h+4, scr->w_depth);
343 mask = XCreatePixmap(dpy, scr->w_win, w+4, h+4, 1);
345 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
346 XFillRectangle(dpy, text, scr->draw_gc, 0, 0, w+4, h+4);
348 XSetForeground(dpy, scr->mono_gc, 0);
349 XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4);
351 XSetForeground(dpy, scr->mono_gc, 1);
353 color = WMWhiteColor(scr->wmscreen);
355 for (x = 0; x <= 4; x++) {
356 GC saveGC = scr->wmscreen->drawStringGC;
357 WMPixel pixel = color->color.pixel;
359 /* ugly hack */
360 color->color.pixel = 1;
361 scr->wmscreen->drawStringGC = scr->mono_gc;
363 for (y = 0; y <= 4; y++) {
364 WMDrawString(scr->wmscreen, mask, color,
365 scr->workspace_name_font, x, y, name, len);
368 scr->wmscreen->drawStringGC = saveGC;
369 color->color.pixel = pixel;
372 WMDrawString(scr->wmscreen, text, color, scr->workspace_name_font,
373 2, 2, scr->workspaces[workspace]->name,
374 strlen(scr->workspaces[workspace]->name));
375 WMReleaseColor(color);
376 #ifdef SHAPE
377 XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask,
378 ShapeSet);
379 #endif
380 XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
381 XClearWindow(dpy, scr->workspace_name);
383 data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
385 XFreePixmap(dpy, text);
386 XFreePixmap(dpy, mask);
388 if (!data->text) {
389 XMapRaised(dpy, scr->workspace_name);
390 XFlush(dpy);
392 goto erro;
395 ximg = RGetXImage(scr->rcontext, scr->root_win, px, py,
396 data->text->width, data->text->height);
398 if (!ximg || !ximg->image) {
399 goto erro;
402 XMapRaised(dpy, scr->workspace_name);
403 XFlush(dpy);
405 data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
406 RDestroyXImage(scr->rcontext, ximg);
408 if (!data->back) {
409 goto erro;
412 data->count = 10;
414 /* set a timeout for the effect */
415 data->timeout = time(NULL) + 2 +
416 (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY*data->count)/1000;
418 scr->workspace_name_data = data;
421 return;
423 erro:
424 if (scr->workspace_name_timer)
425 WMDeleteTimerHandler(scr->workspace_name_timer);
427 if (data->text)
428 RReleaseImage(data->text);
429 if (data->back)
430 RReleaseImage(data->back);
431 wfree(data);
433 scr->workspace_name_data = NULL;
435 scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
436 10*WORKSPACE_NAME_FADE_DELAY,
437 hideWorkpaceName, scr);
441 void
442 wWorkspaceChange(WScreen *scr, int workspace)
444 if (scr->flags.startup || scr->flags.startup2) {
445 return;
448 if (workspace != scr->current_workspace) {
449 wWorkspaceForceChange(scr, workspace);
450 } /*else {
451 showWorkspaceName(scr, workspace);
456 void
457 wWorkspaceRelativeChange(WScreen *scr, int amount)
459 int w;
461 w = scr->current_workspace + amount;
463 if (amount < 0) {
465 if (w >= 0)
466 wWorkspaceChange(scr, w);
467 else if (wPreferences.ws_cycle)
468 wWorkspaceChange(scr, scr->workspace_count + w);
470 } else if (amount > 0) {
472 if (w < scr->workspace_count)
473 wWorkspaceChange(scr, w);
474 else if (wPreferences.ws_advance)
475 wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES-1));
476 else if (wPreferences.ws_cycle)
477 wWorkspaceChange(scr, w % scr->workspace_count);
483 void
484 wWorkspaceForceChange(WScreen *scr, int workspace)
486 WWindow *tmp, *foc=NULL, *foc2=NULL;
488 if (workspace >= MAX_WORKSPACES || workspace < 0)
489 return;
491 SendHelperMessage(scr, 'C', workspace+1, NULL);
493 if (workspace > scr->workspace_count-1) {
494 wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
497 wClipUpdateForWorkspaceChange(scr, workspace);
499 scr->current_workspace = workspace;
501 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
503 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
505 if ((tmp = scr->focused_window)!= NULL) {
506 if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded)
507 && !WFLAGP(tmp, no_focusable))
508 || tmp->flags.changing_workspace) {
509 foc = tmp;
512 /* foc2 = tmp; will fix annoyance with gnome panel
513 * but will create annoyance for every other application
516 while (tmp) {
517 if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
518 /* unmap windows not on this workspace */
519 if ((tmp->flags.mapped||tmp->flags.shaded)
520 && !IS_OMNIPRESENT(tmp)
521 && !tmp->flags.changing_workspace) {
523 wWindowUnmap(tmp);
525 /* also unmap miniwindows not on this workspace */
526 if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
527 tmp->icon && !IS_OMNIPRESENT(tmp)) {
529 XUnmapWindow(dpy, tmp->icon->core->window);
530 tmp->icon->mapped = 0;
532 /* update current workspace of omnipresent windows */
533 if (IS_OMNIPRESENT(tmp)) {
534 WApplication *wapp = wApplicationOf(tmp->main_window);
536 tmp->frame->workspace = workspace;
538 if (wapp) {
539 wapp->last_workspace = workspace;
541 if (!foc2 && (tmp->flags.mapped || tmp->flags.shaded))
542 foc2 = tmp;
544 } else {
545 /* change selected windows' workspace */
546 if (tmp->flags.selected) {
547 wWindowChangeWorkspace(tmp, workspace);
548 if (!tmp->flags.miniaturized && !foc) {
549 foc = tmp;
551 } else {
552 if (!tmp->flags.hidden) {
553 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
554 /* remap windows that are on this workspace */
555 wWindowMap(tmp);
556 if (!foc && !WFLAGP(tmp, no_focusable))
557 foc = tmp;
559 /* Also map miniwindow if not omnipresent */
560 if (!wPreferences.sticky_icons &&
561 tmp->flags.miniaturized &&
562 !IS_OMNIPRESENT(tmp) && tmp->icon) {
563 tmp->icon->mapped = 1;
564 XMapWindow(dpy, tmp->icon->core->window);
569 tmp = tmp->prev;
572 if (!foc)
573 foc = foc2;
575 if (scr->focused_window->flags.mapped && !foc) {
576 foc = scr->focused_window;
578 if (wPreferences.focus_mode == WKF_CLICK) {
579 wSetFocusTo(scr, foc);
580 } else {
581 unsigned int mask;
582 int foo;
583 Window bar, win;
584 WWindow *tmp;
586 tmp = NULL;
587 if (XQueryPointer(dpy, scr->root_win, &bar, &win,
588 &foo, &foo, &foo, &foo, &mask)) {
589 tmp = wWindowFor(win);
591 if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
592 wSetFocusTo(scr, foc);
593 } else {
594 wSetFocusTo(scr, tmp);
599 /* We need to always arrange icons when changing workspace, even if
600 * no autoarrange icons, because else the icons in different workspaces
601 * can be superposed.
602 * This can be avoided if appicons are also workspace specific.
604 if (!wPreferences.sticky_icons)
605 wArrangeIcons(scr, False);
607 if (scr->dock)
608 wAppIconPaint(scr->dock->icon_array[0]);
610 if (scr->clip_icon) {
611 if (scr->workspaces[workspace]->clip->auto_collapse ||
612 scr->workspaces[workspace]->clip->auto_raise_lower) {
613 /* to handle enter notify. This will also */
614 XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
615 XMapWindow(dpy, scr->clip_icon->icon->core->window);
616 } else {
617 wClipIconPaint(scr->clip_icon);
621 showWorkspaceName(scr, workspace);
623 WMPostNotificationName(WMNWorkspaceChanged, scr, (void*)workspace);
625 /* XSync(dpy, False); */
628 #ifdef VIRTUAL_DESKTOP
629 /* TODO:
630 * 1) Allow border around each window so the scrolling
631 * won't just stop at the border.
632 * 2) Make pager.
635 void
636 wWorkspaceManageEdge(WScreen *scr)
638 int w;
639 int vmask;
640 XSetWindowAttributes attribs;
642 /* puts("wWorkspaceManageEdge()"); */
643 if (wPreferences.vedge_thickness) {
644 initVDesk = True;
645 for (w = 0; w < scr->workspace_count; w++) {
646 /* puts("reset workspace"); */
647 wWorkspaceSetViewPort(scr, w, 0, 0);
650 vmask = CWEventMask|CWOverrideRedirect;
651 attribs.event_mask = (EnterWindowMask | LeaveWindowMask | VisibilityChangeMask);
652 attribs.override_redirect = True;
653 scr->virtual_edge_u =
654 XCreateWindow(dpy, scr->root_win, 0, 0,
655 scr->scr_width, wPreferences.vedge_thickness, 0,
656 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
657 scr->virtual_edge_d =
658 XCreateWindow(dpy, scr->root_win, 0, scr->scr_height-wPreferences.vedge_thickness,
659 scr->scr_width, wPreferences.vedge_thickness, 0,
660 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
661 scr->virtual_edge_l =
662 XCreateWindow(dpy, scr->root_win, 0, 0,
663 wPreferences.vedge_thickness, scr->scr_height, 0,
664 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
665 scr->virtual_edge_r =
666 XCreateWindow(dpy, scr->root_win, scr->scr_width-wPreferences.vedge_thickness, 0,
667 wPreferences.vedge_thickness, scr->scr_height, 0,
668 CopyFromParent, InputOnly, CopyFromParent, vmask, &attribs);
669 XMapWindow(dpy, scr->virtual_edge_u);
670 XMapWindow(dpy, scr->virtual_edge_d);
671 XMapWindow(dpy, scr->virtual_edge_l);
672 XMapWindow(dpy, scr->virtual_edge_r);
673 wWorkspaceRaiseEdge(scr);
677 void
678 wWorkspaceRaiseEdge(WScreen *scr)
680 puts("raise edge");
681 if (wPreferences.vedge_thickness && initVDesk) {
682 XRaiseWindow(dpy, scr->virtual_edge_u);
683 XRaiseWindow(dpy, scr->virtual_edge_d);
684 XRaiseWindow(dpy, scr->virtual_edge_l);
685 XRaiseWindow(dpy, scr->virtual_edge_r);
689 void
690 wWorkspaceLowerEdge(WScreen *scr)
692 puts("lower edge");
693 if (wPreferences.vedge_thickness && initVDesk) {
694 XLowerWindow(dpy, scr->virtual_edge_u);
695 XLowerWindow(dpy, scr->virtual_edge_d);
696 XLowerWindow(dpy, scr->virtual_edge_l);
697 XLowerWindow(dpy, scr->virtual_edge_r);
701 void
702 wWorkspaceResizeViewPort(WScreen *scr, int workspace, int width, int height)
704 scr->workspaces[workspace]->width = WMAX(width,scr->scr_width);
705 scr->workspaces[workspace]->height = WMAX(height,scr->scr_height);
708 void
709 updateWorkspaceGeometry(WScreen *scr, int workspace, int *view_x, int *view_y)
711 int most_left, most_right, most_top, most_bottom;
712 WWindow *wwin;
714 /* adjust workspace layout */
715 wwin = scr->focused_window;
716 most_right = scr->scr_width;
717 most_bottom = scr->scr_height;
718 most_left = 0;
719 most_top = 0;
720 for(;wwin; wwin = wwin->prev) {
721 if (wwin->frame->workspace == workspace) {
722 if (!wwin->flags.miniaturized
723 && !wwin->flags.hidden) {
724 if (wwin->frame_x < most_left) { /* record positions, should this be cached? */
725 most_left = wwin->frame_x;
727 if ((int)wwin->frame_x + (int)wwin->frame->core->width > most_right) {
728 most_right = wwin->frame_x + wwin->frame->core->width;
730 if (wwin->frame_y < most_top) {
731 most_top = wwin->frame_y;
733 if (wwin->frame_y + wwin->frame->core->height > most_bottom) {
734 most_bottom = wwin->frame_y + wwin->frame->core->height;
740 scr->workspaces[workspace]->width = WMAX(most_right, scr->scr_width) - WMIN(most_left, 0);
741 scr->workspaces[workspace]->height = WMAX(most_bottom, scr->scr_height) - WMIN(most_top, 0);
743 *view_x += -most_left - scr->workspaces[workspace]->view_x;
744 scr->workspaces[workspace]->view_x = -most_left;
746 *view_y += -most_top - scr->workspaces[workspace]->view_y;
747 scr->workspaces[workspace]->view_y = -most_top;
752 typedef struct _delay_configure {
753 WWindow *wwin;
754 int delay_count;
755 } _delay_configure;
758 void
759 sendConfigureNotify (_delay_configure *delay)
761 WWindow *wwin;
763 delay->delay_count--;
764 if (!delay->delay_count) {
765 for (wwin = delay->wwin; wwin; wwin = wwin->prev) {
766 wWindowSynthConfigureNotify(wwin);
772 Bool
773 wWorkspaceSetViewPort(WScreen *scr, int workspace, int view_x, int view_y)
775 Bool adjust_flag = False;
776 int diff_x, diff_y;
777 static _delay_configure delay_configure = {NULL, 0};
778 WWindow *wwin;
780 /*printf("wWorkspaceSetViewPort %d %d\n", view_x, view_y);*/
782 updateWorkspaceGeometry(scr, workspace, &view_x, &view_y);
784 if (view_x + scr->scr_width > scr->workspaces[workspace]->width) {
785 /* puts("right edge of vdesk"); */
786 view_x = scr->workspaces[workspace]->width - scr->scr_width;
788 if (view_x < 0) {
789 /* puts("left edge of vdesk"); */
790 view_x = 0;
792 if (view_y + scr->scr_height > scr->workspaces[workspace]->height) {
793 /* puts("right edge of vdesk"); */
794 view_y = scr->workspaces[workspace]->height - scr->scr_height;
796 if (view_y < 0) {
797 /* puts("left edge of vdesk"); */
798 view_y = 0;
801 diff_x = scr->workspaces[workspace]->view_x - view_x;
802 diff_y = scr->workspaces[workspace]->view_y - view_y;
803 if (!diff_x && !diff_y)
804 return False;
806 scr->workspaces[workspace]->view_x = view_x;
807 scr->workspaces[workspace]->view_y = view_y;
810 for (wwin = scr->focused_window; wwin; wwin = wwin->prev) {
811 if (wwin->frame->workspace == workspace) {
812 wWindowMove(wwin, wwin->frame_x + diff_x, wwin->frame_y + diff_y);
815 if (1) { /* if delay*/
816 delay_configure.delay_count++;
817 delay_configure.wwin = scr->focused_window;
818 WMAddTimerHandler(200, (WMCallback *)sendConfigureNotify, &delay_configure);
821 return adjust_flag;
825 void
826 wWorkspaceGetViewPosition(WScreen *scr, int workspace, int *view_x, int *view_y)
828 if (view_x) *view_x = scr->workspaces[workspace]->view_x;
829 if (view_y) *view_y = scr->workspaces[workspace]->view_y;
831 #endif
835 static void
836 switchWSCommand(WMenu *menu, WMenuEntry *entry)
838 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
843 static void
844 deleteWSCommand(WMenu *menu, WMenuEntry *entry)
846 wWorkspaceDelete(menu->frame->screen_ptr,
847 menu->frame->screen_ptr->workspace_count-1);
852 static void
853 newWSCommand(WMenu *menu, WMenuEntry *foo)
855 int ws;
857 ws = wWorkspaceNew(menu->frame->screen_ptr);
858 /* autochange workspace*/
859 if (ws>=0)
860 wWorkspaceChange(menu->frame->screen_ptr, ws);
864 if (ws<9) {
865 int kcode;
866 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
867 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
868 entry->rtext =
869 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
875 static char*
876 cropline(char *line)
878 char *start, *end;
880 if (strlen(line)==0)
881 return line;
883 start = line;
884 end = &(line[strlen(line)])-1;
885 while (isspace(*line) && *line!=0) line++;
886 while (isspace(*end) && end!=line) {
887 *end=0;
888 end--;
890 return line;
894 void
895 wWorkspaceRename(WScreen *scr, int workspace, char *name)
897 char buf[MAX_WORKSPACENAME_WIDTH+1];
898 char *tmp;
900 if (workspace >= scr->workspace_count)
901 return;
903 /* trim white spaces */
904 tmp = cropline(name);
906 if (strlen(tmp)==0) {
907 sprintf(buf, _("Workspace %i"), workspace+1);
908 } else {
909 strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
911 buf[MAX_WORKSPACENAME_WIDTH] = 0;
913 /* update workspace */
914 wfree(scr->workspaces[workspace]->name);
915 scr->workspaces[workspace]->name = wstrdup(buf);
917 if (scr->clip_ws_menu) {
918 if (strcmp(scr->clip_ws_menu->entries[workspace+2]->text, buf)!=0) {
919 wfree(scr->clip_ws_menu->entries[workspace+2]->text);
920 scr->clip_ws_menu->entries[workspace+2]->text = wstrdup(buf);
921 wMenuRealize(scr->clip_ws_menu);
924 if (scr->workspace_menu) {
925 if (strcmp(scr->workspace_menu->entries[workspace+2]->text, buf)!=0) {
926 wfree(scr->workspace_menu->entries[workspace+2]->text);
927 scr->workspace_menu->entries[workspace+2]->text = wstrdup(buf);
928 wMenuRealize(scr->workspace_menu);
932 if (scr->clip_icon)
933 wClipIconPaint(scr->clip_icon);
935 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)workspace);
941 /* callback for when menu entry is edited */
942 static void
943 onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
945 char *tmp;
947 tmp = entry->text;
948 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
952 WMenu*
953 wWorkspaceMenuMake(WScreen *scr, Bool titled)
955 WMenu *wsmenu;
957 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
958 if (!wsmenu) {
959 wwarning(_("could not create Workspace menu"));
960 return NULL;
963 /* callback to be called when an entry is edited */
964 wsmenu->on_edit = onMenuEntryEdited;
966 wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
967 wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
969 return wsmenu;
974 void
975 wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
977 int i;
978 long ws;
979 char title[MAX_WORKSPACENAME_WIDTH+1];
980 WMenuEntry *entry;
981 int tmp;
983 if (!menu)
984 return;
986 if (menu->entry_no < scr->workspace_count+2) {
987 /* new workspace(s) added */
988 i = scr->workspace_count-(menu->entry_no-2);
989 ws = menu->entry_no - 2;
990 while (i>0) {
991 strcpy(title, scr->workspaces[ws]->name);
993 entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
994 entry->flags.indicator = 1;
995 entry->flags.editable = 1;
997 i--;
998 ws++;
1000 } else if (menu->entry_no > scr->workspace_count+2) {
1001 /* removed workspace(s) */
1002 for (i = menu->entry_no-1; i >= scr->workspace_count+2; i--) {
1003 wMenuRemoveItem(menu, i);
1006 wMenuRealize(menu);
1008 for (i=0; i<scr->workspace_count; i++) {
1009 menu->entries[i+2]->flags.indicator_on = 0;
1011 menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;
1013 /* don't let user destroy current workspace */
1014 if (scr->current_workspace == scr->workspace_count-1) {
1015 wMenuSetEnabled(menu, 1, False);
1016 } else {
1017 wMenuSetEnabled(menu, 1, True);
1020 tmp = menu->frame->top_width + 5;
1021 /* if menu got unreachable, bring it to a visible place */
1022 if (menu->frame_x < tmp - (int)menu->frame->core->width)
1023 wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
1025 wMenuPaint(menu);
1029 void
1030 wWorkspaceSaveState(WScreen *scr, WMPropList *old_state)
1032 WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
1033 int i;
1035 make_keys();
1037 old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
1038 parr = WMCreatePLArray(NULL);
1039 for (i=0; i < scr->workspace_count; i++) {
1040 pstr = WMCreatePLString(scr->workspaces[i]->name);
1041 wks_state = WMCreatePLDictionary(dName, pstr, NULL);
1042 WMReleasePropList(pstr);
1043 if (!wPreferences.flags.noclip) {
1044 pstr = wClipSaveWorkspaceState(scr, i);
1045 WMPutInPLDictionary(wks_state, dClip, pstr);
1046 WMReleasePropList(pstr);
1047 } else if (old_wks_state!=NULL) {
1048 if ((foo = WMGetFromPLArray(old_wks_state, i))!=NULL) {
1049 if ((bar = WMGetFromPLDictionary(foo, dClip))!=NULL) {
1050 WMPutInPLDictionary(wks_state, dClip, bar);
1054 WMAddToPLArray(parr, wks_state);
1055 WMReleasePropList(wks_state);
1057 WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
1058 WMReleasePropList(parr);
1062 void
1063 wWorkspaceRestoreState(WScreen *scr)
1065 WMPropList *parr, *pstr, *wks_state, *clip_state;
1066 int i, j, wscount;
1068 make_keys();
1070 if (scr->session_state == NULL)
1071 return;
1073 parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
1075 if (!parr)
1076 return;
1078 wscount = scr->workspace_count;
1079 for (i=0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
1080 wks_state = WMGetFromPLArray(parr, i);
1081 if (WMIsPLDictionary(wks_state))
1082 pstr = WMGetFromPLDictionary(wks_state, dName);
1083 else
1084 pstr = wks_state;
1085 if (i >= scr->workspace_count)
1086 wWorkspaceNew(scr);
1087 if (scr->workspace_menu) {
1088 wfree(scr->workspace_menu->entries[i+2]->text);
1089 scr->workspace_menu->entries[i+2]->text = wstrdup(WMGetFromPLString(pstr));
1090 scr->workspace_menu->flags.realized = 0;
1092 wfree(scr->workspaces[i]->name);
1093 scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
1094 if (!wPreferences.flags.noclip) {
1095 clip_state = WMGetFromPLDictionary(wks_state, dClip);
1096 if (scr->workspaces[i]->clip)
1097 wDockDestroy(scr->workspaces[i]->clip);
1098 scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state,
1099 WM_CLIP);
1100 if (i>0)
1101 wDockHideIcons(scr->workspaces[i]->clip);
1103 /* We set the global icons here, because scr->workspaces[i]->clip
1104 * was not valid in wDockRestoreState().
1105 * There we only set icon->omnipresent to know which icons we
1106 * need to set here.
1108 for (j=0; j<scr->workspaces[i]->clip->max_icons; j++) {
1109 WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
1111 if (aicon && aicon->omnipresent) {
1112 aicon->omnipresent = 0;
1113 wClipMakeIconOmnipresent(aicon, True);
1114 XMapWindow(dpy, aicon->icon->core->window);
1119 WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)i);