Initial revision
[wmaker-crm.git] / src / workspace.c
blob35d9251e8e967b85cd054a9a62ad1efde8bcf212
1 /* workspace.c- Workspace management
2 *
3 * WindowMaker 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 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <ctype.h>
30 #include <string.h>
32 #include "WindowMaker.h"
33 #include "wcore.h"
34 #include "framewin.h"
35 #include "window.h"
36 #include "icon.h"
37 #include "funcs.h"
38 #include "menu.h"
39 #include "application.h"
40 #include "dock.h"
41 #include "actions.h"
42 #include "workspace.h"
43 #include "appicon.h"
45 #include <proplist.h>
48 extern WPreferences wPreferences;
49 extern XContext wWinContext;
52 static proplist_t dWorkspaces=NULL;
53 static proplist_t dClip, dName;
56 static void
57 make_keys()
59 if (dWorkspaces!=NULL)
60 return;
62 dWorkspaces = PLMakeString("Workspaces");
63 dName = PLMakeString("Name");
64 dClip = PLMakeString("Clip");
68 void
69 wWorkspaceMake(WScreen *scr, int count)
71 while (count>0) {
72 wWorkspaceNew(scr);
73 count--;
78 int
79 wWorkspaceNew(WScreen *scr)
81 WWorkspace *wspace, **list;
82 int i;
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);
93 } else
94 wspace->clip = NULL;
97 list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
99 for (i=0; i<scr->workspace_count-1; i++) {
100 list[i] = scr->workspaces[i];
102 list[i] = wspace;
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;
111 return -1;
116 void
117 wWorkspaceDelete(WScreen *scr, int workspace)
119 WWindow *tmp;
120 WWorkspace **list;
121 int i, j;
123 if (workspace<=0)
124 return;
126 /* verify if workspace is in use by some window */
127 tmp = scr->focused_window;
128 while (tmp) {
129 if (tmp->frame->workspace==workspace)
130 return;
131 tmp = tmp->prev;
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));
140 j = 0;
141 for (i=0; i<scr->workspace_count; i++) {
142 if (i!=workspace)
143 list[j++] = scr->workspaces[i];
144 else {
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--;
156 /* update menu */
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;
165 i = menu->entry_no;
166 while (i>scr->workspace_count)
167 wMenuRemoveItem(menu, --i);
168 wMenuRealize(menu);
170 /* and clip menu */
171 if (scr->clip_submenu) {
172 WMenu *menu = scr->clip_submenu;
174 i = menu->entry_no;
175 while (i>scr->workspace_count)
176 wMenuRemoveItem(menu, --i);
177 wMenuRealize(menu);
182 void
183 wWorkspaceChange(WScreen *scr, int workspace)
185 if (workspace != scr->current_workspace) {
186 wWorkspaceForceChange(scr, workspace);
190 void
191 wWorkspaceForceChange(WScreen *scr, int workspace)
193 WWindow *tmp, *foc=NULL;
194 #ifdef EXPERIMENTAL
195 WWorkspaceTexture *wsback;
196 #endif
198 if (workspace >= MAX_WORKSPACES || workspace < 0)
199 return;
201 #ifdef EXPERIMENTAL
202 /* update the background for the workspace */
203 if (workspace < scr->wspaceTextureCount
204 && scr->wspaceTextures[workspace]) {
205 wsback = scr->wspaceTextures[workspace];
206 } else {
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) {
211 wsback = NULL;
212 } else {
213 wsback = scr->defaultTexure;
216 if (wsback) {
217 if (wsback->pixmap!=None) {
218 XSetWindowBackgroundPixmap(dpy, scr->root_win, wsback->pixmap);
219 } else {
220 XSetWindowBackground(dpy, scr->root_win, wsback->solid);
222 XClearWindow(dpy, scr->root_win);
223 XFlush(dpy);
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) {
240 while (tmp) {
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;
255 } else {
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;
267 if (wapp) {
268 wapp->last_workspace = workspace;
271 } else {
272 /* change selected windows' workspace */
273 if (tmp->flags.selected) {
274 wWindowChangeWorkspace(tmp, workspace);
275 if (!tmp->flags.miniaturized) {
276 foc = tmp;
278 } else {
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);
283 if (!foc)
284 foc = tmp;
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);
298 tmp = tmp->prev;
301 if (scr->focused_window->flags.mapped) {
302 foc = scr->focused_window;
304 if (wPreferences.focus_mode == WKF_CLICK) {
305 wSetFocusTo(scr, foc);
306 } else {
307 unsigned int mask;
308 int foo;
309 Window bar, win;
310 WWindow *tmp;
312 tmp = NULL;
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);
319 } else {
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
327 * can be superposed.
328 * This can be avoided if appicons are also workspace specific.
330 if (!wPreferences.sticky_icons)
331 wArrangeIcons(scr, False);
333 if (scr->dock)
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);
340 } else {
341 wClipIconPaint(scr->clip_icon);
347 static void
348 switchWSCommand(WMenu *menu, WMenuEntry *entry)
350 wWorkspaceChange(menu->frame->screen_ptr, (int)entry->clientdata);
355 static void
356 deleteWSCommand(WMenu *menu, WMenuEntry *entry)
358 wWorkspaceDelete(menu->frame->screen_ptr,
359 menu->frame->screen_ptr->workspace_count-1);
364 static void
365 newWSCommand(WMenu *menu, WMenuEntry *foo)
367 int ws;
369 ws = wWorkspaceNew(menu->frame->screen_ptr);
370 /* autochange workspace*/
371 if (ws>=0)
372 wWorkspaceChange(menu->frame->screen_ptr, ws);
376 if (ws<9) {
377 int kcode;
378 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
379 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
380 entry->rtext =
381 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
387 static char*
388 cropline(char *line)
390 char *start, *end;
392 if (strlen(line)==0)
393 return line;
395 start = line;
396 end = &(line[strlen(line)])-1;
397 while (isspace(*line) && *line!=0) line++;
398 while (isspace(*end) && end!=line) {
399 *end=0;
400 end--;
402 return line;
406 void
407 wWorkspaceRename(WScreen *scr, int workspace, char *name)
409 char buf[MAX_WORKSPACENAME_WIDTH+1];
410 char *tmp;
412 if (workspace >= scr->workspace_count)
413 return;
415 /* trim white spaces */
416 tmp = cropline(name);
418 if (strlen(tmp)==0) {
419 sprintf(buf, _("Workspace %i"), workspace+1);
420 } else {
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);
444 if (scr->clip_icon)
445 wClipIconPaint(scr->clip_icon);
451 /* callback for when menu entry is edited */
452 static void
453 onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
455 char *tmp;
457 tmp = entry->text;
458 wWorkspaceRename(menu->frame->screen_ptr, (int)entry->clientdata, tmp);
462 WMenu*
463 wWorkspaceMenuMake(WScreen *scr, Bool titled)
465 WMenu *wsmenu;
467 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
468 if (!wsmenu) {
469 wwarning(_("could not create Workspace menu"));
470 return NULL;
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);
479 return wsmenu;
484 void
485 wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
487 int i, ws;
488 char title[MAX_WORKSPACENAME_WIDTH+1];
489 WMenuEntry *entry;
490 int tmp;
492 if (!menu)
493 return;
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;
499 while (i>0) {
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;
506 i--;
507 ws++;
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);
515 wMenuRealize(menu);
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);
525 } else {
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);
534 wMenuPaint(menu);
538 void
539 wWorkspaceSaveState(WScreen *scr, proplist_t old_state)
541 proplist_t parr, pstr;
542 proplist_t wks_state, old_wks_state;
543 proplist_t foo, bar;
544 int i;
546 make_keys();
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);
553 PLRelease(pstr);
554 if (!wPreferences.flags.noclip) {
555 pstr = wClipSaveWorkspaceState(scr, i);
556 PLInsertDictionaryEntry(wks_state, dClip, pstr);
557 PLRelease(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);
569 PLRelease(parr);
573 void
574 wWorkspaceRestoreState(WScreen *scr)
576 proplist_t parr, pstr, wks_state;
577 proplist_t clip_state;
578 int i, wscount;
580 make_keys();
582 parr = PLGetDictionaryEntry(scr->session_state, dWorkspaces);
584 if (!parr)
585 return;
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);
592 else
593 pstr = wks_state;
594 if (i >= scr->workspace_count)
595 wWorkspaceNew(scr);
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,
608 WM_CLIP);
609 if (i>0)
610 wDockHideIcons(scr->workspaces[i]->clip);