1 /* workspace.c- Workspace management
3 * WindowMaker 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"
48 extern WPreferences wPreferences
;
49 extern XContext wWinContext
;
52 static proplist_t dWorkspaces
=NULL
;
53 static proplist_t dClip
, dName
;
59 if (dWorkspaces
!=NULL
)
62 dWorkspaces
= PLMakeString("Workspaces");
63 dName
= PLMakeString("Name");
64 dClip
= PLMakeString("Clip");
69 wWorkspaceMake(WScreen
*scr
, int count
)
79 wWorkspaceNew(WScreen
*scr
)
81 WWorkspace
*wspace
, **list
;
84 if (scr
->workspace_count
< MAX_WORKSPACES
) {
85 scr
->workspace_count
++;
87 wspace
= wmalloc(sizeof(WWorkspace
));
89 wspace
->name
= wmalloc(strlen(_("Workspace %i"))+8);
90 sprintf(wspace
->name
, _("Workspace %i"), scr
->workspace_count
);
91 if (!wPreferences
.flags
.noclip
) {
92 wspace
->clip
= wDockCreate(scr
, WM_CLIP
);
97 list
= wmalloc(sizeof(WWorkspace
*)*scr
->workspace_count
);
99 for (i
=0; i
<scr
->workspace_count
-1; i
++) {
100 list
[i
] = scr
->workspaces
[i
];
103 free(scr
->workspaces
);
104 scr
->workspaces
= list
;
106 wWorkspaceMenuUpdate(scr
, scr
->workspace_menu
);
107 wWorkspaceMenuUpdate(scr
, scr
->clip_ws_menu
);
109 return scr
->workspace_count
-1;
117 wWorkspaceDelete(WScreen
*scr
, int workspace
)
126 /* verify if workspace is in use by some window */
127 tmp
= scr
->focused_window
;
129 if (tmp
->frame
->workspace
==workspace
)
134 if (!wPreferences
.flags
.noclip
) {
135 wDockDestroy(scr
->workspaces
[workspace
]->clip
);
136 scr
->workspaces
[workspace
]->clip
= NULL
;
139 list
= wmalloc(sizeof(WWorkspace
*)*(scr
->workspace_count
-1));
141 for (i
=0; i
<scr
->workspace_count
; i
++) {
143 list
[j
++] = scr
->workspaces
[i
];
145 if (scr
->workspaces
[i
]->name
)
146 free(scr
->workspaces
[i
]->name
);
147 free(scr
->workspaces
[i
]);
150 free(scr
->workspaces
);
151 scr
->workspaces
= list
;
153 scr
->workspace_count
--;
157 wWorkspaceMenuUpdate(scr
, scr
->workspace_menu
);
158 /* clip workspace menu */
159 wWorkspaceMenuUpdate(scr
, scr
->clip_ws_menu
);
161 /* update also window menu */
162 if (scr
->workspace_submenu
) {
163 WMenu
*menu
= scr
->workspace_submenu
;
166 while (i
>scr
->workspace_count
)
167 wMenuRemoveItem(menu
, --i
);
171 if (scr
->clip_submenu
) {
172 WMenu
*menu
= scr
->clip_submenu
;
175 while (i
>scr
->workspace_count
)
176 wMenuRemoveItem(menu
, --i
);
183 wWorkspaceChange(WScreen
*scr
, int workspace
)
185 if (workspace
!= scr
->current_workspace
) {
186 wWorkspaceForceChange(scr
, workspace
);
191 wWorkspaceForceChange(WScreen
*scr
, int workspace
)
193 WWindow
*tmp
, *foc
=NULL
;
195 WWorkspaceTexture
*wsback
;
198 if (workspace
>= MAX_WORKSPACES
|| workspace
< 0)
202 /* update the background for the workspace */
203 if (workspace
< scr
->wspaceTextureCount
204 && scr
->wspaceTextures
[workspace
]) {
205 wsback
= scr
->wspaceTextures
[workspace
];
207 /* check if the previous workspace used the default background */
208 if (scr
->current_workspace
< 0
209 || scr
->current_workspace
>= scr
->wspaceTextureCount
210 || scr
->wspaceTextures
[scr
->current_workspace
]==NULL
) {
213 wsback
= scr
->defaultTexure
;
217 if (wsback
->pixmap
!=None
) {
218 XSetWindowBackgroundPixmap(dpy
, scr
->root_win
, wsback
->pixmap
);
220 XSetWindowBackground(dpy
, scr
->root_win
, wsback
->solid
);
222 XClearWindow(dpy
, scr
->root_win
);
225 #endif /* EXPERIMENTAL */
227 if (workspace
> scr
->workspace_count
-1) {
228 wWorkspaceMake(scr
, workspace
- scr
->workspace_count
+ 1);
231 wClipUpdateForWorkspaceChange(scr
, workspace
);
233 scr
->current_workspace
= workspace
;
235 wWorkspaceMenuUpdate(scr
, scr
->workspace_menu
);
237 wWorkspaceMenuUpdate(scr
, scr
->clip_ws_menu
);
239 if ((tmp
= scr
->focused_window
)!= NULL
) {
241 if (tmp
->frame
->workspace
!=workspace
&& !tmp
->flags
.selected
) {
242 /* unmap windows not on this workspace */
243 if ((tmp
->flags
.mapped
||tmp
->flags
.shaded
)
244 && !tmp
->window_flags
.omnipresent
245 && !tmp
->flags
.changing_workspace
) {
246 XUnmapWindow(dpy
, tmp
->frame
->core
->window
);
247 tmp
->flags
.mapped
= 0;
249 /* also unmap miniwindows not on this workspace */
250 if (tmp
->flags
.miniaturized
&&
251 !tmp
->window_flags
.omnipresent
&& tmp
->icon
) {
252 if (!wPreferences
.sticky_icons
) {
253 XUnmapWindow(dpy
, tmp
->icon
->core
->window
);
254 tmp
->icon
->mapped
= 0;
256 tmp
->icon
->mapped
= 1;
257 /* Why is this here? -Alfredo */
258 XMapWindow(dpy
, tmp
->icon
->core
->window
);
261 /* update current workspace of omnipresent windows */
262 if (tmp
->window_flags
.omnipresent
) {
263 WApplication
*wapp
= wApplicationOf(tmp
->main_window
);
265 tmp
->frame
->workspace
= workspace
;
268 wapp
->last_workspace
= workspace
;
272 /* change selected windows' workspace */
273 if (tmp
->flags
.selected
) {
274 wWindowChangeWorkspace(tmp
, workspace
);
275 if (!tmp
->flags
.miniaturized
) {
279 if (!tmp
->flags
.hidden
) {
280 if (!(tmp
->flags
.mapped
|| tmp
->flags
.miniaturized
)) {
281 /* remap windows that are on this workspace */
282 XMapWindow(dpy
, tmp
->frame
->core
->window
);
285 if (!tmp
->flags
.shaded
)
286 tmp
->flags
.mapped
= 1;
288 /* Also map miniwindow if not omnipresent */
289 if (!wPreferences
.sticky_icons
&&
290 tmp
->flags
.miniaturized
&&
291 !tmp
->window_flags
.omnipresent
&& tmp
->icon
) {
292 tmp
->icon
->mapped
= 1;
293 XMapWindow(dpy
, tmp
->icon
->core
->window
);
301 if (scr
->focused_window
->flags
.mapped
) {
302 foc
= scr
->focused_window
;
304 if (wPreferences
.focus_mode
== WKF_CLICK
) {
305 wSetFocusTo(scr
, foc
);
313 if (XQueryPointer(dpy
, scr
->root_win
, &bar
, &win
,
314 &foo
, &foo
, &foo
, &foo
, &mask
)) {
315 tmp
= wWindowFor(win
);
317 if (!tmp
&& wPreferences
.focus_mode
== WKF_SLOPPY
) {
318 wSetFocusTo(scr
, foc
);
320 wSetFocusTo(scr
, tmp
);
325 /* We need to always arrange icons when changing workspace, even if
326 * no autoarrange icons, because else the icons in different workspaces
328 * This can be avoided if appicons are also workspace specific.
330 if (!wPreferences
.sticky_icons
)
331 wArrangeIcons(scr
, False
);
334 wAppIconPaint(scr
->dock
->icon_array
[0]);
335 if (scr
->clip_icon
) {
336 if (scr
->workspaces
[workspace
]->clip
->auto_collapse
) {
337 /* to handle enter notify. This will also */
338 XUnmapWindow(dpy
, scr
->clip_icon
->icon
->core
->window
);
339 XMapWindow(dpy
, scr
->clip_icon
->icon
->core
->window
);
341 wClipIconPaint(scr
->clip_icon
);
348 switchWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
350 wWorkspaceChange(menu
->frame
->screen_ptr
, (int)entry
->clientdata
);
356 deleteWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
358 wWorkspaceDelete(menu
->frame
->screen_ptr
,
359 menu
->frame
->screen_ptr
->workspace_count
-1);
365 newWSCommand(WMenu
*menu
, WMenuEntry
*foo
)
369 ws
= wWorkspaceNew(menu
->frame
->screen_ptr
);
370 /* autochange workspace*/
372 wWorkspaceChange(menu
->frame
->screen_ptr
, ws
);
378 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
379 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
381 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
396 end
= &(line
[strlen(line
)])-1;
397 while (isspace(*line
) && *line
!=0) line
++;
398 while (isspace(*end
) && end
!=line
) {
407 wWorkspaceRename(WScreen
*scr
, int workspace
, char *name
)
409 char buf
[MAX_WORKSPACENAME_WIDTH
+1];
412 if (workspace
>= scr
->workspace_count
)
415 /* trim white spaces */
416 tmp
= cropline(name
);
418 if (strlen(tmp
)==0) {
419 sprintf(buf
, _("Workspace %i"), workspace
+1);
421 strncpy(buf
, tmp
, MAX_WORKSPACENAME_WIDTH
);
423 buf
[MAX_WORKSPACENAME_WIDTH
] = 0;
425 /* update workspace */
426 free(scr
->workspaces
[workspace
]->name
);
427 scr
->workspaces
[workspace
]->name
= wstrdup(buf
);
429 if (scr
->clip_ws_menu
) {
430 if (strcmp(scr
->clip_ws_menu
->entries
[workspace
+2]->text
, buf
)!=0) {
431 free(scr
->clip_ws_menu
->entries
[workspace
+2]->text
);
432 scr
->clip_ws_menu
->entries
[workspace
+2]->text
= wstrdup(buf
);
433 wMenuRealize(scr
->clip_ws_menu
);
436 if (scr
->workspace_menu
) {
437 if (strcmp(scr
->workspace_menu
->entries
[workspace
+2]->text
, buf
)!=0) {
438 free(scr
->workspace_menu
->entries
[workspace
+2]->text
);
439 scr
->workspace_menu
->entries
[workspace
+2]->text
= wstrdup(buf
);
440 wMenuRealize(scr
->workspace_menu
);
443 UpdateSwitchMenuWorkspace(scr
, workspace
);
445 wClipIconPaint(scr
->clip_icon
);
451 /* callback for when menu entry is edited */
453 onMenuEntryEdited(WMenu
*menu
, WMenuEntry
*entry
)
458 wWorkspaceRename(menu
->frame
->screen_ptr
, (int)entry
->clientdata
, tmp
);
463 wWorkspaceMenuMake(WScreen
*scr
, Bool titled
)
467 wsmenu
= wMenuCreate(scr
, titled
? _("Workspaces") : NULL
, False
);
469 wwarning(_("could not create Workspace menu"));
473 /* callback to be called when an entry is edited */
474 wsmenu
->on_edit
= onMenuEntryEdited
;
476 wMenuAddCallback(wsmenu
, _("New"), newWSCommand
, NULL
);
477 wMenuAddCallback(wsmenu
, _("Destroy Last"), deleteWSCommand
, NULL
);
485 wWorkspaceMenuUpdate(WScreen
*scr
, WMenu
*menu
)
488 char title
[MAX_WORKSPACENAME_WIDTH
+1];
495 if (menu
->entry_no
< scr
->workspace_count
+2) {
496 /* new workspace(s) added */
497 i
= scr
->workspace_count
-(menu
->entry_no
-2);
498 ws
= menu
->entry_no
- 2;
500 strcpy(title
, scr
->workspaces
[ws
]->name
);
502 entry
= wMenuAddCallback(menu
, title
, switchWSCommand
, (void*)ws
);
503 entry
->flags
.indicator
= 1;
504 entry
->flags
.editable
= 1;
509 } else if (menu
->entry_no
> scr
->workspace_count
+2) {
510 /* removed workspace(s) */
511 for (i
= menu
->entry_no
-1; i
>= scr
->workspace_count
+2; i
--) {
512 wMenuRemoveItem(menu
, i
);
517 for (i
=0; i
<scr
->workspace_count
; i
++) {
518 menu
->entries
[i
+2]->flags
.indicator_on
= 0;
520 menu
->entries
[scr
->current_workspace
+2]->flags
.indicator_on
= 1;
522 /* don't let user destroy current workspace */
523 if (scr
->current_workspace
== scr
->workspace_count
-1) {
524 wMenuSetEnabled(menu
, 1, False
);
526 wMenuSetEnabled(menu
, 1, True
);
529 tmp
= menu
->frame
->top_width
+ 5;
530 /* if menu got unreachable, bring it to a visible place */
531 if (menu
->frame_x
< tmp
- (int)menu
->frame
->core
->width
)
532 wMenuMove(menu
, tmp
- (int)menu
->frame
->core
->width
, menu
->frame_y
, False
);
539 wWorkspaceSaveState(WScreen
*scr
, proplist_t old_state
)
541 proplist_t parr
, pstr
;
542 proplist_t wks_state
, old_wks_state
;
548 old_wks_state
= PLGetDictionaryEntry(old_state
, dWorkspaces
);
549 parr
= PLMakeArrayFromElements(NULL
);
550 for (i
=0; i
< scr
->workspace_count
; i
++) {
551 pstr
= PLMakeString(scr
->workspaces
[i
]->name
);
552 wks_state
= PLMakeDictionaryFromEntries(dName
, pstr
, NULL
);
554 if (!wPreferences
.flags
.noclip
) {
555 pstr
= wClipSaveWorkspaceState(scr
, i
);
556 PLInsertDictionaryEntry(wks_state
, dClip
, pstr
);
558 } else if (old_wks_state
!=NULL
) {
559 if ((foo
= PLGetArrayElement(old_wks_state
, i
))!=NULL
) {
560 if ((bar
= PLGetDictionaryEntry(foo
, dClip
))!=NULL
) {
561 PLInsertDictionaryEntry(wks_state
, dClip
, bar
);
565 PLAppendArrayElement(parr
, wks_state
);
566 PLRelease(wks_state
);
568 PLInsertDictionaryEntry(scr
->session_state
, dWorkspaces
, parr
);
574 wWorkspaceRestoreState(WScreen
*scr
)
576 proplist_t parr
, pstr
, wks_state
;
577 proplist_t clip_state
;
582 parr
= PLGetDictionaryEntry(scr
->session_state
, dWorkspaces
);
587 wscount
= scr
->workspace_count
;
588 for (i
=0; i
< PLGetNumberOfElements(parr
); i
++) {
589 wks_state
= PLGetArrayElement(parr
, i
);
590 if (PLIsDictionary(wks_state
))
591 pstr
= PLGetDictionaryEntry(wks_state
, dName
);
594 if (i
>= scr
->workspace_count
)
596 if (scr
->workspace_menu
) {
597 free(scr
->workspace_menu
->entries
[i
+2]->text
);
598 scr
->workspace_menu
->entries
[i
+2]->text
= wstrdup(PLGetString(pstr
));
599 scr
->workspace_menu
->flags
.realized
= 0;
601 free(scr
->workspaces
[i
]->name
);
602 scr
->workspaces
[i
]->name
= wstrdup(PLGetString(pstr
));
603 if (!wPreferences
.flags
.noclip
) {
604 clip_state
= PLGetDictionaryEntry(wks_state
, dClip
);
605 if (scr
->workspaces
[i
]->clip
)
606 wDockDestroy(scr
->workspaces
[i
]->clip
);
607 scr
->workspaces
[i
]->clip
= wDockRestoreState(scr
, clip_state
,
610 wDockHideIcons(scr
->workspaces
[i
]->clip
);