0.51.1 pre snapshot. Be careful, it may be buggy. It fixes some bugs though.
[wmaker-crm.git] / src / workspace.c
blob78eb7628635f9e2a751e3b55254c39ddb2974bbf
1 /* workspace.c- Workspace management
2 *
3 * Window Maker 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"
44 #ifdef GNOME_STUFF
45 #include "gnome.h"
46 #endif
47 #ifdef KWM_HINTS
48 #include "kwm.h"
49 #endif
51 #include <proplist.h>
54 extern WPreferences wPreferences;
55 extern XContext wWinContext;
58 static proplist_t dWorkspaces=NULL;
59 static proplist_t dClip, dName;
62 static void
63 make_keys()
65 if (dWorkspaces!=NULL)
66 return;
68 dWorkspaces = PLMakeString("Workspaces");
69 dName = PLMakeString("Name");
70 dClip = PLMakeString("Clip");
74 void
75 wWorkspaceMake(WScreen *scr, int count)
77 while (count>0) {
78 wWorkspaceNew(scr);
79 count--;
84 int
85 wWorkspaceNew(WScreen *scr)
87 WWorkspace *wspace, **list;
88 int i;
90 if (scr->workspace_count < MAX_WORKSPACES) {
91 scr->workspace_count++;
93 wspace = wmalloc(sizeof(WWorkspace));
94 wspace->name = NULL;
96 #ifdef KWM_HINTS
97 if (scr->flags.kwm_syncing_count) {
98 wspace->name = wKWMGetWorkspaceName(scr, scr->workspace_count-1);
100 #endif
101 if (!wspace->name) {
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);
109 } else
110 wspace->clip = NULL;
112 list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
114 for (i=0; i<scr->workspace_count-1; i++) {
115 list[i] = scr->workspaces[i];
117 list[i] = wspace;
118 free(scr->workspaces);
119 scr->workspaces = list;
121 wWorkspaceMenuUpdate(scr, scr->workspace_menu);
122 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
123 #ifdef GNOME_STUFF
124 wGNOMEUpdateWorkspaceHints(scr);
125 #endif
126 #ifdef KWM_HINTS
127 if (!scr->flags.kwm_syncing_count) {
128 wKWMUpdateWorkspaceCountHint(scr);
129 wKWMUpdateWorkspaceNameHint(scr, scr->workspace_count-1);
131 #ifdef not_used
132 wKWMSetUsableAreaHint(scr, scr->workspace_count-1);
133 #endif
134 #endif
135 XFlush(dpy);
137 return scr->workspace_count-1;
139 return -1;
144 Bool
145 wWorkspaceDelete(WScreen *scr, int workspace)
147 WWindow *tmp;
148 WWorkspace **list;
149 int i, j;
152 if (workspace<=0)
153 return False;
155 /* verify if workspace is in use by some window */
156 tmp = scr->focused_window;
157 while (tmp) {
158 if (!IS_OMNIPRESENT(tmp) && tmp->frame->workspace==workspace)
159 return False;
160 tmp = tmp->prev;
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));
169 j = 0;
170 for (i=0; i<scr->workspace_count; i++) {
171 if (i!=workspace)
172 list[j++] = scr->workspaces[i];
173 else {
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--;
185 /* update menu */
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;
194 i = menu->entry_no;
195 while (i>scr->workspace_count)
196 wMenuRemoveItem(menu, --i);
197 wMenuRealize(menu);
199 /* and clip menu */
200 if (scr->clip_submenu) {
201 WMenu *menu = scr->clip_submenu;
203 i = menu->entry_no;
204 while (i>scr->workspace_count)
205 wMenuRemoveItem(menu, --i);
206 wMenuRealize(menu);
209 #ifdef GNOME_STUFF
210 wGNOMEUpdateWorkspaceHints(scr);
211 #endif
212 #ifdef KWM_HINTS
213 wKWMUpdateWorkspaceCountHint(scr);
214 #endif
216 if (scr->current_workspace >= scr->workspace_count)
217 wWorkspaceChange(scr, scr->workspace_count-1);
219 return True;
223 void
224 wWorkspaceChange(WScreen *scr, int workspace)
226 if (scr->flags.startup || scr->flags.startup2) {
227 return;
230 if (workspace != scr->current_workspace) {
231 wWorkspaceForceChange(scr, workspace);
236 void
237 wWorkspaceRelativeChange(WScreen *scr, int amount)
239 int w;
241 w = scr->current_workspace + amount;
243 if (amount < 0) {
245 if (w >= 0)
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);
263 void
264 wWorkspaceForceChange(WScreen *scr, int workspace)
266 WWindow *tmp, *foc=NULL, *foc2=NULL;
268 if (workspace >= MAX_WORKSPACES || workspace < 0)
269 return;
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)
287 foc = tmp;
289 while (tmp) {
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) {
296 wWindowUnmap(tmp);
298 /* also unmap miniwindows not on this workspace */
299 if (tmp->flags.miniaturized && !IS_OMNIPRESENT(tmp)
300 && tmp->icon) {
301 if (!wPreferences.sticky_icons) {
302 XUnmapWindow(dpy, tmp->icon->core->window);
303 tmp->icon->mapped = 0;
304 } else {
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;
316 if (wapp) {
317 wapp->last_workspace = workspace;
319 if (!foc2)
320 foc2 = tmp;
322 } else {
323 /* change selected windows' workspace */
324 if (tmp->flags.selected) {
325 wWindowChangeWorkspace(tmp, workspace);
326 if (!tmp->flags.miniaturized && !foc) {
327 foc = tmp;
329 } else {
330 if (!tmp->flags.hidden) {
331 if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
332 /* remap windows that are on this workspace */
333 wWindowMap(tmp);
334 if (!foc)
335 foc = tmp;
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);
347 tmp = tmp->prev;
350 if (!foc)
351 foc = foc2;
353 if (scr->focused_window->flags.mapped && !foc) {
354 foc = scr->focused_window;
356 if (wPreferences.focus_mode == WKF_CLICK) {
357 wSetFocusTo(scr, foc);
358 } else {
359 unsigned int mask;
360 int foo;
361 Window bar, win;
362 WWindow *tmp;
364 tmp = NULL;
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);
371 } else {
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
379 * can be superposed.
380 * This can be avoided if appicons are also workspace specific.
382 if (!wPreferences.sticky_icons)
383 wArrangeIcons(scr, False);
385 if (scr->dock)
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);
393 } else {
394 wClipIconPaint(scr->clip_icon);
398 #ifdef GNOME_STUFF
399 wGNOMEUpdateCurrentWorkspaceHint(scr);
400 #endif
401 #ifdef KWM_HINTS
402 wKWMUpdateCurrentWorkspaceHint(scr);
403 #endif
404 /* XSync(dpy, False); */
408 static void
409 switchWSCommand(WMenu *menu, WMenuEntry *entry)
411 wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
416 static void
417 deleteWSCommand(WMenu *menu, WMenuEntry *entry)
419 wWorkspaceDelete(menu->frame->screen_ptr,
420 menu->frame->screen_ptr->workspace_count-1);
425 static void
426 newWSCommand(WMenu *menu, WMenuEntry *foo)
428 int ws;
430 ws = wWorkspaceNew(menu->frame->screen_ptr);
431 /* autochange workspace*/
432 if (ws>=0)
433 wWorkspaceChange(menu->frame->screen_ptr, ws);
437 if (ws<9) {
438 int kcode;
439 if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
440 kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
441 entry->rtext =
442 wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
448 static char*
449 cropline(char *line)
451 char *start, *end;
453 if (strlen(line)==0)
454 return line;
456 start = line;
457 end = &(line[strlen(line)])-1;
458 while (isspace(*line) && *line!=0) line++;
459 while (isspace(*end) && end!=line) {
460 *end=0;
461 end--;
463 return line;
467 void
468 wWorkspaceRename(WScreen *scr, int workspace, char *name)
470 char buf[MAX_WORKSPACENAME_WIDTH+1];
471 char *tmp;
473 if (workspace >= scr->workspace_count)
474 return;
476 /* trim white spaces */
477 tmp = cropline(name);
479 if (strlen(tmp)==0) {
480 sprintf(buf, _("Workspace %i"), workspace+1);
481 } else {
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);
507 if (scr->clip_icon)
508 wClipIconPaint(scr->clip_icon);
510 #ifdef GNOME_STUFF
511 wGNOMEUpdateWorkspaceNamesHint(scr);
512 #endif
513 #ifdef KWM_HINTS
514 wKWMUpdateWorkspaceNameHint(scr, workspace);
515 #endif
521 /* callback for when menu entry is edited */
522 static void
523 onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
525 char *tmp;
527 tmp = entry->text;
528 wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
532 WMenu*
533 wWorkspaceMenuMake(WScreen *scr, Bool titled)
535 WMenu *wsmenu;
537 wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
538 if (!wsmenu) {
539 wwarning(_("could not create Workspace menu"));
540 return NULL;
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);
549 return wsmenu;
554 void
555 wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
557 int i;
558 long ws;
559 char title[MAX_WORKSPACENAME_WIDTH+1];
560 WMenuEntry *entry;
561 int tmp;
563 if (!menu)
564 return;
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;
570 while (i>0) {
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;
577 i--;
578 ws++;
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);
586 wMenuRealize(menu);
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);
596 } else {
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);
605 wMenuPaint(menu);
609 void
610 wWorkspaceSaveState(WScreen *scr, proplist_t old_state)
612 proplist_t parr, pstr;
613 proplist_t wks_state, old_wks_state;
614 proplist_t foo, bar;
615 int i;
617 make_keys();
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);
624 PLRelease(pstr);
625 if (!wPreferences.flags.noclip) {
626 pstr = wClipSaveWorkspaceState(scr, i);
627 PLInsertDictionaryEntry(wks_state, dClip, pstr);
628 PLRelease(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);
640 PLRelease(parr);
644 void
645 wWorkspaceRestoreState(WScreen *scr)
647 proplist_t parr, pstr, wks_state;
648 proplist_t clip_state;
649 int i, wscount;
651 make_keys();
653 parr = PLGetDictionaryEntry(scr->session_state, dWorkspaces);
655 if (!parr)
656 return;
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);
663 else
664 pstr = wks_state;
665 if (i >= scr->workspace_count)
666 wWorkspaceNew(scr);
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,
679 WM_CLIP);
680 if (i>0)
681 wDockHideIcons(scr->workspaces[i]->clip);
683 #ifdef KWM_HINTS
684 wKWMUpdateWorkspaceNameHint(scr, i);
685 #endif
687 #ifdef GNOME_STUFF
688 wGNOMEUpdateWorkspaceNamesHint(scr);
689 #endif