~profzoom/ubuntu/quantal/wmaker/bug-1079925

« back to all changes in this revision

Viewing changes to src/workspace.c

  • Committer: Bazaar Package Importer
  • Author(s): Marcelo E. Magallon
  • Date: 2004-11-10 14:05:30 UTC
  • Revision ID: james.westby@ubuntu.com-20041110140530-qpd66b5lm38x7apk
Tags: upstream-0.91.0
ImportĀ upstreamĀ versionĀ 0.91.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* workspace.c- Workspace management
 
2
 *
 
3
 *  Window Maker window manager
 
4
 *
 
5
 *  Copyright (c) 1997-2003 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.
 
11
 *
 
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.
 
16
 *
 
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.
 
21
 */
 
22
#include "wconfig.h"
 
23
 
 
24
#include <X11/Xlib.h>
 
25
#include <X11/Xutil.h>
 
26
#ifdef SHAPE
 
27
#include <X11/extensions/shape.h>
 
28
#endif
 
29
 
 
30
#include <stdlib.h>
 
31
#include <stdio.h>
 
32
#include <unistd.h>
 
33
#include <ctype.h>
 
34
#include <string.h>
 
35
#include <time.h>
 
36
#include <sys/time.h>
 
37
 
 
38
#include "WindowMaker.h"
 
39
#include "wcore.h"
 
40
#include "framewin.h"
 
41
#include "window.h"
 
42
#include "icon.h"
 
43
#include "funcs.h"
 
44
#include "menu.h"
 
45
#include "application.h"
 
46
#include "dock.h"
 
47
#include "actions.h"
 
48
#include "workspace.h"
 
49
#include "appicon.h"
 
50
#ifdef NETWM_HINTS
 
51
#include "wmspec.h"
 
52
#endif
 
53
 
 
54
#include "xinerama.h"
 
55
 
 
56
 
 
57
extern WPreferences wPreferences;
 
58
extern XContext wWinContext;
 
59
extern XContext wVEdgeContext;
 
60
 
 
61
extern void ProcessPendingEvents();
 
62
 
 
63
static WMPropList *dWorkspaces=NULL;
 
64
static WMPropList *dClip, *dName;
 
65
 
 
66
 
 
67
 
 
68
static void
 
69
make_keys()
 
70
{
 
71
    if (dWorkspaces!=NULL)
 
72
        return;
 
73
 
 
74
    dWorkspaces = WMCreatePLString("Workspaces");
 
75
    dName = WMCreatePLString("Name");
 
76
    dClip = WMCreatePLString("Clip");
 
77
}
 
78
 
 
79
 
 
80
void
 
81
wWorkspaceMake(WScreen *scr, int count)
 
82
{
 
83
    while (count>0) {
 
84
        wWorkspaceNew(scr);
 
85
        count--;
 
86
    }
 
87
}
 
88
 
 
89
 
 
90
int
 
91
wWorkspaceNew(WScreen *scr)
 
92
{
 
93
    WWorkspace *wspace, **list;
 
94
    int i;
 
95
 
 
96
    if (scr->workspace_count < MAX_WORKSPACES) {
 
97
        scr->workspace_count++;
 
98
 
 
99
        wspace = wmalloc(sizeof(WWorkspace));
 
100
        wspace->name = NULL;
 
101
 
 
102
        if (!wspace->name) {
 
103
            wspace->name = wmalloc(strlen(_("Workspace %i"))+8);
 
104
            sprintf(wspace->name, _("Workspace %i"), scr->workspace_count);
 
105
        }
 
106
 
 
107
 
 
108
        if (!wPreferences.flags.noclip) {
 
109
            wspace->clip = wDockCreate(scr, WM_CLIP);
 
110
        } else
 
111
            wspace->clip = NULL;
 
112
 
 
113
        list = wmalloc(sizeof(WWorkspace*)*scr->workspace_count);
 
114
 
 
115
        for (i=0; i<scr->workspace_count-1; i++) {
 
116
            list[i] = scr->workspaces[i];
 
117
        }
 
118
        list[i] = wspace;
 
119
        if (scr->workspaces)
 
120
            wfree(scr->workspaces);
 
121
        scr->workspaces = list;
 
122
 
 
123
        wWorkspaceMenuUpdate(scr, scr->workspace_menu);
 
124
        wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
 
125
#ifdef VIRTUAL_DESKTOP
 
126
        wspace->view_x = wspace->view_y = 0;
 
127
        wspace->height = scr->scr_height;
 
128
        wspace->width = scr->scr_width;
 
129
#endif
 
130
#ifdef NETWM_HINTS
 
131
        wNETWMUpdateDesktop(scr);
 
132
#endif
 
133
 
 
134
        WMPostNotificationName(WMNWorkspaceCreated, scr,
 
135
                               (void*)(scr->workspace_count-1));
 
136
        XFlush(dpy);
 
137
 
 
138
        return scr->workspace_count-1;
 
139
    }
 
140
 
 
141
    return -1;
 
142
}
 
143
 
 
144
 
 
145
Bool
 
146
wWorkspaceDelete(WScreen *scr, int workspace)
 
147
{
 
148
    WWindow *tmp;
 
149
    WWorkspace **list;
 
150
    int i, j;
 
151
 
 
152
    if (workspace<=0)
 
153
        return False;
 
154
 
 
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;
 
161
    }
 
162
 
 
163
    if (!wPreferences.flags.noclip) {
 
164
        wDockDestroy(scr->workspaces[workspace]->clip);
 
165
        scr->workspaces[workspace]->clip = NULL;
 
166
    }
 
167
 
 
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
                wfree(scr->workspaces[i]->name);
 
176
            wfree(scr->workspaces[i]);
 
177
        }
 
178
    }
 
179
    wfree(scr->workspaces);
 
180
    scr->workspaces = list;
 
181
 
 
182
    scr->workspace_count--;
 
183
 
 
184
 
 
185
    /* update menu */
 
186
    wWorkspaceMenuUpdate(scr, scr->workspace_menu);
 
187
    /* clip workspace menu */
 
188
    wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
 
189
 
 
190
    /* update also window menu */
 
191
    if (scr->workspace_submenu) {
 
192
        WMenu *menu = scr->workspace_submenu;
 
193
 
 
194
        i = menu->entry_no;
 
195
        while (i>scr->workspace_count)
 
196
            wMenuRemoveItem(menu, --i);
 
197
        wMenuRealize(menu);
 
198
    }
 
199
    /* and clip menu */
 
200
    if (scr->clip_submenu) {
 
201
        WMenu *menu = scr->clip_submenu;
 
202
 
 
203
        i = menu->entry_no;
 
204
        while (i>scr->workspace_count)
 
205
            wMenuRemoveItem(menu, --i);
 
206
        wMenuRealize(menu);
 
207
    }
 
208
 
 
209
#ifdef NETWM_HINTS
 
210
    wNETWMUpdateDesktop(scr);
 
211
#endif
 
212
 
 
213
    WMPostNotificationName(WMNWorkspaceDestroyed, scr,
 
214
                           (void*)(scr->workspace_count-1));
 
215
 
 
216
    if (scr->current_workspace >= scr->workspace_count)
 
217
        wWorkspaceChange(scr, scr->workspace_count-1);
 
218
 
 
219
    return True;
 
220
}
 
221
 
 
222
 
 
223
typedef struct WorkspaceNameData {
 
224
    int count;
 
225
    RImage *back;
 
226
    RImage *text;
 
227
    time_t timeout;
 
228
} WorkspaceNameData;
 
229
 
 
230
 
 
231
static void
 
232
hideWorkspaceName(void *data)
 
233
{
 
234
    WScreen *scr = (WScreen*)data;
 
235
 
 
236
    if (!scr->workspace_name_data || scr->workspace_name_data->count == 0
 
237
        || time(NULL) > scr->workspace_name_data->timeout) {
 
238
        XUnmapWindow(dpy, scr->workspace_name);
 
239
 
 
240
        if (scr->workspace_name_data) {
 
241
            RReleaseImage(scr->workspace_name_data->back);
 
242
            RReleaseImage(scr->workspace_name_data->text);
 
243
            wfree(scr->workspace_name_data);
 
244
 
 
245
            scr->workspace_name_data = NULL;
 
246
        }
 
247
        scr->workspace_name_timer = NULL;
 
248
    } else {
 
249
        RImage *img = RCloneImage(scr->workspace_name_data->back);
 
250
        Pixmap pix;
 
251
 
 
252
        scr->workspace_name_timer =
 
253
            WMAddTimerHandler(WORKSPACE_NAME_FADE_DELAY, hideWorkspaceName, scr);
 
254
 
 
255
        RCombineImagesWithOpaqueness(img, scr->workspace_name_data->text,
 
256
                                     scr->workspace_name_data->count*255/10);
 
257
 
 
258
        RConvertImage(scr->rcontext, img, &pix);
 
259
 
 
260
        RReleaseImage(img);
 
261
 
 
262
        XSetWindowBackgroundPixmap(dpy, scr->workspace_name, pix);
 
263
        XClearWindow(dpy, scr->workspace_name);
 
264
        XFreePixmap(dpy, pix);
 
265
        XFlush(dpy);
 
266
 
 
267
        scr->workspace_name_data->count--;
 
268
    }
 
269
}
 
270
 
 
271
 
 
272
static void
 
273
showWorkspaceName(WScreen *scr, int workspace)
 
274
{
 
275
    WorkspaceNameData *data;
 
276
    RXImage *ximg;
 
277
    Pixmap text, mask;
 
278
    int w, h;
 
279
    int px, py;
 
280
    char *name = scr->workspaces[workspace]->name;
 
281
    int len = strlen(name);
 
282
    int x, y;
 
283
 
 
284
    if (wPreferences.workspace_name_display_position == WD_NONE ||
 
285
        scr->workspace_count < 2) {
 
286
        return;
 
287
    }
 
288
 
 
289
    if (scr->workspace_name_timer) {
 
290
        WMDeleteTimerHandler(scr->workspace_name_timer);
 
291
        XUnmapWindow(dpy, scr->workspace_name);
 
292
        XFlush(dpy);
 
293
    }
 
294
    scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY,
 
295
                                                  hideWorkspaceName, scr);
 
296
 
 
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);
 
301
    }
 
302
 
 
303
    data = wmalloc(sizeof(WorkspaceNameData));
 
304
    data->back = NULL;
 
305
 
 
306
    w = WMWidthOfString(scr->workspace_name_font, name, len);
 
307
    h = WMFontHeight(scr->workspace_name_font);
 
308
 
 
309
    switch (wPreferences.workspace_name_display_position) {
 
310
    case WD_TOP:
 
311
        px = (scr->scr_width - (w+4))/2;
 
312
        py = 0;
 
313
        break;
 
314
    case WD_BOTTOM:
 
315
        px = (scr->scr_width - (w+4))/2;
 
316
        py = scr->scr_height - (h+4);
 
317
        break;
 
318
    case WD_TOPLEFT:
 
319
        px = 0;
 
320
        py = 0;
 
321
        break;
 
322
    case WD_TOPRIGHT:
 
323
        px = scr->scr_width - (w+4);
 
324
        py = 0;
 
325
        break;
 
326
    case WD_BOTTOMLEFT:
 
327
        px = 0;
 
328
        py = scr->scr_height - (h+4);
 
329
        break;
 
330
    case WD_BOTTOMRIGHT:
 
331
        px = scr->scr_width - (w+4);
 
332
        py = scr->scr_height - (h+4);
 
333
        break;
 
334
    case WD_CENTER:
 
335
    default:
 
336
        px = (scr->scr_width - (w+4))/2;
 
337
        py = (scr->scr_height - (h+4))/2;
 
338
        break;
 
339
    }
 
340
    XResizeWindow(dpy, scr->workspace_name, w+4, h+4);
 
341
    XMoveWindow(dpy, scr->workspace_name, px, py);
 
342
 
 
343
    text = XCreatePixmap(dpy, scr->w_win, w+4, h+4, scr->w_depth);
 
344
    mask = XCreatePixmap(dpy, scr->w_win, w+4, h+4, 1);
 
345
 
 
346
    /*XSetForeground(dpy, scr->mono_gc, 0);
 
347
     XFillRectangle(dpy, mask, scr->mono_gc, 0, 0, w+4, h+4);*/
 
348
 
 
349
    XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w+4, h+4);
 
350
 
 
351
    for (x = 0; x <= 4; x++) {
 
352
        for (y = 0; y <= 4; y++) {
 
353
            WMDrawString(scr->wmscreen, text, scr->white,
 
354
                         scr->workspace_name_font, x, y, name, len);
 
355
        }
 
356
    }
 
357
 
 
358
    XSetForeground(dpy, scr->mono_gc, 1);
 
359
    XSetBackground(dpy, scr->mono_gc, 0);
 
360
 
 
361
    XCopyPlane(dpy, text, mask, scr->mono_gc, 0, 0, w+4, h+4, 0, 0, 1<<(scr->w_depth-1));
 
362
 
 
363
    /*XSetForeground(dpy, scr->mono_gc, 1);*/
 
364
    XSetBackground(dpy, scr->mono_gc, 1);
 
365
 
 
366
    XFillRectangle(dpy, text, WMColorGC(scr->black), 0, 0, w+4, h+4);
 
367
 
 
368
    WMDrawString(scr->wmscreen, text, scr->white, scr->workspace_name_font,
 
369
                 2, 2, name, len);
 
370
 
 
371
#ifdef SHAPE
 
372
    XShapeCombineMask(dpy, scr->workspace_name, ShapeBounding, 0, 0, mask,
 
373
                      ShapeSet);
 
374
#endif
 
375
    XSetWindowBackgroundPixmap(dpy, scr->workspace_name, text);
 
376
    XClearWindow(dpy, scr->workspace_name);
 
377
 
 
378
    data->text = RCreateImageFromDrawable(scr->rcontext, text, None);
 
379
 
 
380
    XFreePixmap(dpy, text);
 
381
    XFreePixmap(dpy, mask);
 
382
 
 
383
    if (!data->text) {
 
384
        XMapRaised(dpy, scr->workspace_name);
 
385
        XFlush(dpy);
 
386
 
 
387
        goto erro;
 
388
    }
 
389
 
 
390
    ximg = RGetXImage(scr->rcontext, scr->root_win, px, py,
 
391
                      data->text->width, data->text->height);
 
392
 
 
393
    if (!ximg || !ximg->image) {
 
394
        goto erro;
 
395
    }
 
396
 
 
397
    XMapRaised(dpy, scr->workspace_name);
 
398
    XFlush(dpy);
 
399
 
 
400
    data->back = RCreateImageFromXImage(scr->rcontext, ximg->image, NULL);
 
401
    RDestroyXImage(scr->rcontext, ximg);
 
402
 
 
403
    if (!data->back) {
 
404
        goto erro;
 
405
    }
 
406
 
 
407
    data->count = 10;
 
408
 
 
409
    /* set a timeout for the effect */
 
410
    data->timeout = time(NULL) + 2 +
 
411
        (WORKSPACE_NAME_DELAY + WORKSPACE_NAME_FADE_DELAY*data->count)/1000;
 
412
 
 
413
    scr->workspace_name_data = data;
 
414
 
 
415
 
 
416
    return;
 
417
 
 
418
erro:
 
419
    if (scr->workspace_name_timer)
 
420
        WMDeleteTimerHandler(scr->workspace_name_timer);
 
421
 
 
422
    if (data->text)
 
423
        RReleaseImage(data->text);
 
424
    if (data->back)
 
425
        RReleaseImage(data->back);
 
426
    wfree(data);
 
427
 
 
428
    scr->workspace_name_data = NULL;
 
429
 
 
430
    scr->workspace_name_timer = WMAddTimerHandler(WORKSPACE_NAME_DELAY +
 
431
                                                  10*WORKSPACE_NAME_FADE_DELAY,
 
432
                                                  hideWorkspaceName, scr);
 
433
}
 
434
 
 
435
 
 
436
void
 
437
wWorkspaceChange(WScreen *scr, int workspace)
 
438
{
 
439
    if (scr->flags.startup || scr->flags.startup2) {
 
440
        return;
 
441
    }
 
442
 
 
443
    if (workspace != scr->current_workspace) {
 
444
        wWorkspaceForceChange(scr, workspace);
 
445
    } /*else {
 
446
    showWorkspaceName(scr, workspace);
 
447
    }*/
 
448
}
 
449
 
 
450
 
 
451
void
 
452
wWorkspaceRelativeChange(WScreen *scr, int amount)
 
453
{
 
454
    int w;
 
455
 
 
456
    w = scr->current_workspace + amount;
 
457
 
 
458
    if (amount < 0) {
 
459
        if (w >= 0) {
 
460
            wWorkspaceChange(scr, w);
 
461
        } else if (wPreferences.ws_cycle) {
 
462
            wWorkspaceChange(scr, scr->workspace_count + w);
 
463
        }
 
464
    } else if (amount > 0) {
 
465
        if (w < scr->workspace_count) {
 
466
            wWorkspaceChange(scr, w);
 
467
        } else if (wPreferences.ws_advance) {
 
468
            wWorkspaceChange(scr, WMIN(w, MAX_WORKSPACES-1));
 
469
        } else if (wPreferences.ws_cycle) {
 
470
            wWorkspaceChange(scr, w % scr->workspace_count);
 
471
        }
 
472
    }
 
473
}
 
474
 
 
475
 
 
476
void
 
477
wWorkspaceForceChange(WScreen *scr, int workspace)
 
478
{
 
479
    WWindow *tmp, *foc=NULL, *foc2=NULL;
 
480
 
 
481
    if (workspace >= MAX_WORKSPACES || workspace < 0)
 
482
        return;
 
483
 
 
484
    SendHelperMessage(scr, 'C', workspace+1, NULL);
 
485
 
 
486
    if (workspace > scr->workspace_count-1) {
 
487
        wWorkspaceMake(scr, workspace - scr->workspace_count + 1);
 
488
    }
 
489
 
 
490
    wClipUpdateForWorkspaceChange(scr, workspace);
 
491
 
 
492
    scr->current_workspace = workspace;
 
493
 
 
494
    wWorkspaceMenuUpdate(scr, scr->workspace_menu);
 
495
 
 
496
    wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
 
497
 
 
498
    if ((tmp = scr->focused_window)!= NULL) {
 
499
        if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded) &&
 
500
             !WFLAGP(tmp, no_focusable)) || tmp->flags.changing_workspace) {
 
501
            foc = tmp;
 
502
        }
 
503
 
 
504
        /* foc2 = tmp; will fix annoyance with gnome panel
 
505
         * but will create annoyance for every other application
 
506
         */
 
507
        while (tmp) {
 
508
            if (tmp->frame->workspace!=workspace && !tmp->flags.selected) {
 
509
                /* unmap windows not on this workspace */
 
510
                if ((tmp->flags.mapped||tmp->flags.shaded) &&
 
511
                    !IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) {
 
512
                    wWindowUnmap(tmp);
 
513
                }
 
514
                /* also unmap miniwindows not on this workspace */
 
515
                if (!wPreferences.sticky_icons && tmp->flags.miniaturized &&
 
516
                    tmp->icon && !IS_OMNIPRESENT(tmp)) {
 
517
                    XUnmapWindow(dpy, tmp->icon->core->window);
 
518
                    tmp->icon->mapped = 0;
 
519
                }
 
520
                /* update current workspace of omnipresent windows */
 
521
                if (IS_OMNIPRESENT(tmp)) {
 
522
                    WApplication *wapp = wApplicationOf(tmp->main_window);
 
523
 
 
524
                    tmp->frame->workspace = workspace;
 
525
 
 
526
                    if (wapp) {
 
527
                        wapp->last_workspace = workspace;
 
528
                    }
 
529
                    if (!foc2 && (tmp->flags.mapped || tmp->flags.shaded)) {
 
530
                        foc2 = tmp;
 
531
                    }
 
532
                }
 
533
            } else {
 
534
                /* change selected windows' workspace */
 
535
                if (tmp->flags.selected) {
 
536
                    wWindowChangeWorkspace(tmp, workspace);
 
537
                    if (!tmp->flags.miniaturized && !foc) {
 
538
                        foc = tmp;
 
539
                    }
 
540
                } else {
 
541
                    if (!tmp->flags.hidden) {
 
542
                        if (!(tmp->flags.mapped || tmp->flags.miniaturized)) {
 
543
                            /* remap windows that are on this workspace */
 
544
                            wWindowMap(tmp);
 
545
                            if (!foc && !WFLAGP(tmp, no_focusable)) {
 
546
                                foc = tmp;
 
547
                            }
 
548
                        }
 
549
                        /* Also map miniwindow if not omnipresent */
 
550
                        if (!wPreferences.sticky_icons &&
 
551
                            tmp->flags.miniaturized &&
 
552
                            !IS_OMNIPRESENT(tmp) && tmp->icon) {
 
553
                            tmp->icon->mapped = 1;
 
554
                            XMapWindow(dpy, tmp->icon->core->window);
 
555
                        }
 
556
                    }
 
557
                }
 
558
            }
 
559
            tmp = tmp->prev;
 
560
        }
 
561
 
 
562
        /* Gobble up events unleashed by our mapping & unmapping.
 
563
         * These may trigger various grab-initiated focus &
 
564
         * crossing events. However, we don't care about them,
 
565
         * and ignore their focus implications altogether to avoid
 
566
         * flicker.
 
567
         */
 
568
        scr->flags.ignore_focus_events = 1;
 
569
        ProcessPendingEvents();
 
570
        scr->flags.ignore_focus_events = 0;
 
571
 
 
572
        if (!foc)
 
573
            foc = foc2;
 
574
 
 
575
        if (scr->focused_window->flags.mapped && !foc) {
 
576
            foc = scr->focused_window;
 
577
        }
 
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;
 
585
 
 
586
            tmp = NULL;
 
587
            if (XQueryPointer(dpy, scr->root_win, &bar, &win,
 
588
                              &foo, &foo, &foo, &foo, &mask)) {
 
589
                tmp = wWindowFor(win);
 
590
            }
 
591
 
 
592
            /* If there's a window under the pointer, focus it.
 
593
             * (we ate all other focus events above, so it's
 
594
             * certainly not focused). Otherwise focus last
 
595
             * focused, or the root (depending on sloppiness)
 
596
             */
 
597
            if (!tmp && wPreferences.focus_mode == WKF_SLOPPY) {
 
598
                wSetFocusTo(scr, foc);
 
599
            } else {
 
600
                wSetFocusTo(scr, tmp);
 
601
            }
 
602
        }
 
603
    }
 
604
 
 
605
    /* We need to always arrange icons when changing workspace, even if
 
606
     * no autoarrange icons, because else the icons in different workspaces
 
607
     * can be superposed.
 
608
     * This can be avoided if appicons are also workspace specific.
 
609
     */
 
610
    if (!wPreferences.sticky_icons)
 
611
        wArrangeIcons(scr, False);
 
612
 
 
613
    if (scr->dock)
 
614
        wAppIconPaint(scr->dock->icon_array[0]);
 
615
 
 
616
    if (scr->clip_icon) {
 
617
        if (scr->workspaces[workspace]->clip->auto_collapse ||
 
618
            scr->workspaces[workspace]->clip->auto_raise_lower) {
 
619
            /* to handle enter notify. This will also */
 
620
            XUnmapWindow(dpy, scr->clip_icon->icon->core->window);
 
621
            XMapWindow(dpy, scr->clip_icon->icon->core->window);
 
622
        } else {
 
623
            wClipIconPaint(scr->clip_icon);
 
624
        }
 
625
    }
 
626
 
 
627
#ifdef NETWM_HINTS
 
628
    wScreenUpdateUsableArea(scr);
 
629
    wNETWMUpdateDesktop(scr);
 
630
#endif
 
631
 
 
632
    showWorkspaceName(scr, workspace);
 
633
 
 
634
    WMPostNotificationName(WMNWorkspaceChanged, scr, (void*)workspace);
 
635
 
 
636
    /*   XSync(dpy, False); */
 
637
}
 
638
 
 
639
 
 
640
#ifdef VIRTUAL_DESKTOP
 
641
 
 
642
/* TODO:
 
643
 *
 
644
 * 1) Allow border around each window so the scrolling
 
645
 *    won't just stop at the border.
 
646
 * 2) Make pager.
 
647
 *
 
648
 */
 
649
 
 
650
#define vec_sub(a, b) wmkpoint((a).x-(b).x, (a).y-(b).y)
 
651
#define vec_add(a, b) wmkpoint((a).x+(b).x, (a).y+(b).y)
 
652
#define vec_inc(a, b) do { (a).x+=(b).x; (a).y+=(b).y; } while(0)
 
653
#define vec_dot(a, b) ((a).x*(b).x + (a).y*(b).y)
 
654
#define vec_scale(a, s) wmkpoint((a).x*s, (a).y*s)
 
655
#define vec_scale2(a, s, t) wmkpoint((a).x*s, (a).y*t)
 
656
 
 
657
#ifndef HAS_BORDER
 
658
#define HAS_BORDER(w) (!(WFLAGP((w), no_border)))
 
659
#endif
 
660
 
 
661
#ifndef IS_VSTUCK
 
662
#define IS_VSTUCK(w) (WFLAGP((w), virtual_stick))
 
663
#endif
 
664
 
 
665
#define updateMinimum(l,p,ml,mp) do { if (cmp(l) && (l)<(ml)) { (ml)=(l); (mp)=(p); }; } while(0)
 
666
 
 
667
static Bool cmp_gez(int i) { return (i >= 0); }
 
668
 
 
669
static Bool cmp_gz(int i)  { return (i > 0); }
 
670
 
 
671
 
 
672
static WMPoint
 
673
getClosestEdge(WScreen * scr, WMPoint direction, Bool (*cmp)(int))
 
674
{
 
675
    WMPoint closest = wmkpoint(0, 0);
 
676
    int closest_len = INT_MAX;
 
677
    WWindow * wwin;
 
678
 
 
679
    for (wwin=scr->focused_window; wwin; wwin=wwin->prev) {
 
680
        if (wwin->frame->workspace == scr->current_workspace) {
 
681
            if (!wwin->flags.miniaturized &&
 
682
                !IS_VSTUCK(wwin) &&
 
683
                !wwin->flags.hidden) {
 
684
                int border = 2*HAS_BORDER(wwin);
 
685
                int len;
 
686
                int x1,x2,y1,y2;
 
687
                int head = wGetHeadForWindow(wwin);
 
688
                WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
 
689
                WMPoint p;
 
690
 
 
691
                x1 = wwin->frame_x - area.x1;
 
692
                y1 = wwin->frame_y - area.y1;
 
693
                x2 = wwin->frame_x + wwin->frame->core->width + border - area.x2;
 
694
                y2 = wwin->frame_y + wwin->frame->core->height + border - area.y2;
 
695
 
 
696
                p = wmkpoint(x1,y1);
 
697
                len = vec_dot(direction, p);
 
698
                updateMinimum(len, p, closest_len, closest);
 
699
 
 
700
                p = wmkpoint(x1,y2);
 
701
                len = vec_dot(direction, p);
 
702
                updateMinimum(len, p, closest_len, closest);
 
703
 
 
704
                p = wmkpoint(x2,y1);
 
705
                len = vec_dot(direction, p);
 
706
                updateMinimum(len, p, closest_len, closest);
 
707
 
 
708
                p = wmkpoint(x2,y2);
 
709
                len = vec_dot(direction, p);
 
710
                updateMinimum(len, p, closest_len, closest);
 
711
            }
 
712
        }
 
713
    }
 
714
 
 
715
    return closest;
 
716
}
 
717
 
 
718
 
 
719
static void
 
720
getViewPosition(WScreen *scr, int workspace, int *x, int *y)
 
721
{
 
722
    *x = scr->workspaces[workspace]->view_x;
 
723
    *y = scr->workspaces[workspace]->view_y;
 
724
}
 
725
 
 
726
 
 
727
void
 
728
wWorkspaceKeyboardMoveDesktop(WScreen *scr, WMPoint direction)
 
729
{
 
730
    int x, y;
 
731
    WMPoint edge = getClosestEdge(scr, direction, cmp_gz);
 
732
    int len = vec_dot(edge, direction);
 
733
    WMPoint step = vec_scale(direction, len);
 
734
    getViewPosition(scr, scr->current_workspace, &x, &y);
 
735
    wWorkspaceSetViewport(scr, scr->current_workspace, x+step.x, y+step.y);
 
736
}
 
737
 
 
738
 
 
739
extern Cursor wCursor[WCUR_LAST];
 
740
 
 
741
 
 
742
static void
 
743
vdMouseMoveDesktop(XEvent *event, WMPoint direction)
 
744
{
 
745
    static int lock = False;
 
746
    if (lock) return;
 
747
    lock = True;
 
748
 
 
749
    Bool done = False;
 
750
    Bool moved = True;
 
751
    WScreen *scr = wScreenForRootWindow(event->xcrossing.root);
 
752
    WMPoint old_pos = wmkpoint(event->xcrossing.x_root, event->xcrossing.y_root);
 
753
    WMPoint step;
 
754
    int x, y;
 
755
    int resisted = 0;
 
756
 
 
757
    if (XGrabPointer(dpy, event->xcrossing.window, False,
 
758
                     PointerMotionMask, GrabModeAsync, GrabModeAsync,
 
759
                     scr->root_win, wCursor[WCUR_EMPTY],
 
760
                     CurrentTime) != GrabSuccess) {
 
761
 
 
762
        /* if the grab fails, do it the old fashioned way */
 
763
        step = vec_scale2(direction, wPreferences.vedge_hscrollspeed,
 
764
                          wPreferences.vedge_vscrollspeed);
 
765
        getViewPosition(scr, scr->current_workspace, &x, &y);
 
766
        if (wWorkspaceSetViewport(scr, scr->current_workspace,
 
767
                                  x+step.x, y+step.y)) {
 
768
            step = vec_scale(direction, 2);
 
769
            XWarpPointer(dpy, None, scr->root_win, 0,0,0,0,
 
770
                         event->xcrossing.x_root - step.x,
 
771
                         event->xcrossing.y_root - step.y);
 
772
        }
 
773
        goto exit;
 
774
    }
 
775
    XSync(dpy, True);
 
776
 
 
777
    if (old_pos.x < 0)
 
778
        old_pos.x = 0;
 
779
    if (old_pos.y < 0)
 
780
        old_pos.y = 0;
 
781
    if (old_pos.x > scr->scr_width)
 
782
        old_pos.x = scr->scr_width;
 
783
    if (old_pos.y > scr->scr_height)
 
784
        old_pos.y = scr->scr_height;
 
785
 
 
786
    while (!done) {
 
787
        XEvent ev;
 
788
        if (moved) {
 
789
            XWarpPointer(dpy, None, scr->root_win, 0, 0, 0, 0,
 
790
                         scr->scr_width/2, scr->scr_height/2);
 
791
            moved = False;
 
792
        }
 
793
        WMMaskEvent(dpy, PointerMotionMask, &ev);
 
794
 
 
795
        switch (ev.type) {
 
796
        case MotionNotify:
 
797
            {
 
798
                int step_len;
 
799
                step = wmkpoint(ev.xmotion.x_root-scr->scr_width/2,
 
800
                                ev.xmotion.y_root-scr->scr_height/2);
 
801
                step_len = vec_dot(step, direction);
 
802
                if (step_len < 0) {
 
803
                    done = True;
 
804
                    break;
 
805
                }
 
806
 
 
807
                if (step_len > 0) {
 
808
                    Bool do_move = True;
 
809
                    int resist = wPreferences.vedge_resistance;
 
810
                    WMPoint closest;
 
811
                    int closest_len = INT_MAX;
 
812
                    if (resist) {
 
813
                        closest = getClosestEdge(scr, direction, cmp_gez);
 
814
                        closest_len = vec_dot(direction, closest);
 
815
                    }
 
816
 
 
817
                    if (!closest_len) {
 
818
                        resisted += step_len;
 
819
                        do_move = resisted >= resist;
 
820
                        if (do_move) {
 
821
                            closest_len = INT_MAX;
 
822
                            step_len = resisted - resist;
 
823
                            resisted = 0;
 
824
                        }
 
825
                    }
 
826
                    if (do_move) {
 
827
                        if (closest_len <= wPreferences.vedge_attraction) {
 
828
                            step = vec_scale(direction, closest_len);
 
829
                        } else {
 
830
                            step = vec_scale(direction, step_len);
 
831
                        }
 
832
 
 
833
                        getViewPosition(scr, scr->current_workspace, &x, &y);
 
834
                        wWorkspaceSetViewport(scr, scr->current_workspace,
 
835
                                              x+step.x, y+step.y);
 
836
                        moved = True;
 
837
                    }
 
838
                }
 
839
            }
 
840
            break;
 
841
        }
 
842
    }
 
843
 
 
844
    step = vec_add(old_pos, vec_scale(direction, -1));
 
845
    XWarpPointer(dpy, None, scr->root_win, 0,0,0,0, step.x, step.y);
 
846
    XUngrabPointer(dpy, CurrentTime);
 
847
 
 
848
exit:
 
849
    lock = False;
 
850
}
 
851
 
 
852
 
 
853
static void
 
854
vdHandleEnter_u(XEvent *event) {
 
855
    vdMouseMoveDesktop(event, VEC_UP);
 
856
}
 
857
 
 
858
 
 
859
static void
 
860
vdHandleEnter_d(XEvent *event) {
 
861
    vdMouseMoveDesktop(event, VEC_DOWN);
 
862
}
 
863
 
 
864
 
 
865
static void
 
866
vdHandleEnter_l(XEvent *event) {
 
867
    vdMouseMoveDesktop(event, VEC_LEFT);
 
868
}
 
869
 
 
870
 
 
871
static void
 
872
vdHandleEnter_r(XEvent *event) {
 
873
    vdMouseMoveDesktop(event, VEC_RIGHT);
 
874
}
 
875
 
 
876
 
 
877
#define LEFT_EDGE   0x01
 
878
#define RIGHT_EDGE  0x02
 
879
#define TOP_EDGE    0x04
 
880
#define BOTTOM_EDGE 0x08
 
881
#define ALL_EDGES   0x0F
 
882
 
 
883
static void
 
884
createEdges(WScreen *scr)
 
885
{
 
886
    if (!scr->virtual_edges) {
 
887
        int i, j, w;
 
888
        int vmask;
 
889
        XSetWindowAttributes attribs;
 
890
 
 
891
        int heads = wXineramaHeads(scr);
 
892
        int *hasEdges = (int*)wmalloc(sizeof(int)*heads);
 
893
 
 
894
        int thickness = 1;
 
895
        int nr_edges = 0;
 
896
        int max_edges = 4*heads;
 
897
        int head;
 
898
        Window *edges = (Window *)wmalloc(sizeof(Window)*max_edges);
 
899
 
 
900
        for (i=0; i<heads; ++i)
 
901
            hasEdges[i] = ALL_EDGES;
 
902
        for (i=0; i<heads; ++i) {
 
903
            WMRect i_rect = wGetRectForHead(scr, i);
 
904
            for (j=i+1; j<heads; ++j) {
 
905
                WMRect j_rect = wGetRectForHead(scr, j);
 
906
 
 
907
                int vlen = (WMIN(i_rect.pos.y + i_rect.size.height,
 
908
                                 j_rect.pos.y + j_rect.size.height) -
 
909
                            WMAX(i_rect.pos.y, j_rect.pos.y));
 
910
 
 
911
                int hlen = (WMIN(i_rect.pos.x + i_rect.size.width,
 
912
                                 j_rect.pos.x + j_rect.size.width) -
 
913
                            WMAX( i_rect.pos.x, j_rect.pos.x));
 
914
 
 
915
                if (vlen > 0 && hlen == 0) { /* horz alignment, vert edges touch */
 
916
                    if (i_rect.pos.x < j_rect.pos.x) { /* i left of j */
 
917
                        hasEdges[i] &= ~RIGHT_EDGE;
 
918
                        hasEdges[j] &= ~LEFT_EDGE;
 
919
                    } else { /* j left of i */
 
920
                        hasEdges[j] &= ~RIGHT_EDGE;
 
921
                        hasEdges[i] &= ~LEFT_EDGE;
 
922
                    }
 
923
                } else if (vlen == 0 && hlen > 0) { /* vert alignment, horz edges touch */
 
924
                    if (i_rect.pos.y < j_rect.pos.y) { /* i top of j */
 
925
                        hasEdges[i] &= ~BOTTOM_EDGE;
 
926
                        hasEdges[j] &= ~TOP_EDGE;
 
927
                    } else { /* j top of i */
 
928
                        hasEdges[j] &= ~BOTTOM_EDGE;
 
929
                        hasEdges[i] &= ~TOP_EDGE;
 
930
                    }
 
931
                }
 
932
            }
 
933
        }
 
934
 
 
935
        for (w = 0; w < scr->workspace_count; w++) {
 
936
            /* puts("reset workspace"); */
 
937
            wWorkspaceSetViewport(scr, w, 0, 0);
 
938
        }
 
939
 
 
940
        vmask = CWEventMask|CWOverrideRedirect;
 
941
        attribs.event_mask = (EnterWindowMask | LeaveWindowMask | VisibilityChangeMask);
 
942
        attribs.override_redirect = True;
 
943
 
 
944
        for (head=0; head<wXineramaHeads(scr); ++head) {
 
945
            WMRect rect = wGetRectForHead(scr, head);
 
946
 
 
947
            if (hasEdges[head] & TOP_EDGE) {
 
948
                edges[nr_edges] =
 
949
                    XCreateWindow(dpy, scr->root_win, rect.pos.x, rect.pos.y,
 
950
                                  rect.size.width, thickness, 0,
 
951
                                  CopyFromParent, InputOnly, CopyFromParent,
 
952
                                  vmask, &attribs);
 
953
                XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
 
954
                             (XPointer)vdHandleEnter_u);
 
955
                ++nr_edges;
 
956
            }
 
957
 
 
958
            if (hasEdges[head] & BOTTOM_EDGE) {
 
959
                edges[nr_edges] =
 
960
                    XCreateWindow(dpy, scr->root_win, rect.pos.x,
 
961
                                  rect.pos.y+rect.size.height-thickness,
 
962
                                  rect.size.width, thickness, 0,
 
963
                                  CopyFromParent, InputOnly, CopyFromParent,
 
964
                                  vmask, &attribs);
 
965
                XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
 
966
                             (XPointer)vdHandleEnter_d);
 
967
                ++nr_edges;
 
968
            }
 
969
 
 
970
            if (hasEdges[head] & LEFT_EDGE) {
 
971
                edges[nr_edges] =
 
972
                    XCreateWindow(dpy, scr->root_win, rect.pos.x, rect.pos.y,
 
973
                                  thickness, rect.pos.y+rect.size.height, 0,
 
974
                                  CopyFromParent, InputOnly, CopyFromParent,
 
975
                                  vmask, &attribs);
 
976
                XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
 
977
                             (XPointer)vdHandleEnter_l);
 
978
                ++nr_edges;
 
979
            }
 
980
 
 
981
            if (hasEdges[head] & RIGHT_EDGE) {
 
982
                edges[nr_edges] =
 
983
                    XCreateWindow(dpy, scr->root_win,
 
984
                                  rect.pos.x + rect.size.width - thickness, rect.pos.y,
 
985
                                  thickness, rect.size.height, 0,
 
986
                                  CopyFromParent, InputOnly, CopyFromParent, vmask,
 
987
                                  &attribs);
 
988
                XSaveContext(dpy, edges[nr_edges], wVEdgeContext,
 
989
                             (XPointer)vdHandleEnter_r);
 
990
                ++nr_edges;
 
991
            }
 
992
        }
 
993
 
 
994
        scr->virtual_nr_edges = nr_edges;
 
995
        scr->virtual_edges = edges;
 
996
 
 
997
        for (i=0; i<scr->virtual_nr_edges; ++i) {
 
998
            XMapWindow(dpy, scr->virtual_edges[i]);
 
999
        }
 
1000
        wWorkspaceRaiseEdge(scr);
 
1001
 
 
1002
        wfree(hasEdges);
 
1003
    }
 
1004
}
 
1005
 
 
1006
 
 
1007
static void
 
1008
destroyEdges(WScreen *scr)
 
1009
{
 
1010
    if (scr->virtual_edges) {
 
1011
        int i;
 
1012
 
 
1013
        for (i=0; i<scr->virtual_nr_edges; ++i) {
 
1014
            XDeleteContext(dpy, scr->virtual_edges[i], wVEdgeContext);
 
1015
            XUnmapWindow(dpy, scr->virtual_edges[i]);
 
1016
            XDestroyWindow(dpy, scr->virtual_edges[i]);
 
1017
        }
 
1018
 
 
1019
        wfree(scr->virtual_edges);
 
1020
        scr->virtual_edges = NULL;
 
1021
        scr->virtual_nr_edges = 0;
 
1022
    }
 
1023
}
 
1024
 
 
1025
 
 
1026
void
 
1027
wWorkspaceUpdateEdge(WScreen *scr)
 
1028
{
 
1029
    if (wPreferences.vdesk_enable) {
 
1030
        createEdges(scr);
 
1031
    } else {
 
1032
        destroyEdges(scr);
 
1033
    }
 
1034
}
 
1035
 
 
1036
 
 
1037
void
 
1038
wWorkspaceRaiseEdge(WScreen *scr)
 
1039
{
 
1040
    static int toggle = 0;
 
1041
    int i;
 
1042
 
 
1043
    if (!scr->virtual_edges)
 
1044
        return;
 
1045
 
 
1046
    if (toggle) {
 
1047
        for (i=0; i<scr->virtual_nr_edges; ++i) {
 
1048
            XRaiseWindow(dpy, scr->virtual_edges[i]);
 
1049
        }
 
1050
    } else {
 
1051
        for (i=scr->virtual_nr_edges-1; i>=0; --i) {
 
1052
            XRaiseWindow(dpy, scr->virtual_edges[i]);
 
1053
        }
 
1054
    }
 
1055
 
 
1056
    toggle ^= 1;
 
1057
}
 
1058
 
 
1059
 
 
1060
void
 
1061
wWorkspaceLowerEdge(WScreen *scr)
 
1062
{
 
1063
    int i;
 
1064
    for (i=0; i<scr->virtual_nr_edges; ++i) {
 
1065
        XLowerWindow(dpy, scr->virtual_edges[i]);
 
1066
    }
 
1067
}
 
1068
 
 
1069
 
 
1070
void
 
1071
wWorkspaceResizeViewport(WScreen *scr, int workspace)
 
1072
{
 
1073
    int x, y;
 
1074
    getViewPosition(scr, scr->current_workspace, &x, &y);
 
1075
    wWorkspaceSetViewport(scr, scr->current_workspace, x, y);
 
1076
}
 
1077
 
 
1078
 
 
1079
void
 
1080
updateWorkspaceGeometry(WScreen *scr, int workspace, int *view_x, int *view_y)
 
1081
{
 
1082
    int most_left, most_right, most_top, most_bottom;
 
1083
    WWindow *wwin;
 
1084
 
 
1085
    int heads = wXineramaHeads(scr);
 
1086
    typedef int strut_t[4];
 
1087
    strut_t * strut = (strut_t*)wmalloc(heads*sizeof(strut_t));
 
1088
    int head, i;
 
1089
 
 
1090
    for (head=0; head<heads; ++head) {
 
1091
        WMRect rect = wGetRectForHead(scr, head);
 
1092
        WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
 
1093
        strut[head][0] = area.x1 - rect.pos.x;
 
1094
        strut[head][1] = rect.pos.x + rect.size.width - area.x2;
 
1095
        strut[head][2] = area.y1 - rect.pos.y;
 
1096
        strut[head][3] = rect.pos.y + rect.size.height - area.y2;
 
1097
    }
 
1098
 
 
1099
    /* adjust workspace layout */
 
1100
    wwin = scr->focused_window;
 
1101
    most_right = 0;
 
1102
    most_bottom = 0;
 
1103
    most_left = scr->scr_width;
 
1104
    most_top = scr->scr_height;
 
1105
    for(;wwin; wwin = wwin->prev) {
 
1106
        if (wwin->frame->workspace == workspace) {
 
1107
            if (!wwin->flags.miniaturized && !IS_VSTUCK(wwin) &&
 
1108
                !wwin->flags.hidden) {
 
1109
 
 
1110
                head = wGetHeadForWindow(wwin);
 
1111
 
 
1112
                i = wwin->frame_x - strut[head][0];
 
1113
                if (i < most_left) /* record positions, should this be cached? */
 
1114
                    most_left = i;
 
1115
                i = wwin->frame_x + wwin->frame->core->width + strut[head][1];
 
1116
                if (HAS_BORDER(wwin))
 
1117
                    i+=2;
 
1118
                if (i > most_right)
 
1119
                    most_right = i;
 
1120
                i = wwin->frame_y - strut[head][2];
 
1121
                if (i < most_top)
 
1122
                    most_top = i;
 
1123
                i = wwin->frame_y + wwin->frame->core->height + strut[head][3];
 
1124
                if (HAS_BORDER(wwin))
 
1125
                    i+=2;
 
1126
                if (i > most_bottom) {
 
1127
                    most_bottom = i;
 
1128
                }
 
1129
            }
 
1130
        }
 
1131
    }
 
1132
 
 
1133
    if (most_left > 0) most_left = 0;
 
1134
    if (most_top > 0) most_top = 0;
 
1135
 
 
1136
    scr->workspaces[workspace]->width = WMAX(most_right, scr->scr_width) - WMIN(most_left, 0);
 
1137
    scr->workspaces[workspace]->height = WMAX(most_bottom, scr->scr_height) - WMIN(most_top, 0);
 
1138
 
 
1139
    *view_x += -most_left - scr->workspaces[workspace]->view_x;
 
1140
    scr->workspaces[workspace]->view_x = -most_left;
 
1141
 
 
1142
    *view_y += -most_top - scr->workspaces[workspace]->view_y;
 
1143
    scr->workspaces[workspace]->view_y = -most_top;
 
1144
 
 
1145
    wfree(strut);
 
1146
}
 
1147
 
 
1148
 
 
1149
typedef struct _delay_configure {
 
1150
    WWindow *wwin;
 
1151
    int delay_count;
 
1152
} _delay_configure;
 
1153
 
 
1154
 
 
1155
void
 
1156
sendConfigureNotify (_delay_configure *delay)
 
1157
{
 
1158
    WWindow *wwin;
 
1159
 
 
1160
    delay->delay_count--;
 
1161
    if (!delay->delay_count) {
 
1162
        for (wwin = delay->wwin; wwin; wwin = wwin->prev) {
 
1163
            wWindowSynthConfigureNotify(wwin);
 
1164
        }
 
1165
    }
 
1166
}
 
1167
 
 
1168
 
 
1169
Bool
 
1170
wWorkspaceSetViewport(WScreen *scr, int workspace, int view_x, int view_y)
 
1171
{
 
1172
    Bool adjust_flag = False;
 
1173
    int diff_x, diff_y;
 
1174
    static _delay_configure delay_configure = {NULL, 0};
 
1175
    WWorkspace *wptr;
 
1176
    WWindow *wwin;
 
1177
 
 
1178
    wptr = scr->workspaces[workspace];
 
1179
 
 
1180
    /*printf("wWorkspaceSetViewport %d %d\n", view_x, view_y);*/
 
1181
 
 
1182
    updateWorkspaceGeometry(scr, workspace, &view_x, &view_y);
 
1183
 
 
1184
    if (view_x + scr->scr_width > wptr->width) {
 
1185
        /* puts("right edge of vdesk"); */
 
1186
        view_x = wptr->width - scr->scr_width;
 
1187
    }
 
1188
    if (view_x < 0) {
 
1189
        /* puts("left edge of vdesk"); */
 
1190
        view_x = 0;
 
1191
    }
 
1192
    if (view_y + scr->scr_height > wptr->height) {
 
1193
        /* puts("right edge of vdesk"); */
 
1194
        view_y = wptr->height - scr->scr_height;
 
1195
    }
 
1196
    if (view_y < 0) {
 
1197
        /* puts("left edge of vdesk"); */
 
1198
        view_y = 0;
 
1199
    }
 
1200
 
 
1201
    diff_x = wptr->view_x - view_x;
 
1202
    diff_y = wptr->view_y - view_y;
 
1203
    if (!diff_x && !diff_y)
 
1204
        return False;
 
1205
 
 
1206
    wptr->view_x = view_x;
 
1207
    wptr->view_y = view_y;
 
1208
 
 
1209
#ifdef NETWM_HINTS
 
1210
    wNETWMUpdateDesktop(scr);
 
1211
#endif
 
1212
 
 
1213
    for (wwin = scr->focused_window; wwin; wwin = wwin->prev) {
 
1214
        if (wwin->frame->workspace == workspace && !IS_VSTUCK(wwin)) {
 
1215
            wWindowMove(wwin, wwin->frame_x + diff_x, wwin->frame_y + diff_y);
 
1216
            adjust_flag = True;
 
1217
        }
 
1218
    }
 
1219
    if (1) { /* if delay*/
 
1220
        delay_configure.delay_count++;
 
1221
        delay_configure.wwin = scr->focused_window;
 
1222
        WMAddTimerHandler(200, (WMCallback *)sendConfigureNotify, &delay_configure);
 
1223
    }
 
1224
 
 
1225
    return adjust_flag;
 
1226
}
 
1227
 
 
1228
 
 
1229
#endif
 
1230
 
 
1231
 
 
1232
static void
 
1233
switchWSCommand(WMenu *menu, WMenuEntry *entry)
 
1234
{
 
1235
    wWorkspaceChange(menu->frame->screen_ptr, (long)entry->clientdata);
 
1236
}
 
1237
 
 
1238
 
 
1239
static void
 
1240
deleteWSCommand(WMenu *menu, WMenuEntry *entry)
 
1241
{
 
1242
    wWorkspaceDelete(menu->frame->screen_ptr,
 
1243
                     menu->frame->screen_ptr->workspace_count-1);
 
1244
}
 
1245
 
 
1246
 
 
1247
static void
 
1248
newWSCommand(WMenu *menu, WMenuEntry *foo)
 
1249
{
 
1250
    int ws;
 
1251
 
 
1252
    ws = wWorkspaceNew(menu->frame->screen_ptr);
 
1253
    /* autochange workspace*/
 
1254
    if (ws>=0)
 
1255
        wWorkspaceChange(menu->frame->screen_ptr, ws);
 
1256
 
 
1257
 
 
1258
    /*
 
1259
     if (ws<9) {
 
1260
     int kcode;
 
1261
     if (wKeyBindings[WKBD_WORKSPACE1+ws]) {
 
1262
     kcode = wKeyBindings[WKBD_WORKSPACE1+ws]->keycode;
 
1263
     entry->rtext =
 
1264
     wstrdup(XKeysymToString(XKeycodeToKeysym(dpy, kcode, 0)));
 
1265
     }
 
1266
     }*/
 
1267
}
 
1268
 
 
1269
 
 
1270
static char*
 
1271
cropline(char *line)
 
1272
{
 
1273
    char *start, *end;
 
1274
 
 
1275
    if (strlen(line)==0)
 
1276
        return line;
 
1277
 
 
1278
    start = line;
 
1279
    end = &(line[strlen(line)])-1;
 
1280
    while (isspace(*line) && *line!=0) line++;
 
1281
    while (isspace(*end) && end!=line) {
 
1282
        *end=0;
 
1283
        end--;
 
1284
    }
 
1285
    return line;
 
1286
}
 
1287
 
 
1288
 
 
1289
void
 
1290
wWorkspaceRename(WScreen *scr, int workspace, char *name)
 
1291
{
 
1292
    char buf[MAX_WORKSPACENAME_WIDTH+1];
 
1293
    char *tmp;
 
1294
 
 
1295
    if (workspace >= scr->workspace_count)
 
1296
        return;
 
1297
 
 
1298
    /* trim white spaces */
 
1299
    tmp = cropline(name);
 
1300
 
 
1301
    if (strlen(tmp)==0) {
 
1302
        snprintf(buf, sizeof(buf), _("Workspace %i"), workspace+1);
 
1303
    } else {
 
1304
        strncpy(buf, tmp, MAX_WORKSPACENAME_WIDTH);
 
1305
    }
 
1306
    buf[MAX_WORKSPACENAME_WIDTH] = 0;
 
1307
 
 
1308
    /* update workspace */
 
1309
    wfree(scr->workspaces[workspace]->name);
 
1310
    scr->workspaces[workspace]->name = wstrdup(buf);
 
1311
 
 
1312
    if (scr->clip_ws_menu) {
 
1313
        if (strcmp(scr->clip_ws_menu->entries[workspace+2]->text, buf)!=0) {
 
1314
            wfree(scr->clip_ws_menu->entries[workspace+2]->text);
 
1315
            scr->clip_ws_menu->entries[workspace+2]->text = wstrdup(buf);
 
1316
            wMenuRealize(scr->clip_ws_menu);
 
1317
        }
 
1318
    }
 
1319
    if (scr->workspace_menu) {
 
1320
        if (strcmp(scr->workspace_menu->entries[workspace+2]->text, buf)!=0) {
 
1321
            wfree(scr->workspace_menu->entries[workspace+2]->text);
 
1322
            scr->workspace_menu->entries[workspace+2]->text = wstrdup(buf);
 
1323
            wMenuRealize(scr->workspace_menu);
 
1324
        }
 
1325
    }
 
1326
 
 
1327
    if (scr->clip_icon)
 
1328
        wClipIconPaint(scr->clip_icon);
 
1329
 
 
1330
    WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)workspace);
 
1331
}
 
1332
 
 
1333
 
 
1334
 
 
1335
 
 
1336
/* callback for when menu entry is edited */
 
1337
static void
 
1338
onMenuEntryEdited(WMenu *menu, WMenuEntry *entry)
 
1339
{
 
1340
    char *tmp;
 
1341
 
 
1342
    tmp = entry->text;
 
1343
    wWorkspaceRename(menu->frame->screen_ptr, (long)entry->clientdata, tmp);
 
1344
}
 
1345
 
 
1346
 
 
1347
WMenu*
 
1348
wWorkspaceMenuMake(WScreen *scr, Bool titled)
 
1349
{
 
1350
    WMenu *wsmenu;
 
1351
 
 
1352
    wsmenu = wMenuCreate(scr, titled ? _("Workspaces") : NULL, False);
 
1353
    if (!wsmenu) {
 
1354
        wwarning(_("could not create Workspace menu"));
 
1355
        return NULL;
 
1356
    }
 
1357
 
 
1358
    /* callback to be called when an entry is edited */
 
1359
    wsmenu->on_edit = onMenuEntryEdited;
 
1360
 
 
1361
    wMenuAddCallback(wsmenu, _("New"), newWSCommand, NULL);
 
1362
    wMenuAddCallback(wsmenu, _("Destroy Last"), deleteWSCommand, NULL);
 
1363
 
 
1364
    return wsmenu;
 
1365
}
 
1366
 
 
1367
 
 
1368
 
 
1369
void
 
1370
wWorkspaceMenuUpdate(WScreen *scr, WMenu *menu)
 
1371
{
 
1372
    int i;
 
1373
    long ws;
 
1374
    char title[MAX_WORKSPACENAME_WIDTH+1];
 
1375
    WMenuEntry *entry;
 
1376
    int tmp;
 
1377
 
 
1378
    if (!menu)
 
1379
        return;
 
1380
 
 
1381
    if (menu->entry_no < scr->workspace_count+2) {
 
1382
        /* new workspace(s) added */
 
1383
        i = scr->workspace_count-(menu->entry_no-2);
 
1384
        ws = menu->entry_no - 2;
 
1385
        while (i>0) {
 
1386
            strcpy(title, scr->workspaces[ws]->name);
 
1387
 
 
1388
            entry = wMenuAddCallback(menu, title, switchWSCommand, (void*)ws);
 
1389
            entry->flags.indicator = 1;
 
1390
            entry->flags.editable = 1;
 
1391
 
 
1392
            i--;
 
1393
            ws++;
 
1394
        }
 
1395
    } else if (menu->entry_no > scr->workspace_count+2) {
 
1396
        /* removed workspace(s) */
 
1397
        for (i = menu->entry_no-1; i >= scr->workspace_count+2; i--) {
 
1398
            wMenuRemoveItem(menu, i);
 
1399
        }
 
1400
    }
 
1401
    wMenuRealize(menu);
 
1402
 
 
1403
    for (i=0; i<scr->workspace_count; i++) {
 
1404
        menu->entries[i+2]->flags.indicator_on = 0;
 
1405
    }
 
1406
    menu->entries[scr->current_workspace+2]->flags.indicator_on = 1;
 
1407
 
 
1408
    /* don't let user destroy current workspace */
 
1409
    if (scr->current_workspace == scr->workspace_count-1) {
 
1410
        wMenuSetEnabled(menu, 1, False);
 
1411
    } else {
 
1412
        wMenuSetEnabled(menu, 1, True);
 
1413
    }
 
1414
 
 
1415
    tmp = menu->frame->top_width + 5;
 
1416
    /* if menu got unreachable, bring it to a visible place */
 
1417
    if (menu->frame_x < tmp - (int)menu->frame->core->width)
 
1418
        wMenuMove(menu, tmp - (int)menu->frame->core->width, menu->frame_y, False);
 
1419
 
 
1420
    wMenuPaint(menu);
 
1421
}
 
1422
 
 
1423
 
 
1424
void
 
1425
wWorkspaceSaveState(WScreen *scr, WMPropList *old_state)
 
1426
{
 
1427
    WMPropList *parr, *pstr, *wks_state, *old_wks_state, *foo, *bar;
 
1428
    int i;
 
1429
 
 
1430
    make_keys();
 
1431
 
 
1432
    old_wks_state = WMGetFromPLDictionary(old_state, dWorkspaces);
 
1433
    parr = WMCreatePLArray(NULL);
 
1434
    for (i=0; i < scr->workspace_count; i++) {
 
1435
        pstr = WMCreatePLString(scr->workspaces[i]->name);
 
1436
        wks_state = WMCreatePLDictionary(dName, pstr, NULL);
 
1437
        WMReleasePropList(pstr);
 
1438
        if (!wPreferences.flags.noclip) {
 
1439
            pstr = wClipSaveWorkspaceState(scr, i);
 
1440
            WMPutInPLDictionary(wks_state, dClip, pstr);
 
1441
            WMReleasePropList(pstr);
 
1442
        } else if (old_wks_state!=NULL) {
 
1443
            if ((foo = WMGetFromPLArray(old_wks_state, i))!=NULL) {
 
1444
                if ((bar = WMGetFromPLDictionary(foo, dClip))!=NULL) {
 
1445
                    WMPutInPLDictionary(wks_state, dClip, bar);
 
1446
                }
 
1447
            }
 
1448
        }
 
1449
        WMAddToPLArray(parr, wks_state);
 
1450
        WMReleasePropList(wks_state);
 
1451
    }
 
1452
    WMPutInPLDictionary(scr->session_state, dWorkspaces, parr);
 
1453
    WMReleasePropList(parr);
 
1454
}
 
1455
 
 
1456
 
 
1457
void
 
1458
wWorkspaceRestoreState(WScreen *scr)
 
1459
{
 
1460
    WMPropList *parr, *pstr, *wks_state, *clip_state;
 
1461
    int i, j, wscount;
 
1462
 
 
1463
    make_keys();
 
1464
 
 
1465
    if (scr->session_state == NULL)
 
1466
        return;
 
1467
 
 
1468
    parr = WMGetFromPLDictionary(scr->session_state, dWorkspaces);
 
1469
 
 
1470
    if (!parr)
 
1471
        return;
 
1472
 
 
1473
    wscount = scr->workspace_count;
 
1474
    for (i=0; i < WMIN(WMGetPropListItemCount(parr), MAX_WORKSPACES); i++) {
 
1475
        wks_state = WMGetFromPLArray(parr, i);
 
1476
        if (WMIsPLDictionary(wks_state))
 
1477
            pstr = WMGetFromPLDictionary(wks_state, dName);
 
1478
        else
 
1479
            pstr = wks_state;
 
1480
        if (i >= scr->workspace_count)
 
1481
            wWorkspaceNew(scr);
 
1482
        if (scr->workspace_menu) {
 
1483
            wfree(scr->workspace_menu->entries[i+2]->text);
 
1484
            scr->workspace_menu->entries[i+2]->text = wstrdup(WMGetFromPLString(pstr));
 
1485
            scr->workspace_menu->flags.realized = 0;
 
1486
        }
 
1487
        wfree(scr->workspaces[i]->name);
 
1488
        scr->workspaces[i]->name = wstrdup(WMGetFromPLString(pstr));
 
1489
        if (!wPreferences.flags.noclip) {
 
1490
            clip_state = WMGetFromPLDictionary(wks_state, dClip);
 
1491
            if (scr->workspaces[i]->clip)
 
1492
                wDockDestroy(scr->workspaces[i]->clip);
 
1493
            scr->workspaces[i]->clip = wDockRestoreState(scr, clip_state,
 
1494
                                                         WM_CLIP);
 
1495
            if (i>0)
 
1496
                wDockHideIcons(scr->workspaces[i]->clip);
 
1497
 
 
1498
            /* We set the global icons here, because scr->workspaces[i]->clip
 
1499
             * was not valid in wDockRestoreState().
 
1500
             * There we only set icon->omnipresent to know which icons we
 
1501
             * need to set here.
 
1502
             */
 
1503
            for (j=0; j<scr->workspaces[i]->clip->max_icons; j++) {
 
1504
                WAppIcon *aicon = scr->workspaces[i]->clip->icon_array[j];
 
1505
 
 
1506
                if (aicon && aicon->omnipresent) {
 
1507
                    aicon->omnipresent = 0;
 
1508
                    wClipMakeIconOmnipresent(aicon, True);
 
1509
                    XMapWindow(dpy, aicon->icon->core->window);
 
1510
                }
 
1511
            }
 
1512
        }
 
1513
 
 
1514
        WMPostNotificationName(WMNWorkspaceNameChanged, scr, (void*)i);
 
1515
    }
 
1516
}
 
1517
 
 
1518