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,
25 #include <X11/Xutil.h>
32 #include "WindowMaker.h"
39 #include "application.h"
42 #include "workspace.h"
54 extern WPreferences wPreferences
;
55 extern XContext wWinContext
;
58 static proplist_t dWorkspaces
=NULL
;
59 static proplist_t dClip
, dName
;
65 if (dWorkspaces
!=NULL
)
68 dWorkspaces
= PLMakeString("Workspaces");
69 dName
= PLMakeString("Name");
70 dClip
= PLMakeString("Clip");
75 wWorkspaceMake(WScreen
*scr
, int count
)
85 wWorkspaceNew(WScreen
*scr
)
87 WWorkspace
*wspace
, **list
;
90 if (scr
->workspace_count
< MAX_WORKSPACES
) {
91 scr
->workspace_count
++;
93 wspace
= wmalloc(sizeof(WWorkspace
));
97 if (scr
->flags
.kwm_syncing_count
) {
98 wspace
->name
= wKWMGetWorkspaceName(scr
, scr
->workspace_count
-1);
102 wspace
->name
= wmalloc(strlen(_("Workspace %i"))+8);
103 sprintf(wspace
->name
, _("Workspace %i"), scr
->workspace_count
);
107 if (!wPreferences
.flags
.noclip
) {
108 wspace
->clip
= wDockCreate(scr
, WM_CLIP
);
112 list
= wmalloc(sizeof(WWorkspace
*)*scr
->workspace_count
);
114 for (i
=0; i
<scr
->workspace_count
-1; i
++) {
115 list
[i
] = scr
->workspaces
[i
];
118 free(scr
->workspaces
);
119 scr
->workspaces
= list
;
121 wWorkspaceMenuUpdate(scr
, scr
->workspace_menu
);
122 wWorkspaceMenuUpdate(scr
, scr
->clip_ws_menu
);
124 wGNOMEUpdateWorkspaceHints(scr
);
127 if (!scr
->flags
.kwm_syncing_count
) {
128 wKWMUpdateWorkspaceCountHint(scr
);
129 wKWMUpdateWorkspaceNameHint(scr
, scr
->workspace_count
-1);
132 wKWMSetUsableAreaHint(scr
, scr
->workspace_count
-1);
137 return scr
->workspace_count
-1;
145 wWorkspaceDelete(WScreen
*scr
, int workspace
)
155 /* verify if workspace is in use by some window */
156 tmp
= scr
->focused_window
;
158 if (!IS_OMNIPRESENT(tmp
) && tmp
->frame
->workspace
==workspace
)
163 if (!wPreferences
.flags
.noclip
) {
164 wDockDestroy(scr
->workspaces
[workspace
]->clip
);
165 scr
->workspaces
[workspace
]->clip
= NULL
;
168 list
= wmalloc(sizeof(WWorkspace
*)*(scr
->workspace_count
-1));
170 for (i
=0; i
<scr
->workspace_count
; i
++) {
172 list
[j
++] = scr
->workspaces
[i
];
174 if (scr
->workspaces
[i
]->name
)
175 free(scr
->workspaces
[i
]->name
);
176 free(scr
->workspaces
[i
]);
179 free(scr
->workspaces
);
180 scr
->workspaces
= list
;
182 scr
->workspace_count
--;
186 wWorkspaceMenuUpdate(scr
, scr
->workspace_menu
);
187 /* clip workspace menu */
188 wWorkspaceMenuUpdate(scr
, scr
->clip_ws_menu
);
190 /* update also window menu */
191 if (scr
->workspace_submenu
) {
192 WMenu
*menu
= scr
->workspace_submenu
;
195 while (i
>scr
->workspace_count
)
196 wMenuRemoveItem(menu
, --i
);
200 if (scr
->clip_submenu
) {
201 WMenu
*menu
= scr
->clip_submenu
;
204 while (i
>scr
->workspace_count
)
205 wMenuRemoveItem(menu
, --i
);
210 wGNOMEUpdateWorkspaceHints(scr
);
213 wKWMUpdateWorkspaceCountHint(scr
);
216 if (scr
->current_workspace
>= scr
->workspace_count
)
217 wWorkspaceChange(scr
, scr
->workspace_count
-1);
224 wWorkspaceChange(WScreen
*scr
, int workspace
)
226 if (scr
->flags
.startup
|| scr
->flags
.startup2
) {
230 if (workspace
!= scr
->current_workspace
) {
231 wWorkspaceForceChange(scr
, workspace
);
237 wWorkspaceRelativeChange(WScreen
*scr
, int amount
)
241 w
= scr
->current_workspace
+ amount
;
246 wWorkspaceChange(scr
, w
);
247 else if (wPreferences
.ws_cycle
)
248 wWorkspaceChange(scr
, scr
->workspace_count
+ w
);
250 } else if (amount
> 0) {
252 if (w
< scr
->workspace_count
)
253 wWorkspaceChange(scr
, w
);
254 else if (wPreferences
.ws_advance
)
255 wWorkspaceChange(scr
, WMIN(w
, MAX_WORKSPACES
-1));
256 else if (wPreferences
.ws_cycle
)
257 wWorkspaceChange(scr
, w
% scr
->workspace_count
);
264 wWorkspaceForceChange(WScreen
*scr
, int workspace
)
266 WWindow
*tmp
, *foc
=NULL
, *foc2
=NULL
;
268 if (workspace
>= MAX_WORKSPACES
|| workspace
< 0)
271 SendHelperMessage(scr
, 'C', workspace
+1, NULL
);
273 if (workspace
> scr
->workspace_count
-1) {
274 wWorkspaceMake(scr
, workspace
- scr
->workspace_count
+ 1);
277 wClipUpdateForWorkspaceChange(scr
, workspace
);
279 scr
->current_workspace
= workspace
;
281 wWorkspaceMenuUpdate(scr
, scr
->workspace_menu
);
283 wWorkspaceMenuUpdate(scr
, scr
->clip_ws_menu
);
285 if ((tmp
= scr
->focused_window
)!= NULL
) {
286 if (IS_OMNIPRESENT(tmp
) || tmp
->flags
.changing_workspace
)
290 if (tmp
->frame
->workspace
!=workspace
&& !tmp
->flags
.selected
) {
291 /* unmap windows not on this workspace */
292 if ((tmp
->flags
.mapped
||tmp
->flags
.shaded
)
293 && !IS_OMNIPRESENT(tmp
)
294 && !tmp
->flags
.changing_workspace
) {
298 /* also unmap miniwindows not on this workspace */
299 if (tmp
->flags
.miniaturized
&& !IS_OMNIPRESENT(tmp
)
301 if (!wPreferences
.sticky_icons
) {
302 XUnmapWindow(dpy
, tmp
->icon
->core
->window
);
303 tmp
->icon
->mapped
= 0;
305 tmp
->icon
->mapped
= 1;
306 /* Why is this here? -Alfredo */
307 XMapWindow(dpy
, tmp
->icon
->core
->window
);
310 /* update current workspace of omnipresent windows */
311 if (IS_OMNIPRESENT(tmp
)) {
312 WApplication
*wapp
= wApplicationOf(tmp
->main_window
);
314 tmp
->frame
->workspace
= workspace
;
317 wapp
->last_workspace
= workspace
;
323 /* change selected windows' workspace */
324 if (tmp
->flags
.selected
) {
325 wWindowChangeWorkspace(tmp
, workspace
);
326 if (!tmp
->flags
.miniaturized
&& !foc
) {
330 if (!tmp
->flags
.hidden
) {
331 if (!(tmp
->flags
.mapped
|| tmp
->flags
.miniaturized
)) {
332 /* remap windows that are on this workspace */
337 /* Also map miniwindow if not omnipresent */
338 if (!wPreferences
.sticky_icons
&&
339 tmp
->flags
.miniaturized
&&
340 !IS_OMNIPRESENT(tmp
) && tmp
->icon
) {
341 tmp
->icon
->mapped
= 1;
342 XMapWindow(dpy
, tmp
->icon
->core
->window
);
353 if (scr
->focused_window
->flags
.mapped
&& !foc
) {
354 foc
= scr
->focused_window
;
356 if (wPreferences
.focus_mode
== WKF_CLICK
) {
357 wSetFocusTo(scr
, foc
);
365 if (XQueryPointer(dpy
, scr
->root_win
, &bar
, &win
,
366 &foo
, &foo
, &foo
, &foo
, &mask
)) {
367 tmp
= wWindowFor(win
);
369 if (!tmp
&& wPreferences
.focus_mode
== WKF_SLOPPY
) {
370 wSetFocusTo(scr
, foc
);
372 wSetFocusTo(scr
, tmp
);
377 /* We need to always arrange icons when changing workspace, even if
378 * no autoarrange icons, because else the icons in different workspaces
380 * This can be avoided if appicons are also workspace specific.
382 if (!wPreferences
.sticky_icons
)
383 wArrangeIcons(scr
, False
);
386 wAppIconPaint(scr
->dock
->icon_array
[0]);
387 if (scr
->clip_icon
) {
388 if (scr
->workspaces
[workspace
]->clip
->auto_collapse
||
389 scr
->workspaces
[workspace
]->clip
->auto_raise_lower
) {
390 /* to handle enter notify. This will also */
391 XUnmapWindow(dpy
, scr
->clip_icon
->icon
->core
->window
);
392 XMapWindow(dpy
, scr
->clip_icon
->icon
->core
->window
);
394 wClipIconPaint(scr
->clip_icon
);
399 wGNOMEUpdateCurrentWorkspaceHint(scr
);
402 wKWMUpdateCurrentWorkspaceHint(scr
);
404 /* XSync(dpy, False); */
409 switchWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
411 wWorkspaceChange(menu
->frame
->screen_ptr
, (long)entry
->clientdata
);
417 deleteWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
419 wWorkspaceDelete(menu
->frame
->screen_ptr
,
420 menu
->frame
->screen_ptr
->workspace_count
-1);
426 newWSCommand(WMenu
*menu
, WMenuEntry
*foo
)
430 ws
= wWorkspaceNew(menu
->frame
->screen_ptr
);
431 /* autochange workspace*/
433 wWorkspaceChange(menu
->frame
->screen_ptr
, ws
);
439 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
440 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
442 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
457 end
= &(line
[strlen(line
)])-1;
458 while (isspace(*line
) && *line
!=0) line
++;
459 while (isspace(*end
) && end
!=line
) {
468 wWorkspaceRename(WScreen
*scr
, int workspace
, char *name
)
470 char buf
[MAX_WORKSPACENAME_WIDTH
+1];
473 if (workspace
>= scr
->workspace_count
)
476 /* trim white spaces */
477 tmp
= cropline(name
);
479 if (strlen(tmp
)==0) {
480 sprintf(buf
, _("Workspace %i"), workspace
+1);
482 strncpy(buf
, tmp
, MAX_WORKSPACENAME_WIDTH
);
484 buf
[MAX_WORKSPACENAME_WIDTH
] = 0;
486 /* update workspace */
487 free(scr
->workspaces
[workspace
]->name
);
488 scr
->workspaces
[workspace
]->name
= wstrdup(buf
);
490 if (scr
->clip_ws_menu
) {
491 if (strcmp(scr
->clip_ws_menu
->entries
[workspace
+2]->text
, buf
)!=0) {
492 free(scr
->clip_ws_menu
->entries
[workspace
+2]->text
);
493 scr
->clip_ws_menu
->entries
[workspace
+2]->text
= wstrdup(buf
);
494 wMenuRealize(scr
->clip_ws_menu
);
497 if (scr
->workspace_menu
) {
498 if (strcmp(scr
->workspace_menu
->entries
[workspace
+2]->text
, buf
)!=0) {
499 free(scr
->workspace_menu
->entries
[workspace
+2]->text
);
500 scr
->workspace_menu
->entries
[workspace
+2]->text
= wstrdup(buf
);
501 wMenuRealize(scr
->workspace_menu
);
505 UpdateSwitchMenuWorkspace(scr
, workspace
);
508 wClipIconPaint(scr
->clip_icon
);
511 wGNOMEUpdateWorkspaceNamesHint(scr
);
514 wKWMUpdateWorkspaceNameHint(scr
, workspace
);
521 /* callback for when menu entry is edited */
523 onMenuEntryEdited(WMenu
*menu
, WMenuEntry
*entry
)
528 wWorkspaceRename(menu
->frame
->screen_ptr
, (long)entry
->clientdata
, tmp
);
533 wWorkspaceMenuMake(WScreen
*scr
, Bool titled
)
537 wsmenu
= wMenuCreate(scr
, titled
? _("Workspaces") : NULL
, False
);
539 wwarning(_("could not create Workspace menu"));
543 /* callback to be called when an entry is edited */
544 wsmenu
->on_edit
= onMenuEntryEdited
;
546 wMenuAddCallback(wsmenu
, _("New"), newWSCommand
, NULL
);
547 wMenuAddCallback(wsmenu
, _("Destroy Last"), deleteWSCommand
, NULL
);
555 wWorkspaceMenuUpdate(WScreen
*scr
, WMenu
*menu
)
559 char title
[MAX_WORKSPACENAME_WIDTH
+1];
566 if (menu
->entry_no
< scr
->workspace_count
+2) {
567 /* new workspace(s) added */
568 i
= scr
->workspace_count
-(menu
->entry_no
-2);
569 ws
= menu
->entry_no
- 2;
571 strcpy(title
, scr
->workspaces
[ws
]->name
);
573 entry
= wMenuAddCallback(menu
, title
, switchWSCommand
, (void*)ws
);
574 entry
->flags
.indicator
= 1;
575 entry
->flags
.editable
= 1;
580 } else if (menu
->entry_no
> scr
->workspace_count
+2) {
581 /* removed workspace(s) */
582 for (i
= menu
->entry_no
-1; i
>= scr
->workspace_count
+2; i
--) {
583 wMenuRemoveItem(menu
, i
);
588 for (i
=0; i
<scr
->workspace_count
; i
++) {
589 menu
->entries
[i
+2]->flags
.indicator_on
= 0;
591 menu
->entries
[scr
->current_workspace
+2]->flags
.indicator_on
= 1;
593 /* don't let user destroy current workspace */
594 if (scr
->current_workspace
== scr
->workspace_count
-1) {
595 wMenuSetEnabled(menu
, 1, False
);
597 wMenuSetEnabled(menu
, 1, True
);
600 tmp
= menu
->frame
->top_width
+ 5;
601 /* if menu got unreachable, bring it to a visible place */
602 if (menu
->frame_x
< tmp
- (int)menu
->frame
->core
->width
)
603 wMenuMove(menu
, tmp
- (int)menu
->frame
->core
->width
, menu
->frame_y
, False
);
610 wWorkspaceSaveState(WScreen
*scr
, proplist_t old_state
)
612 proplist_t parr
, pstr
;
613 proplist_t wks_state
, old_wks_state
;
619 old_wks_state
= PLGetDictionaryEntry(old_state
, dWorkspaces
);
620 parr
= PLMakeArrayFromElements(NULL
);
621 for (i
=0; i
< scr
->workspace_count
; i
++) {
622 pstr
= PLMakeString(scr
->workspaces
[i
]->name
);
623 wks_state
= PLMakeDictionaryFromEntries(dName
, pstr
, NULL
);
625 if (!wPreferences
.flags
.noclip
) {
626 pstr
= wClipSaveWorkspaceState(scr
, i
);
627 PLInsertDictionaryEntry(wks_state
, dClip
, pstr
);
629 } else if (old_wks_state
!=NULL
) {
630 if ((foo
= PLGetArrayElement(old_wks_state
, i
))!=NULL
) {
631 if ((bar
= PLGetDictionaryEntry(foo
, dClip
))!=NULL
) {
632 PLInsertDictionaryEntry(wks_state
, dClip
, bar
);
636 PLAppendArrayElement(parr
, wks_state
);
637 PLRelease(wks_state
);
639 PLInsertDictionaryEntry(scr
->session_state
, dWorkspaces
, parr
);
645 wWorkspaceRestoreState(WScreen
*scr
)
647 proplist_t parr
, pstr
, wks_state
;
648 proplist_t clip_state
;
653 parr
= PLGetDictionaryEntry(scr
->session_state
, dWorkspaces
);
658 wscount
= scr
->workspace_count
;
659 for (i
=0; i
< PLGetNumberOfElements(parr
); i
++) {
660 wks_state
= PLGetArrayElement(parr
, i
);
661 if (PLIsDictionary(wks_state
))
662 pstr
= PLGetDictionaryEntry(wks_state
, dName
);
665 if (i
>= scr
->workspace_count
)
667 if (scr
->workspace_menu
) {
668 free(scr
->workspace_menu
->entries
[i
+2]->text
);
669 scr
->workspace_menu
->entries
[i
+2]->text
= wstrdup(PLGetString(pstr
));
670 scr
->workspace_menu
->flags
.realized
= 0;
672 free(scr
->workspaces
[i
]->name
);
673 scr
->workspaces
[i
]->name
= wstrdup(PLGetString(pstr
));
674 if (!wPreferences
.flags
.noclip
) {
675 clip_state
= PLGetDictionaryEntry(wks_state
, dClip
);
676 if (scr
->workspaces
[i
]->clip
)
677 wDockDestroy(scr
->workspaces
[i
]->clip
);
678 scr
->workspaces
[i
]->clip
= wDockRestoreState(scr
, clip_state
,
681 wDockHideIcons(scr
->workspaces
[i
]->clip
);
684 wKWMUpdateWorkspaceNameHint(scr
, i
);
688 wGNOMEUpdateWorkspaceNamesHint(scr
);