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

« back to all changes in this revision

Viewing changes to src/menu.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
/* menu.c- generic menu, used for root menu, application menus etc.
 
2
 *
 
3
 *  Window Maker window manager
 
4
 *
 
5
 *  Copyright (c) 1997-2003 Alfredo K. Kojima
 
6
 *  Copyright (c) 1998-2003 Dan Pascu
 
7
 *
 
8
 *  This program is free software; you can redistribute it and/or modify
 
9
 *  it under the terms of the GNU General Public License as published by
 
10
 *  the Free Software Foundation; either version 2 of the License, or
 
11
 *  (at your option) any later version.
 
12
 *
 
13
 *  This program is distributed in the hope that it will be useful,
 
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 *  GNU General Public License for more details.
 
17
 *
 
18
 *  You should have received a copy of the GNU General Public License
 
19
 *  along with this program; if not, write to the Free Software
 
20
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 
21
 *  USA.
 
22
 */
 
23
 
 
24
#include "wconfig.h"
 
25
 
 
26
#include <X11/Xlib.h>
 
27
#include <X11/Xutil.h>
 
28
#include <X11/keysym.h>
 
29
#include <stdlib.h>
 
30
#include <string.h>
 
31
#include <stdio.h>
 
32
#include <unistd.h>
 
33
#include <ctype.h>
 
34
#if 0
 
35
#include <nana.h>
 
36
#endif
 
37
#include "WindowMaker.h"
 
38
#include "wcore.h"
 
39
#include "framewin.h"
 
40
#include "menu.h"
 
41
#include "actions.h"
 
42
#include "funcs.h"
 
43
#include "stacking.h"
 
44
#include "text.h"
 
45
#include "xinerama.h"
 
46
#include "workspace.h"
 
47
 
 
48
 
 
49
/****** Global Variables ******/
 
50
 
 
51
extern Cursor wCursor[WCUR_LAST];
 
52
 
 
53
extern XContext wWinContext;
 
54
 
 
55
extern WPreferences wPreferences;
 
56
 
 
57
#define MOD_MASK wPreferences.modifier_mask
 
58
 
 
59
#define MENU_SCROLL_STEP  menuScrollParameters[(int)wPreferences.menu_scroll_speed].steps
 
60
#define MENU_SCROLL_DELAY menuScrollParameters[(int)wPreferences.menu_scroll_speed].delay
 
61
 
 
62
 
 
63
 
 
64
#define MENUW(m)        ((m)->frame->core->width+2*FRAME_BORDER_WIDTH)
 
65
#define MENUH(m)        ((m)->frame->core->height+2*FRAME_BORDER_WIDTH)
 
66
 
 
67
 
 
68
/***** Local Stuff ******/
 
69
 
 
70
 
 
71
#define WSS_ROOTMENU    (1<<0)
 
72
#define WSS_SWITCHMENU  (1<<1)
 
73
#define WSS_WSMENU      (1<<2)
 
74
 
 
75
 
 
76
static struct {
 
77
    int steps;
 
78
    int delay;
 
79
} menuScrollParameters[5] = {
 
80
    {MENU_SCROLL_STEPS_UF, MENU_SCROLL_DELAY_UF},
 
81
    {MENU_SCROLL_STEPS_F, MENU_SCROLL_DELAY_F},
 
82
    {MENU_SCROLL_STEPS_M, MENU_SCROLL_DELAY_M},
 
83
    {MENU_SCROLL_STEPS_S, MENU_SCROLL_DELAY_S},
 
84
    {MENU_SCROLL_STEPS_US, MENU_SCROLL_DELAY_US}};
 
85
 
 
86
 
 
87
static void menuMouseDown(WObjDescriptor *desc, XEvent *event);
 
88
static void menuExpose(WObjDescriptor *desc, XEvent *event);
 
89
 
 
90
static void menuTitleDoubleClick(WCoreWindow *sender, void *data, XEvent *event);
 
91
static void menuTitleMouseDown(WCoreWindow *sender, void *data, XEvent *event);
 
92
 
 
93
static void menuCloseClick(WCoreWindow *sender, void *data, XEvent *event);
 
94
 
 
95
static void updateTexture(WMenu *menu);
 
96
 
 
97
#ifndef LITE
 
98
static int saveMenuRecurs(WMPropList *menus, WScreen *scr, WMenu *menu);
 
99
static int restoreMenuRecurs(WScreen *scr, WMPropList *menus, WMenu *menu, char *path);
 
100
#endif /* !LITE */
 
101
 
 
102
static void selectEntry(WMenu *menu, int entry_no);
 
103
static void closeCascade(WMenu *menu);
 
104
 
 
105
 
 
106
/****** Notification Observers ******/
 
107
 
 
108
static void
 
109
appearanceObserver(void *self, WMNotification *notif)
 
110
{
 
111
    WMenu *menu = (WMenu*)self;
 
112
    int flags = (int)WMGetNotificationClientData(notif);
 
113
 
 
114
    if (!menu->flags.realized)
 
115
        return;
 
116
 
 
117
    if (WMGetNotificationName(notif) == WNMenuAppearanceSettingsChanged) {
 
118
        if (flags & WFontSettings) {
 
119
            menu->flags.realized = 0;
 
120
            wMenuRealize(menu);
 
121
        }
 
122
        if (flags & WTextureSettings) {
 
123
            if (!menu->flags.brother)
 
124
                updateTexture(menu);
 
125
        }
 
126
        if (flags & (WTextureSettings|WColorSettings)) {
 
127
            wMenuPaint(menu);
 
128
        }
 
129
    } else if (menu->flags.titled) {
 
130
 
 
131
        if (flags & WFontSettings) {
 
132
            menu->flags.realized = 0;
 
133
            wMenuRealize(menu);
 
134
        }
 
135
        if (flags & WTextureSettings) {
 
136
            menu->frame->flags.need_texture_remake = 1;
 
137
        }
 
138
        if (flags & (WColorSettings|WTextureSettings)) {
 
139
            wFrameWindowPaint(menu->frame);
 
140
        }
 
141
    }
 
142
}
 
143
 
 
144
/************************************/
 
145
 
 
146
 
 
147
/*
 
148
 *----------------------------------------------------------------------
 
149
 * wMenuCreate--
 
150
 *      Creates a new empty menu with the specified title. If main_menu
 
151
 * is True, the created menu will be a main menu, which has some special
 
152
 * properties such as being placed over other normal menus.
 
153
 *      If title is NULL, the menu will have no titlebar.
 
154
 *
 
155
 * Returns:
 
156
 *      The created menu.
 
157
 *----------------------------------------------------------------------
 
158
 */
 
159
WMenu*
 
160
wMenuCreate(WScreen *screen, char *title, int main_menu)
 
161
{
 
162
    WMenu *menu;
 
163
    static int brother=0;
 
164
    int tmp, flags;
 
165
 
 
166
    menu = wmalloc(sizeof(WMenu));
 
167
 
 
168
    memset(menu, 0, sizeof(WMenu));
 
169
 
 
170
#ifdef SINGLE_MENULEVEL
 
171
    tmp = WMSubmenuLevel;
 
172
#else
 
173
    tmp = (main_menu ? WMMainMenuLevel : WMSubmenuLevel);
 
174
#endif
 
175
 
 
176
    flags = WFF_SINGLE_STATE|WFF_BORDER;
 
177
    if (title) {
 
178
        flags |= WFF_TITLEBAR|WFF_RIGHT_BUTTON;
 
179
        menu->flags.titled = 1;
 
180
    }
 
181
    menu->frame =
 
182
        wFrameWindowCreate(screen, tmp, 8, 2, 1, 1, &wPreferences.menu_title_clearance, flags,
 
183
                           screen->menu_title_texture, NULL,
 
184
                           screen->menu_title_color,
 
185
                           &screen->menu_title_font);
 
186
 
 
187
    menu->frame->core->descriptor.parent = menu;
 
188
    menu->frame->core->descriptor.parent_type = WCLASS_MENU;
 
189
    menu->frame->core->descriptor.handle_mousedown = menuMouseDown;
 
190
 
 
191
    wFrameWindowHideButton(menu->frame, WFF_RIGHT_BUTTON);
 
192
 
 
193
    if (title) {
 
194
        menu->frame->title = wstrdup(title);
 
195
    }
 
196
 
 
197
    menu->frame->flags.justification = WTJ_LEFT;
 
198
 
 
199
    menu->frame->rbutton_image = screen->b_pixmaps[WBUT_CLOSE];
 
200
 
 
201
    menu->entry_no = 0;
 
202
    menu->alloced_entries = 0;
 
203
    menu->selected_entry = -1;
 
204
    menu->entries = NULL;
 
205
 
 
206
    menu->frame_x = screen->app_menu_x;
 
207
    menu->frame_y = screen->app_menu_y;
 
208
 
 
209
    menu->frame->child = menu;
 
210
 
 
211
    menu->flags.lowered = 0;
 
212
 
 
213
    /* create borders */
 
214
    if (title) {
 
215
        /* setup object descriptors */
 
216
        menu->frame->on_mousedown_titlebar = menuTitleMouseDown;
 
217
        menu->frame->on_dblclick_titlebar = menuTitleDoubleClick;
 
218
    }
 
219
 
 
220
    menu->frame->on_click_right = menuCloseClick;
 
221
 
 
222
 
 
223
    menu->menu = wCoreCreate(menu->frame->core, 0, menu->frame->top_width,
 
224
                             menu->frame->core->width, 10);
 
225
 
 
226
    menu->menu->descriptor.parent = menu;
 
227
    menu->menu->descriptor.parent_type = WCLASS_MENU;
 
228
    menu->menu->descriptor.handle_expose = menuExpose;
 
229
    menu->menu->descriptor.handle_mousedown = menuMouseDown;
 
230
 
 
231
    menu->menu_texture_data = None;
 
232
 
 
233
    XMapWindow(dpy, menu->menu->window);
 
234
 
 
235
    XFlush(dpy);
 
236
 
 
237
    if (!brother) {
 
238
        brother = 1;
 
239
        menu->brother = wMenuCreate(screen, title, main_menu);
 
240
        brother = 0;
 
241
        menu->brother->flags.brother = 1;
 
242
        menu->brother->brother = menu;
 
243
    }
 
244
    WMAddNotificationObserver(appearanceObserver, menu,
 
245
                              WNMenuAppearanceSettingsChanged, menu);
 
246
 
 
247
    WMAddNotificationObserver(appearanceObserver, menu,
 
248
                              WNMenuTitleAppearanceSettingsChanged, menu);
 
249
 
 
250
 
 
251
    return menu;
 
252
}
 
253
 
 
254
 
 
255
 
 
256
 
 
257
WMenu*
 
258
wMenuCreateForApp(WScreen *screen, char *title, int main_menu)
 
259
{
 
260
    WMenu *menu;
 
261
 
 
262
    menu = wMenuCreate(screen, title, main_menu);
 
263
    if (!menu)
 
264
        return NULL;
 
265
    menu->flags.app_menu = 1;
 
266
    menu->brother->flags.app_menu = 1;
 
267
 
 
268
    return menu;
 
269
}
 
270
 
 
271
 
 
272
 
 
273
static void
 
274
insertEntry(WMenu *menu, WMenuEntry *entry, int index)
 
275
{
 
276
    int i;
 
277
 
 
278
    for (i = menu->entry_no-1; i >= index; i--) {
 
279
        menu->entries[i]->order++;
 
280
        menu->entries[i+1] = menu->entries[i];
 
281
    }
 
282
    menu->entries[index] = entry;
 
283
}
 
284
 
 
285
 
 
286
WMenuEntry*
 
287
wMenuInsertCallback(WMenu *menu, int index, char *text,
 
288
                    void (*callback)(WMenu *menu, WMenuEntry *entry),
 
289
                    void *clientdata)
 
290
{
 
291
    WMenuEntry *entry;
 
292
 
 
293
    menu->flags.realized = 0;
 
294
    menu->brother->flags.realized = 0;
 
295
 
 
296
    /* reallocate array if it's too small */
 
297
    if (menu->entry_no >= menu->alloced_entries) {
 
298
        void *tmp;
 
299
 
 
300
        tmp = wrealloc(menu->entries,
 
301
                       sizeof(WMenuEntry)*(menu->alloced_entries+5));
 
302
 
 
303
        menu->entries = tmp;
 
304
        menu->alloced_entries += 5;
 
305
 
 
306
        menu->brother->entries = tmp;
 
307
        menu->brother->alloced_entries = menu->alloced_entries;
 
308
    }
 
309
    entry = wmalloc(sizeof(WMenuEntry));
 
310
    memset(entry, 0, sizeof(WMenuEntry));
 
311
    entry->flags.enabled = 1;
 
312
    entry->text = wstrdup(text);
 
313
    entry->cascade = -1;
 
314
    entry->clientdata = clientdata;
 
315
    entry->callback = callback;
 
316
    if (index<0 || index>=menu->entry_no) {
 
317
        entry->order = menu->entry_no;
 
318
        menu->entries[menu->entry_no] = entry;
 
319
    } else {
 
320
        entry->order = index;
 
321
        insertEntry(menu, entry, index);
 
322
    }
 
323
 
 
324
    menu->entry_no++;
 
325
    menu->brother->entry_no = menu->entry_no;
 
326
 
 
327
    return entry;
 
328
}
 
329
 
 
330
 
 
331
 
 
332
void
 
333
wMenuEntrySetCascade(WMenu *menu, WMenuEntry *entry, WMenu *cascade)
 
334
{
 
335
    WMenu *brother = menu->brother;
 
336
    int i, done;
 
337
 
 
338
    assert(menu->flags.brother==0);
 
339
 
 
340
    if (entry->cascade>=0) {
 
341
        menu->flags.realized = 0;
 
342
        brother->flags.realized = 0;
 
343
    }
 
344
 
 
345
    cascade->parent = menu;
 
346
 
 
347
    cascade->brother->parent = brother;
 
348
 
 
349
    done = 0;
 
350
    for (i=0; i<menu->cascade_no; i++) {
 
351
        if (menu->cascades[i]==NULL) {
 
352
            menu->cascades[i] = cascade;
 
353
            brother->cascades[i] = cascade->brother;
 
354
            done = 1;
 
355
            entry->cascade = i;
 
356
            break;
 
357
        }
 
358
    }
 
359
    if (!done) {
 
360
        entry->cascade = menu->cascade_no;
 
361
 
 
362
        menu->cascades = wrealloc(menu->cascades,
 
363
                                  sizeof(WMenu)*(menu->cascade_no+1));
 
364
        menu->cascades[menu->cascade_no++] = cascade;
 
365
 
 
366
 
 
367
        brother->cascades = wrealloc(brother->cascades,
 
368
                                     sizeof(WMenu)*(brother->cascade_no+1));
 
369
        brother->cascades[brother->cascade_no++] = cascade->brother;
 
370
    }
 
371
 
 
372
 
 
373
    if (menu->flags.lowered) {
 
374
 
 
375
        cascade->flags.lowered = 1;
 
376
        ChangeStackingLevel(cascade->frame->core, WMNormalLevel);
 
377
 
 
378
        cascade->brother->flags.lowered = 1;
 
379
        ChangeStackingLevel(cascade->brother->frame->core, WMNormalLevel);
 
380
    }
 
381
 
 
382
    if (!menu->flags.realized)
 
383
        wMenuRealize(menu);
 
384
}
 
385
 
 
386
 
 
387
void
 
388
wMenuEntryRemoveCascade(WMenu *menu, WMenuEntry *entry)
 
389
{
 
390
    assert(menu->flags.brother==0);
 
391
 
 
392
    /* destroy cascade menu */
 
393
    if (entry->cascade>=0 && menu->cascades
 
394
        && menu->cascades[entry->cascade]!=NULL) {
 
395
 
 
396
        wMenuDestroy(menu->cascades[entry->cascade], True);
 
397
 
 
398
        menu->cascades[entry->cascade] = NULL;
 
399
        menu->brother->cascades[entry->cascade] = NULL;
 
400
 
 
401
        entry->cascade = -1;
 
402
    }
 
403
}
 
404
 
 
405
 
 
406
void
 
407
wMenuRemoveItem(WMenu *menu, int index)
 
408
{
 
409
    int i;
 
410
 
 
411
    if (menu->flags.brother) {
 
412
        wMenuRemoveItem(menu->brother, index);
 
413
        return;
 
414
    }
 
415
 
 
416
    if (index>=menu->entry_no) return;
 
417
 
 
418
    /* destroy cascade menu */
 
419
    wMenuEntryRemoveCascade(menu, menu->entries[index]);
 
420
 
 
421
    /* destroy unshared data */
 
422
 
 
423
    if (menu->entries[index]->text)
 
424
        wfree(menu->entries[index]->text);
 
425
 
 
426
    if (menu->entries[index]->rtext)
 
427
        wfree(menu->entries[index]->rtext);
 
428
 
 
429
    if (menu->entries[index]->free_cdata && menu->entries[index]->clientdata)
 
430
        (*menu->entries[index]->free_cdata)(menu->entries[index]->clientdata);
 
431
 
 
432
    wfree(menu->entries[index]);
 
433
 
 
434
    for (i=index; i<menu->entry_no-1; i++) {
 
435
        menu->entries[i+1]->order--;
 
436
        menu->entries[i]=menu->entries[i+1];
 
437
    }
 
438
    menu->entry_no--;
 
439
    menu->brother->entry_no--;
 
440
}
 
441
 
 
442
 
 
443
static Pixmap
 
444
renderTexture(WMenu *menu)
 
445
{
 
446
    RImage *img;
 
447
    Pixmap pix;
 
448
    int i;
 
449
    RColor light;
 
450
    RColor dark;
 
451
    RColor mid;
 
452
    WScreen *scr = menu->menu->screen_ptr;
 
453
    WTexture *texture = scr->menu_item_texture;
 
454
 
 
455
    if (wPreferences.menu_style == MS_NORMAL) {
 
456
        img = wTextureRenderImage(texture, menu->menu->width,
 
457
                                  menu->entry_height, WREL_MENUENTRY);
 
458
    } else {
 
459
        img = wTextureRenderImage(texture, menu->menu->width,
 
460
                                  menu->menu->height+1, WREL_MENUENTRY);
 
461
    }
 
462
    if (!img) {
 
463
        wwarning(_("could not render texture: %s"),
 
464
                 RMessageForError(RErrorCode));
 
465
 
 
466
        return None;
 
467
    }
 
468
 
 
469
    if (wPreferences.menu_style == MS_SINGLE_TEXTURE) {
 
470
        light.alpha = 0;
 
471
        light.red = light.green = light.blue = 80;
 
472
 
 
473
        dark.alpha = 255;
 
474
        dark.red = dark.green = dark.blue = 0;
 
475
 
 
476
        mid.alpha = 0;
 
477
        mid.red = mid.green = mid.blue = 40;
 
478
 
 
479
        for (i = 1; i < menu->entry_no; i++) {
 
480
            ROperateLine(img, RSubtractOperation, 0, i*menu->entry_height-2,
 
481
                         menu->menu->width-1, i*menu->entry_height-2, &mid);
 
482
 
 
483
            RDrawLine(img, 0, i*menu->entry_height-1,
 
484
                      menu->menu->width-1, i*menu->entry_height-1, &dark);
 
485
 
 
486
            ROperateLine(img, RAddOperation, 0, i*menu->entry_height,
 
487
                         menu->menu->width-1, i*menu->entry_height,
 
488
                         &light);
 
489
        }
 
490
    }
 
491
    if (!RConvertImage(scr->rcontext, img, &pix)) {
 
492
        wwarning(_("error rendering image:%s"), RMessageForError(RErrorCode));
 
493
    }
 
494
    RReleaseImage(img);
 
495
 
 
496
    return pix;
 
497
}
 
498
 
 
499
 
 
500
static void
 
501
updateTexture(WMenu *menu)
 
502
{
 
503
    WScreen *scr = menu->menu->screen_ptr;
 
504
 
 
505
    /* setup background texture */
 
506
    if (scr->menu_item_texture->any.type != WTEX_SOLID) {
 
507
        if (!menu->flags.brother) {
 
508
            FREE_PIXMAP(menu->menu_texture_data);
 
509
 
 
510
            menu->menu_texture_data = renderTexture(menu);
 
511
 
 
512
            XSetWindowBackgroundPixmap(dpy, menu->menu->window,
 
513
                                       menu->menu_texture_data);
 
514
            XClearWindow(dpy, menu->menu->window);
 
515
 
 
516
            XSetWindowBackgroundPixmap(dpy, menu->brother->menu->window,
 
517
                                       menu->menu_texture_data);
 
518
            XClearWindow(dpy, menu->brother->menu->window);
 
519
        }
 
520
    } else {
 
521
        XSetWindowBackground(dpy, menu->menu->window,
 
522
                             scr->menu_item_texture->any.color.pixel);
 
523
        XClearWindow(dpy, menu->menu->window);
 
524
    }
 
525
}
 
526
 
 
527
 
 
528
void
 
529
wMenuRealize(WMenu *menu)
 
530
{
 
531
    int i;
 
532
    int width, rwidth, mrwidth, mwidth;
 
533
    int theight, twidth, eheight;
 
534
    WScreen *scr = menu->frame->screen_ptr;
 
535
    static int brother_done=0;
 
536
    int flags;
 
537
 
 
538
    if (!brother_done) {
 
539
        brother_done = 1;
 
540
        wMenuRealize(menu->brother);
 
541
        brother_done = 0;
 
542
    }
 
543
 
 
544
    flags = WFF_SINGLE_STATE|WFF_BORDER;
 
545
    if (menu->flags.titled)
 
546
        flags |= WFF_TITLEBAR|WFF_RIGHT_BUTTON;
 
547
 
 
548
    wFrameWindowUpdateBorders(menu->frame, flags);
 
549
 
 
550
    if (menu->flags.titled) {
 
551
        twidth = WMWidthOfString(scr->menu_title_font, menu->frame->title,
 
552
                                 strlen(menu->frame->title));
 
553
        theight = menu->frame->top_width;
 
554
        twidth += theight + (wPreferences.new_style ? 16 : 8);
 
555
    } else {
 
556
        twidth = 0;
 
557
        theight = 0;
 
558
    }
 
559
    eheight = WMFontHeight(scr->menu_entry_font) + 6 + wPreferences.menu_text_clearance * 2;
 
560
    menu->entry_height = eheight;
 
561
    mrwidth = 0;
 
562
    mwidth = 0;
 
563
    for (i=0; i<menu->entry_no; i++) {
 
564
        char *text;
 
565
 
 
566
        /* search widest text */
 
567
        text = menu->entries[i]->text;
 
568
        width = WMWidthOfString(scr->menu_entry_font, text, strlen(text))+10;
 
569
 
 
570
        if (menu->entries[i]->flags.indicator) {
 
571
            width += MENU_INDICATOR_SPACE;
 
572
        }
 
573
 
 
574
        if (width > mwidth)
 
575
            mwidth = width;
 
576
 
 
577
        /* search widest text on right */
 
578
        text = menu->entries[i]->rtext;
 
579
        if (text)
 
580
            rwidth = WMWidthOfString(scr->menu_entry_font, text, strlen(text))
 
581
                + 10;
 
582
        else if (menu->entries[i]->cascade>=0)
 
583
            rwidth = 16;
 
584
        else
 
585
            rwidth = 4;
 
586
 
 
587
        if (rwidth > mrwidth)
 
588
            mrwidth = rwidth;
 
589
    }
 
590
    mwidth += mrwidth;
 
591
 
 
592
    if (mwidth < twidth)
 
593
        mwidth = twidth;
 
594
 
 
595
 
 
596
    wCoreConfigure(menu->menu, 0, theight, mwidth, menu->entry_no*eheight -1);
 
597
 
 
598
    wFrameWindowResize(menu->frame, mwidth, menu->entry_no*eheight-1
 
599
                       + menu->frame->top_width + menu->frame->bottom_width);
 
600
 
 
601
 
 
602
    updateTexture(menu);
 
603
 
 
604
    menu->flags.realized = 1;
 
605
 
 
606
    if (menu->flags.mapped)
 
607
        wMenuPaint(menu);
 
608
    if (menu->brother->flags.mapped)
 
609
        wMenuPaint(menu->brother);
 
610
}
 
611
 
 
612
 
 
613
void
 
614
wMenuDestroy(WMenu *menu, int recurse)
 
615
{
 
616
    int i;
 
617
 
 
618
    WMRemoveNotificationObserver(menu);
 
619
 
 
620
    /* remove any pending timers */
 
621
    if (menu->timer)
 
622
        WMDeleteTimerHandler(menu->timer);
 
623
    menu->timer = NULL;
 
624
 
 
625
    /* call destroy handler */
 
626
    if (menu->on_destroy)
 
627
        (*menu->on_destroy)(menu);
 
628
 
 
629
    /* Destroy items if this menu own them. If this is the "brother" menu,
 
630
     * leave them alone as it is shared by them.
 
631
     */
 
632
    if (!menu->flags.brother) {
 
633
        for (i=0; i<menu->entry_no; i++) {
 
634
 
 
635
            wfree(menu->entries[i]->text);
 
636
 
 
637
            if (menu->entries[i]->rtext)
 
638
                wfree(menu->entries[i]->rtext);
 
639
#ifdef USER_MENU
 
640
 
 
641
            if (menu->entries[i]->instances){
 
642
                WMReleasePropList(menu->entries[i]->instances);
 
643
            }
 
644
#endif /* USER_MENU */
 
645
 
 
646
            if (menu->entries[i]->free_cdata && menu->entries[i]->clientdata) {
 
647
                (*menu->entries[i]->free_cdata)(menu->entries[i]->clientdata);
 
648
            }
 
649
            wfree(menu->entries[i]);
 
650
        }
 
651
 
 
652
        if (recurse) {
 
653
            for (i=0; i<menu->cascade_no; i++) {
 
654
                if (menu->cascades[i]) {
 
655
                    if (menu->cascades[i]->flags.brother)
 
656
                        wMenuDestroy(menu->cascades[i]->brother, recurse);
 
657
                    else
 
658
                        wMenuDestroy(menu->cascades[i], recurse);
 
659
                }
 
660
            }
 
661
        }
 
662
 
 
663
        if (menu->entries)
 
664
            wfree(menu->entries);
 
665
 
 
666
    }
 
667
 
 
668
    FREE_PIXMAP(menu->menu_texture_data);
 
669
 
 
670
    if (menu->cascades)
 
671
        wfree(menu->cascades);
 
672
 
 
673
    wCoreDestroy(menu->menu);
 
674
    wFrameWindowDestroy(menu->frame);
 
675
 
 
676
    /* destroy copy of this menu */
 
677
    if (!menu->flags.brother && menu->brother)
 
678
        wMenuDestroy(menu->brother, False);
 
679
 
 
680
    wfree(menu);
 
681
}
 
682
 
 
683
 
 
684
#define F_NORMAL        0
 
685
#define F_TOP           1
 
686
#define F_BOTTOM        2
 
687
#define F_NONE          3
 
688
 
 
689
static void
 
690
drawFrame(WScreen *scr, Drawable win, int y, int w, int h, int type)
 
691
{
 
692
    XSegment segs[2];
 
693
    int i;
 
694
 
 
695
    i = 0;
 
696
    segs[i].x1 = segs[i].x2 = w-1;
 
697
    segs[i].y1 = y;
 
698
    segs[i].y2 = y + h - 1;
 
699
    i++;
 
700
    if (type != F_TOP && type != F_NONE) {
 
701
        segs[i].x1 = 1;
 
702
        segs[i].y1 = segs[i].y2 = y + h-2;
 
703
        segs[i].x2 = w-1;
 
704
        i++;
 
705
    }
 
706
    XDrawSegments(dpy, win, scr->menu_item_auxtexture->dim_gc, segs, i);
 
707
 
 
708
    i = 0;
 
709
    segs[i].x1 = 0;
 
710
    segs[i].y1 = y;
 
711
    segs[i].x2 = 0;
 
712
    segs[i].y2 = y + h - 1;
 
713
    i++;
 
714
    if (type != F_BOTTOM && type != F_NONE) {
 
715
        segs[i].x1 = 0;
 
716
        segs[i].y1 = y;
 
717
        segs[i].x2 = w-1;
 
718
        segs[i].y2 = y;
 
719
        i++;
 
720
    }
 
721
    XDrawSegments(dpy, win, scr->menu_item_auxtexture->light_gc, segs, i);
 
722
 
 
723
    if (type != F_TOP && type != F_NONE)
 
724
        XDrawLine(dpy, win, scr->menu_item_auxtexture->dark_gc, 0, y+h-1,
 
725
                  w-1, y+h-1);
 
726
}
 
727
 
 
728
 
 
729
static void
 
730
paintEntry(WMenu *menu, int index, int selected)
 
731
{
 
732
    WScreen *scr=menu->frame->screen_ptr;
 
733
    Window win = menu->menu->window;
 
734
    WMenuEntry *entry=menu->entries[index];
 
735
    GC light, dim, dark;
 
736
    WMColor *color;
 
737
    int x, y, w, h, tw;
 
738
    int type;
 
739
 
 
740
    if (!menu->flags.realized) return;
 
741
    h = menu->entry_height;
 
742
    w = menu->menu->width;
 
743
    y = index * h;
 
744
 
 
745
    light = scr->menu_item_auxtexture->light_gc;
 
746
    dim = scr->menu_item_auxtexture->dim_gc;
 
747
    dark = scr->menu_item_auxtexture->dark_gc;
 
748
 
 
749
    if (wPreferences.menu_style == MS_FLAT && menu->entry_no > 1) {
 
750
        if (index == 0)
 
751
            type = F_TOP;
 
752
        else if (index == menu->entry_no - 1)
 
753
            type = F_BOTTOM;
 
754
        else
 
755
            type = F_NONE;
 
756
    } else {
 
757
        type = F_NORMAL;
 
758
    }
 
759
 
 
760
    /* paint background */
 
761
    if (selected) {
 
762
        XFillRectangle(dpy, win, WMColorGC(scr->select_color), 1, y+1, w-2, h-3);
 
763
        if (scr->menu_item_texture->any.type == WTEX_SOLID)
 
764
            drawFrame(scr, win, y, w, h, type);
 
765
    } else {
 
766
        if (scr->menu_item_texture->any.type == WTEX_SOLID) {
 
767
            XClearArea(dpy, win, 0, y + 1, w - 1, h - 3, False);
 
768
            /* draw the frame */
 
769
            drawFrame(scr, win, y, w, h, type);
 
770
        } else {
 
771
            XClearArea(dpy, win, 0, y, w, h, False);
 
772
        }
 
773
    }
 
774
 
 
775
    if (selected) {
 
776
        if (entry->flags.enabled)
 
777
            color = scr->select_text_color;
 
778
        else
 
779
            color = scr->dtext_color;
 
780
    } else if (!entry->flags.enabled) {
 
781
        color = scr->dtext_color;
 
782
    } else {
 
783
        color = scr->mtext_color;
 
784
    }
 
785
    /* draw text */
 
786
    x = 5;
 
787
    if (entry->flags.indicator)
 
788
        x += MENU_INDICATOR_SPACE + 2;
 
789
 
 
790
    WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font,
 
791
                 x, 3 + y + wPreferences.menu_text_clearance, entry->text, strlen(entry->text));
 
792
 
 
793
    if (entry->cascade>=0) {
 
794
        /* draw the cascade indicator */
 
795
        XDrawLine(dpy, win, dim,   w-11, y+6,   w-6,  y+h/2-1);
 
796
        XDrawLine(dpy, win, light, w-11, y+h-8, w-6,  y+h/2-1);
 
797
        XDrawLine(dpy, win, dark,  w-12, y+6,   w-12, y+h-8);
 
798
    }
 
799
 
 
800
    /* draw indicator */
 
801
    if (entry->flags.indicator && entry->flags.indicator_on) {
 
802
        int iw, ih;
 
803
        WPixmap *indicator;
 
804
 
 
805
 
 
806
        switch (entry->flags.indicator_type) {
 
807
        case MI_CHECK:
 
808
            indicator = scr->menu_check_indicator;
 
809
            break;
 
810
        case MI_MINIWINDOW:
 
811
            indicator = scr->menu_mini_indicator;
 
812
            break;
 
813
        case MI_HIDDEN:
 
814
            indicator = scr->menu_hide_indicator;
 
815
            break;
 
816
        case MI_SHADED:
 
817
            indicator = scr->menu_shade_indicator;
 
818
            break;
 
819
        case MI_DIAMOND:
 
820
        default:
 
821
            indicator = scr->menu_radio_indicator;
 
822
            break;
 
823
        }
 
824
 
 
825
        iw = indicator->width;
 
826
        ih = indicator->height;
 
827
        XSetClipMask(dpy, scr->copy_gc, indicator->mask);
 
828
        XSetClipOrigin(dpy, scr->copy_gc, 5, y+(h-ih)/2);
 
829
        if (selected) {
 
830
            if (entry->flags.enabled) {
 
831
                XSetForeground(dpy, scr->copy_gc, WMColorPixel(scr->select_text_color));
 
832
            } else {
 
833
                XSetForeground(dpy, scr->copy_gc, WMColorPixel(scr->dtext_color));
 
834
            }
 
835
        } else {
 
836
            if (entry->flags.enabled) {
 
837
                XSetForeground(dpy, scr->copy_gc, WMColorPixel(scr->mtext_color));
 
838
            } else {
 
839
                XSetForeground(dpy, scr->copy_gc, WMColorPixel(scr->dtext_color));
 
840
            }
 
841
        }
 
842
        XFillRectangle(dpy, win, scr->copy_gc, 5, y+(h-ih)/2, iw, ih);
 
843
        /*
 
844
         XCopyArea(dpy, indicator->image, win, scr->copy_gc, 0, 0,
 
845
         iw, ih, 5, y+(h-ih)/2);
 
846
         */
 
847
        XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
 
848
    }
 
849
 
 
850
    /* draw right text */
 
851
 
 
852
    if (entry->rtext && entry->cascade<0) {
 
853
        tw = WMWidthOfString(scr->menu_entry_font, entry->rtext,
 
854
                             strlen(entry->rtext));
 
855
 
 
856
        WMDrawString(scr->wmscreen, win, color, scr->menu_entry_font, w-6-tw,
 
857
                     y + 3 + wPreferences.menu_text_clearance, entry->rtext, strlen(entry->rtext));
 
858
    }
 
859
}
 
860
 
 
861
 
 
862
static void
 
863
move_menus(WMenu *menu, int x, int y)
 
864
{
 
865
    while (menu->parent) {
 
866
        menu = menu->parent;
 
867
        x -= MENUW(menu);
 
868
        if (!wPreferences.align_menus && menu->selected_entry>=0) {
 
869
            y -= menu->selected_entry*menu->entry_height;
 
870
        }
 
871
    }
 
872
    wMenuMove(menu, x, y, True);
 
873
}
 
874
 
 
875
static void
 
876
makeVisible(WMenu *menu)
 
877
{
 
878
    WScreen *scr = menu->frame->screen_ptr;
 
879
    int x1, y1, x2, y2, new_x, new_y, move;
 
880
    WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
 
881
 
 
882
    if (menu->entry_no<0) return;
 
883
 
 
884
    x1 = menu->frame_x;
 
885
    y1 = menu->frame_y+menu->frame->top_width
 
886
        + menu->selected_entry*menu->entry_height;
 
887
    x2 = x1 + MENUW(menu);
 
888
    y2 = y1 + menu->entry_height;
 
889
 
 
890
    new_x = x1;
 
891
    new_y = y1;
 
892
    move = 0;
 
893
 
 
894
 
 
895
    if (x1 < rect.pos.x) {
 
896
        new_x = rect.pos.x;
 
897
        move = 1;
 
898
    } else if (x2 >= rect.pos.x + rect.size.width) {
 
899
        new_x = rect.pos.x + rect.size.width - MENUW(menu) - 1;
 
900
        move = 1;
 
901
    }
 
902
 
 
903
    if (y1 < rect.pos.y) {
 
904
        new_y = rect.pos.y;
 
905
        move = 1;
 
906
    } else if (y2 >= rect.pos.y + rect.size.height) {
 
907
        new_y = rect.pos.y + rect.size.height - menu->entry_height - 1;
 
908
        move = 1;
 
909
    }
 
910
 
 
911
    new_y = new_y - menu->frame->top_width
 
912
        - menu->selected_entry*menu->entry_height;
 
913
    move_menus(menu, new_x, new_y);
 
914
}
 
915
 
 
916
 
 
917
static int
 
918
check_key(WMenu *menu, XKeyEvent *event)
 
919
{
 
920
    int i, ch, s;
 
921
    char buffer[32];
 
922
 
 
923
    if (XLookupString(event, buffer, 32, NULL, NULL)<1)
 
924
        return -1;
 
925
 
 
926
    ch = toupper(buffer[0]);
 
927
 
 
928
    s = (menu->selected_entry>=0 ? menu->selected_entry+1 : 0);
 
929
 
 
930
again:
 
931
    for (i=s; i<menu->entry_no; i++) {
 
932
        if (ch==toupper(menu->entries[i]->text[0])) {
 
933
            return i;
 
934
        }
 
935
    }
 
936
    /* no match. Retry from start, if previous started from a selected entry */
 
937
    if (s!=0) {
 
938
        s = 0;
 
939
        goto again;
 
940
    }
 
941
    return -1;
 
942
}
 
943
 
 
944
 
 
945
static int
 
946
keyboardMenu(WMenu *menu)
 
947
{
 
948
    XEvent event;
 
949
    KeySym ksym=NoSymbol;
 
950
    int done=0;
 
951
    int index;
 
952
    WMenuEntry *entry;
 
953
    int old_pos_x = menu->frame_x;
 
954
    int old_pos_y = menu->frame_y;
 
955
    int new_x = old_pos_x, new_y = old_pos_y;
 
956
    WMRect rect = wGetRectForHead(menu->frame->screen_ptr,
 
957
                                  wGetHeadForPointerLocation(menu->frame->screen_ptr));
 
958
 
 
959
    if (menu->flags.editing)
 
960
        return False;
 
961
 
 
962
 
 
963
    XGrabKeyboard(dpy, menu->frame->core->window, True, GrabModeAsync,
 
964
                  GrabModeAsync, CurrentTime);
 
965
 
 
966
 
 
967
    if (menu->frame_y+menu->frame->top_width >= rect.pos.y + rect.size.height)
 
968
        new_y = rect.pos.y + rect.size.height - menu->frame->top_width;
 
969
 
 
970
    if (menu->frame_x+MENUW(menu) >= rect.pos.x + rect.size.width)
 
971
        new_x = rect.pos.x + rect.size.width - MENUW(menu) - 1;
 
972
 
 
973
    move_menus(menu, new_x, new_y);
 
974
 
 
975
    while (!done && menu->flags.mapped) {
 
976
        XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
 
977
        WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonPressMask
 
978
                    |ButtonReleaseMask|KeyPressMask|KeyReleaseMask
 
979
                    |SubstructureNotifyMask, &event);
 
980
 
 
981
        switch (event.type) {
 
982
        case KeyPress:
 
983
            ksym = XLookupKeysym(&event.xkey, 0);
 
984
            switch (ksym) {
 
985
            case XK_Escape:
 
986
                done = 1;
 
987
                break;
 
988
 
 
989
            case XK_Home:
 
990
#ifdef XK_KP_Home
 
991
            case XK_KP_Home:
 
992
#endif
 
993
                selectEntry(menu, 0);
 
994
                makeVisible(menu);
 
995
                break;
 
996
 
 
997
            case XK_End:
 
998
#ifdef XK_KP_End
 
999
            case XK_KP_End:
 
1000
#endif
 
1001
                selectEntry(menu, menu->entry_no-1);
 
1002
                makeVisible(menu);
 
1003
                break;
 
1004
 
 
1005
            case XK_Up:
 
1006
#ifdef ARROWLESS_KBD
 
1007
            case XK_k:
 
1008
#endif
 
1009
#ifdef XK_KP_Up
 
1010
            case XK_KP_Up:
 
1011
#endif
 
1012
                if (menu->selected_entry <= 0)
 
1013
                    selectEntry(menu, menu->entry_no-1);
 
1014
                else
 
1015
                    selectEntry(menu, menu->selected_entry-1);
 
1016
                makeVisible(menu);
 
1017
                break;
 
1018
 
 
1019
            case XK_Down:
 
1020
#ifdef ARROWLESS_KBD
 
1021
            case XK_j:
 
1022
#endif
 
1023
#ifdef XK_KP_Down
 
1024
            case XK_KP_Down:
 
1025
#endif
 
1026
                if (menu->selected_entry<0)
 
1027
                    selectEntry(menu, 0);
 
1028
                else if (menu->selected_entry == menu->entry_no-1)
 
1029
                    selectEntry(menu, 0);
 
1030
                else if (menu->selected_entry < menu->entry_no-1)
 
1031
                    selectEntry(menu, menu->selected_entry+1);
 
1032
                makeVisible(menu);
 
1033
                break;
 
1034
 
 
1035
            case XK_Right:
 
1036
#ifdef ARROWLESS_KBD
 
1037
            case XK_l:
 
1038
#endif
 
1039
#ifdef XK_KP_Right
 
1040
            case XK_KP_Right:
 
1041
#endif
 
1042
                if (menu->selected_entry>=0) {
 
1043
                    WMenuEntry *entry;
 
1044
                    entry = menu->entries[menu->selected_entry];
 
1045
 
 
1046
                    if (entry->cascade >= 0 && menu->cascades
 
1047
                        && menu->cascades[entry->cascade]->entry_no > 0) {
 
1048
 
 
1049
                        XUngrabKeyboard(dpy, CurrentTime);
 
1050
 
 
1051
                        selectEntry(menu->cascades[entry->cascade], 0);
 
1052
                        if (!keyboardMenu(menu->cascades[entry->cascade]))
 
1053
                            done = 1;
 
1054
 
 
1055
                        XGrabKeyboard(dpy, menu->frame->core->window, True,
 
1056
                                      GrabModeAsync, GrabModeAsync,
 
1057
                                      CurrentTime);
 
1058
                    }
 
1059
                }
 
1060
                break;
 
1061
 
 
1062
            case XK_Left:
 
1063
#ifdef ARROWLESS_KBD
 
1064
            case XK_h:
 
1065
#endif
 
1066
#ifdef XK_KP_Left
 
1067
            case XK_KP_Left:
 
1068
#endif
 
1069
                if (menu->parent!=NULL && menu->parent->selected_entry>=0) {
 
1070
                    selectEntry(menu, -1);
 
1071
                    move_menus(menu, old_pos_x, old_pos_y);
 
1072
                    return True;
 
1073
                }
 
1074
                break;
 
1075
 
 
1076
            case XK_Return:
 
1077
                done = 2;
 
1078
                break;
 
1079
 
 
1080
            default:
 
1081
                index = check_key(menu, &event.xkey);
 
1082
                if (index>=0) {
 
1083
                    selectEntry(menu, index);
 
1084
                }
 
1085
            }
 
1086
            break;
 
1087
 
 
1088
        default:
 
1089
            if (event.type==ButtonPress)
 
1090
                done = 1;
 
1091
 
 
1092
            WMHandleEvent(&event);
 
1093
        }
 
1094
    }
 
1095
 
 
1096
    XUngrabKeyboard(dpy, CurrentTime);
 
1097
 
 
1098
    if (done==2 && menu->selected_entry>=0) {
 
1099
        entry = menu->entries[menu->selected_entry];
 
1100
    } else {
 
1101
        entry = NULL;
 
1102
    }
 
1103
 
 
1104
    if (entry && entry->callback!=NULL && entry->flags.enabled
 
1105
        && entry->cascade < 0) {
 
1106
#if (MENU_BLINK_COUNT > 0)
 
1107
        int sel = menu->selected_entry;
 
1108
        int i;
 
1109
 
 
1110
        for (i=0; i<MENU_BLINK_COUNT; i++) {
 
1111
            paintEntry(menu, sel, False);
 
1112
            XSync(dpy, 0);
 
1113
            wusleep(MENU_BLINK_DELAY);
 
1114
            paintEntry(menu, sel, True);
 
1115
            XSync(dpy, 0);
 
1116
            wusleep(MENU_BLINK_DELAY);
 
1117
        }
 
1118
#endif
 
1119
        selectEntry(menu, -1);
 
1120
 
 
1121
        if (!menu->flags.buttoned) {
 
1122
            wMenuUnmap(menu);
 
1123
            move_menus(menu, old_pos_x, old_pos_y);
 
1124
        }
 
1125
        closeCascade(menu);
 
1126
 
 
1127
        (*entry->callback)(menu, entry);
 
1128
    } else {
 
1129
        if (!menu->flags.buttoned) {
 
1130
            wMenuUnmap(menu);
 
1131
            move_menus(menu, old_pos_x, old_pos_y);
 
1132
        }
 
1133
        selectEntry(menu, -1);
 
1134
    }
 
1135
 
 
1136
 
 
1137
    /* returns True if returning from a submenu to a parent menu,
 
1138
     * False if exiting from menu */
 
1139
    return False;
 
1140
}
 
1141
 
 
1142
 
 
1143
void
 
1144
wMenuMapAt(WMenu *menu, int x, int y, int keyboard)
 
1145
{
 
1146
    WMRect rect = wGetRectForHead(menu->frame->screen_ptr,
 
1147
                                  wGetHeadForPointerLocation(menu->frame->screen_ptr));
 
1148
 
 
1149
    if (!menu->flags.realized) {
 
1150
        menu->flags.realized=1;
 
1151
        wMenuRealize(menu);
 
1152
    }
 
1153
    if (!menu->flags.mapped) {
 
1154
        if (wPreferences.wrap_menus) {
 
1155
            if (x<rect.pos.x) x = rect.pos.x;
 
1156
            if (y<rect.pos.y) y = rect.pos.y;
 
1157
            if (x+MENUW(menu) > rect.pos.x + rect.size.width)
 
1158
                x = rect.pos.x + rect.size.width - MENUW(menu);
 
1159
            if (y+MENUH(menu) > rect.pos.y + rect.size.height)
 
1160
                y = rect.pos.y + rect.size.height - MENUH(menu);
 
1161
        }
 
1162
 
 
1163
        XMoveWindow(dpy, menu->frame->core->window, x, y);
 
1164
        menu->frame_x = x;
 
1165
        menu->frame_y = y;
 
1166
        XMapWindow(dpy, menu->frame->core->window);
 
1167
        wRaiseFrame(menu->frame->core);
 
1168
        menu->flags.mapped = 1;
 
1169
    } else {
 
1170
        selectEntry(menu, 0);
 
1171
    }
 
1172
 
 
1173
    if (keyboard)
 
1174
        keyboardMenu(menu);
 
1175
}
 
1176
 
 
1177
 
 
1178
void
 
1179
wMenuMap(WMenu *menu)
 
1180
{
 
1181
    if (!menu->flags.realized) {
 
1182
        menu->flags.realized=1;
 
1183
        wMenuRealize(menu);
 
1184
    }
 
1185
    if (menu->flags.app_menu && menu->parent==NULL) {
 
1186
        menu->frame_x = menu->frame->screen_ptr->app_menu_x;
 
1187
        menu->frame_y = menu->frame->screen_ptr->app_menu_y;
 
1188
        XMoveWindow(dpy, menu->frame->core->window, menu->frame_x, menu->frame_y);
 
1189
    }
 
1190
    XMapWindow(dpy, menu->frame->core->window);
 
1191
    wRaiseFrame(menu->frame->core);
 
1192
    menu->flags.mapped = 1;
 
1193
}
 
1194
 
 
1195
 
 
1196
void
 
1197
wMenuUnmap(WMenu *menu)
 
1198
{
 
1199
    int i;
 
1200
 
 
1201
    XUnmapWindow(dpy, menu->frame->core->window);
 
1202
    if (menu->flags.titled && menu->flags.buttoned) {
 
1203
        wFrameWindowHideButton(menu->frame, WFF_RIGHT_BUTTON);
 
1204
    }
 
1205
    menu->flags.buttoned = 0;
 
1206
    menu->flags.mapped = 0;
 
1207
    menu->flags.open_to_left = 0;
 
1208
 
 
1209
    for (i=0; i<menu->cascade_no; i++) {
 
1210
        if (menu->cascades[i]!=NULL
 
1211
            && menu->cascades[i]->flags.mapped
 
1212
            && !menu->cascades[i]->flags.buttoned) {
 
1213
 
 
1214
            wMenuUnmap(menu->cascades[i]);
 
1215
        }
 
1216
    }
 
1217
    menu->selected_entry = -1;
 
1218
}
 
1219
 
 
1220
 
 
1221
 
 
1222
void
 
1223
wMenuPaint(WMenu *menu)
 
1224
{
 
1225
    int i;
 
1226
 
 
1227
    if (!menu->flags.mapped) {
 
1228
        return;
 
1229
    }
 
1230
 
 
1231
    /* paint entries */
 
1232
    for (i=0; i<menu->entry_no; i++) {
 
1233
        paintEntry(menu, i, i==menu->selected_entry);
 
1234
    }
 
1235
}
 
1236
 
 
1237
 
 
1238
void
 
1239
wMenuSetEnabled(WMenu *menu, int index, int enable)
 
1240
{
 
1241
    if (index>=menu->entry_no) return;
 
1242
    menu->entries[index]->flags.enabled=enable;
 
1243
    paintEntry(menu, index, index==menu->selected_entry);
 
1244
    paintEntry(menu->brother, index, index==menu->selected_entry);
 
1245
}
 
1246
 
 
1247
 
 
1248
/* ====================================================================== */
 
1249
 
 
1250
 
 
1251
static void
 
1252
editEntry(WMenu *menu, WMenuEntry *entry)
 
1253
{
 
1254
    WTextInput *text;
 
1255
    XEvent event;
 
1256
    WObjDescriptor *desc;
 
1257
    char *t;
 
1258
    int done = 0;
 
1259
    Window old_focus;
 
1260
    int old_revert;
 
1261
 
 
1262
    menu->flags.editing = 1;
 
1263
 
 
1264
    text = wTextCreate(menu->menu, 1, menu->entry_height * entry->order,
 
1265
                       menu->menu->width - 2, menu->entry_height - 1);
 
1266
 
 
1267
    wTextPutText(text, entry->text);
 
1268
    XGetInputFocus(dpy, &old_focus, &old_revert);
 
1269
    XSetInputFocus(dpy, text->core->window, RevertToNone, CurrentTime);
 
1270
 
 
1271
    if (XGrabKeyboard(dpy, text->core->window, True, GrabModeAsync,
 
1272
                      GrabModeAsync, CurrentTime)!=GrabSuccess) {
 
1273
        wwarning(_("could not grab keyboard"));
 
1274
        wTextDestroy(text);
 
1275
 
 
1276
        wSetFocusTo(menu->frame->screen_ptr,
 
1277
                    menu->frame->screen_ptr->focused_window);
 
1278
        return;
 
1279
    }
 
1280
 
 
1281
 
 
1282
    while (!done && !text->done) {
 
1283
        XSync(dpy, 0);
 
1284
        XAllowEvents(dpy, AsyncKeyboard|AsyncPointer, CurrentTime);
 
1285
        XSync(dpy, 0);
 
1286
        WMNextEvent(dpy, &event);
 
1287
 
 
1288
        if (XFindContext(dpy, event.xany.window, wWinContext,
 
1289
                         (XPointer *)&desc)==XCNOENT)
 
1290
            desc = NULL;
 
1291
 
 
1292
        if ((desc != NULL) && (desc->handle_anything != NULL)) {
 
1293
 
 
1294
            (*desc->handle_anything)(desc, &event);
 
1295
 
 
1296
        } else {
 
1297
            switch (event.type) {
 
1298
            case ButtonPress:
 
1299
                XAllowEvents(dpy, ReplayPointer, CurrentTime);
 
1300
                done = 1;
 
1301
 
 
1302
            default:
 
1303
                WMHandleEvent(&event);
 
1304
                break;
 
1305
            }
 
1306
        }
 
1307
    }
 
1308
 
 
1309
    XSetInputFocus(dpy, old_focus, old_revert, CurrentTime);
 
1310
 
 
1311
    wSetFocusTo(menu->frame->screen_ptr,
 
1312
                menu->frame->screen_ptr->focused_window);
 
1313
 
 
1314
 
 
1315
    t = wTextGetText(text);
 
1316
    /* if !t, the user has canceled editing */
 
1317
    if (t) {
 
1318
        if (entry->text)
 
1319
            wfree(entry->text);
 
1320
        entry->text = wstrdup(t);
 
1321
 
 
1322
        menu->flags.realized = 0;
 
1323
    }
 
1324
    wTextDestroy(text);
 
1325
 
 
1326
    XUngrabKeyboard(dpy, CurrentTime);
 
1327
 
 
1328
    if (t && menu->on_edit)
 
1329
        (*menu->on_edit)(menu, entry);
 
1330
 
 
1331
    menu->flags.editing = 0;
 
1332
 
 
1333
    if (!menu->flags.realized)
 
1334
        wMenuRealize(menu);
 
1335
}
 
1336
 
 
1337
 
 
1338
static void
 
1339
selectEntry(WMenu *menu, int entry_no)
 
1340
{
 
1341
    WMenuEntry *entry;
 
1342
    WMenu *submenu;
 
1343
    int old_entry;
 
1344
 
 
1345
    if (menu->entries==NULL)
 
1346
        return;
 
1347
 
 
1348
    if (entry_no >= menu->entry_no)
 
1349
        return;
 
1350
 
 
1351
    old_entry = menu->selected_entry;
 
1352
    menu->selected_entry = entry_no;
 
1353
 
 
1354
    if (old_entry!=entry_no) {
 
1355
 
 
1356
        /* unselect previous entry */
 
1357
        if (old_entry>=0) {
 
1358
            paintEntry(menu, old_entry, False);
 
1359
            entry = menu->entries[old_entry];
 
1360
 
 
1361
            /* unmap cascade */
 
1362
            if (entry->cascade>=0 && menu->cascades) {
 
1363
                if (!menu->cascades[entry->cascade]->flags.buttoned) {
 
1364
                    wMenuUnmap(menu->cascades[entry->cascade]);
 
1365
                }
 
1366
            }
 
1367
        }
 
1368
 
 
1369
        if (entry_no<0) {
 
1370
            menu->selected_entry = -1;
 
1371
            return;
 
1372
        }
 
1373
        entry = menu->entries[entry_no];
 
1374
 
 
1375
        if (entry->cascade>=0 && menu->cascades && entry->flags.enabled) {
 
1376
            /* Callback for when the submenu is opened.
 
1377
             */
 
1378
            submenu = menu->cascades[entry->cascade];
 
1379
            if (submenu && submenu->flags.brother)
 
1380
                submenu = submenu->brother;
 
1381
 
 
1382
            if (entry->callback) {
 
1383
                /* Only call the callback if the submenu is not yet mapped.
 
1384
                 */
 
1385
                if (menu->flags.brother) {
 
1386
                    if (!submenu || !submenu->flags.mapped)
 
1387
                        (*entry->callback)(menu->brother, entry);
 
1388
                } else {
 
1389
                    if (!submenu || !submenu->flags.buttoned)
 
1390
                        (*entry->callback)(menu, entry);
 
1391
                }
 
1392
            }
 
1393
 
 
1394
            /* the submenu menu might have changed */
 
1395
            submenu = menu->cascades[entry->cascade];
 
1396
 
 
1397
            /* map cascade */
 
1398
            if (!submenu->flags.mapped) {
 
1399
                int x, y;
 
1400
 
 
1401
                if (!submenu->flags.realized)
 
1402
                    wMenuRealize(submenu);
 
1403
                if (wPreferences.wrap_menus) {
 
1404
                    if (menu->flags.open_to_left)
 
1405
                        submenu->flags.open_to_left = 1;
 
1406
 
 
1407
                    if (submenu->flags.open_to_left) {
 
1408
                        x = menu->frame_x - MENUW(submenu);
 
1409
                        if (x<0) {
 
1410
                            x = 0;
 
1411
                            submenu->flags.open_to_left = 0;
 
1412
                        }
 
1413
                    } else {
 
1414
                        x = menu->frame_x + MENUW(menu);
 
1415
 
 
1416
                        if (x + MENUW(submenu)
 
1417
                            >= menu->frame->screen_ptr->scr_width) {
 
1418
 
 
1419
                            x = menu->frame_x - MENUW(submenu);
 
1420
                            submenu->flags.open_to_left = 1;
 
1421
                        }
 
1422
                    }
 
1423
                } else {
 
1424
                    x = menu->frame_x + MENUW(menu);
 
1425
                }
 
1426
 
 
1427
                if (wPreferences.align_menus) {
 
1428
                    y = menu->frame_y;
 
1429
                } else {
 
1430
                    y = menu->frame_y + menu->entry_height*entry_no;
 
1431
                    if (menu->flags.titled)
 
1432
                        y += menu->frame->top_width;
 
1433
                    if (menu->cascades[entry->cascade]->flags.titled)
 
1434
                        y -= menu->cascades[entry->cascade]->frame->top_width;
 
1435
                }
 
1436
 
 
1437
                wMenuMapAt(menu->cascades[entry->cascade], x, y, False);
 
1438
                menu->cascades[entry->cascade]->parent = menu;
 
1439
            } else {
 
1440
                return;
 
1441
            }
 
1442
        }
 
1443
        paintEntry(menu, entry_no, True);
 
1444
    }
 
1445
}
 
1446
 
 
1447
 
 
1448
static WMenu*
 
1449
findMenu(WScreen *scr, int *x_ret, int *y_ret)
 
1450
{
 
1451
    WMenu *menu;
 
1452
    WObjDescriptor *desc;
 
1453
    Window root_ret, win, junk_win;
 
1454
    int x, y, wx, wy;
 
1455
    unsigned int mask;
 
1456
 
 
1457
    XQueryPointer(dpy, scr->root_win, &root_ret, &win, &x, &y, &wx, &wy,
 
1458
                  &mask);
 
1459
 
 
1460
    if (win==None) return NULL;
 
1461
 
 
1462
    if (XFindContext(dpy, win, wWinContext, (XPointer *)&desc)==XCNOENT)
 
1463
        return NULL;
 
1464
 
 
1465
    if (desc->parent_type == WCLASS_MENU) {
 
1466
        menu = (WMenu*)desc->parent;
 
1467
        XTranslateCoordinates(dpy, root_ret, menu->menu->window, wx, wy,
 
1468
                              x_ret, y_ret, &junk_win);
 
1469
        return menu;
 
1470
    }
 
1471
    return NULL;
 
1472
}
 
1473
 
 
1474
 
 
1475
 
 
1476
 
 
1477
static void
 
1478
closeCascade(WMenu *menu)
 
1479
{
 
1480
    WMenu *parent=menu->parent;
 
1481
 
 
1482
    if (menu->flags.brother
 
1483
        || (!menu->flags.buttoned
 
1484
            && (!menu->flags.app_menu||menu->parent!=NULL))) {
 
1485
 
 
1486
        selectEntry(menu, -1);
 
1487
        XSync(dpy, 0);
 
1488
#if (MENU_BLINK_DELAY > 2)
 
1489
        wusleep(MENU_BLINK_DELAY/2);
 
1490
#endif
 
1491
        wMenuUnmap(menu);
 
1492
        while (parent!=NULL
 
1493
               && (parent->parent!=NULL || !parent->flags.app_menu
 
1494
                   || parent->flags.brother)
 
1495
               && !parent->flags.buttoned) {
 
1496
            selectEntry(parent, -1);
 
1497
            wMenuUnmap(parent);
 
1498
            parent = parent->parent;
 
1499
        }
 
1500
        if (parent)
 
1501
            selectEntry(parent, -1);
 
1502
    }
 
1503
}
 
1504
 
 
1505
 
 
1506
static void
 
1507
closeBrotherCascadesOf(WMenu *menu)
 
1508
{
 
1509
    WMenu *tmp;
 
1510
    int i;
 
1511
 
 
1512
    for (i=0; i<menu->cascade_no; i++) {
 
1513
        if (menu->cascades[i]->flags.brother) {
 
1514
            tmp = menu->cascades[i];
 
1515
        } else {
 
1516
            tmp = menu->cascades[i]->brother;
 
1517
        }
 
1518
        if (tmp->flags.mapped) {
 
1519
            selectEntry(tmp->parent, -1);
 
1520
            closeBrotherCascadesOf(tmp);
 
1521
            break;
 
1522
        }
 
1523
    }
 
1524
}
 
1525
 
 
1526
 
 
1527
#define getEntryAt(menu, x, y)   ((y)<0 ? -1 : (y)/(menu->entry_height))
 
1528
 
 
1529
 
 
1530
static WMenu*
 
1531
parentMenu(WMenu *menu)
 
1532
{
 
1533
    WMenu *parent;
 
1534
    WMenuEntry *entry;
 
1535
 
 
1536
    if (menu->flags.buttoned)
 
1537
        return menu;
 
1538
 
 
1539
    while (menu->parent && menu->parent->flags.mapped) {
 
1540
        parent = menu->parent;
 
1541
        if (parent->selected_entry < 0)
 
1542
            break;
 
1543
        entry = parent->entries[parent->selected_entry];
 
1544
        if (!entry->flags.enabled || entry->cascade<0 || !parent->cascades ||
 
1545
            parent->cascades[entry->cascade] != menu)
 
1546
            break;
 
1547
        menu = parent;
 
1548
        if (menu->flags.buttoned)
 
1549
            break;
 
1550
    }
 
1551
 
 
1552
    return menu;
 
1553
}
 
1554
 
 
1555
 
 
1556
 
 
1557
/*
 
1558
 * Will raise the passed menu, if submenu = 0
 
1559
 * If submenu > 0 will also raise all mapped submenus
 
1560
 * until the first buttoned one
 
1561
 * If submenu < 0 will also raise all mapped parent menus
 
1562
 * until the first buttoned one
 
1563
 */
 
1564
 
 
1565
static void
 
1566
raiseMenus(WMenu *menu, int submenus)
 
1567
{
 
1568
    WMenu *submenu;
 
1569
    int i;
 
1570
 
 
1571
    if(!menu) return;
 
1572
 
 
1573
    wRaiseFrame(menu->frame->core);
 
1574
 
 
1575
    if (submenus>0 && menu->selected_entry>=0) {
 
1576
        i = menu->entries[menu->selected_entry]->cascade;
 
1577
        if (i>=0 && menu->cascades) {
 
1578
            submenu = menu->cascades[i];
 
1579
            if (submenu->flags.mapped && !submenu->flags.buttoned)
 
1580
                raiseMenus(submenu, submenus);
 
1581
        }
 
1582
    }
 
1583
    if (submenus<0 && !menu->flags.buttoned &&
 
1584
        menu->parent && menu->parent->flags.mapped)
 
1585
        raiseMenus(menu->parent, submenus);
 
1586
}
 
1587
 
 
1588
 
 
1589
WMenu*
 
1590
wMenuUnderPointer(WScreen *screen)
 
1591
{
 
1592
    WObjDescriptor *desc;
 
1593
    Window root_ret, win;
 
1594
    int dummy;
 
1595
    unsigned int mask;
 
1596
 
 
1597
    XQueryPointer(dpy, screen->root_win, &root_ret, &win, &dummy, &dummy,
 
1598
                  &dummy, &dummy, &mask);
 
1599
 
 
1600
    if (win==None) return NULL;
 
1601
 
 
1602
    if (XFindContext(dpy, win, wWinContext, (XPointer *)&desc)==XCNOENT)
 
1603
        return NULL;
 
1604
 
 
1605
    if (desc->parent_type == WCLASS_MENU)
 
1606
        return (WMenu *)desc->parent;
 
1607
    return NULL;
 
1608
}
 
1609
 
 
1610
 
 
1611
 
 
1612
 
 
1613
static void
 
1614
getPointerPosition(WScreen *scr, int *x, int *y)
 
1615
{
 
1616
    Window root_ret, win;
 
1617
    int wx, wy;
 
1618
    unsigned int mask;
 
1619
 
 
1620
    XQueryPointer(dpy, scr->root_win, &root_ret, &win, x, y, &wx, &wy, &mask);
 
1621
}
 
1622
 
 
1623
 
 
1624
static void
 
1625
getScrollAmount(WMenu *menu, int *hamount, int *vamount)
 
1626
{
 
1627
    WScreen *scr = menu->menu->screen_ptr;
 
1628
    int menuX1 = menu->frame_x;
 
1629
    int menuY1 = menu->frame_y;
 
1630
    int menuX2 = menu->frame_x + MENUW(menu);
 
1631
    int menuY2 = menu->frame_y + MENUH(menu);
 
1632
    int xroot, yroot;
 
1633
    WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
 
1634
 
 
1635
 
 
1636
    *hamount = 0;
 
1637
    *vamount = 0;
 
1638
 
 
1639
    getPointerPosition(scr, &xroot, &yroot);
 
1640
 
 
1641
    if (xroot <= (rect.pos.x + 1) && menuX1 < rect.pos.x) {
 
1642
        /* scroll to the right */
 
1643
        *hamount = WMIN(MENU_SCROLL_STEP, abs(menuX1));
 
1644
 
 
1645
    } else if (xroot >= (rect.pos.x + rect.size.width - 2) &&
 
1646
               menuX2 > (rect.pos.x + rect.size.width - 1)) {
 
1647
        /* scroll to the left */
 
1648
        *hamount = WMIN(MENU_SCROLL_STEP, abs(menuX2-rect.pos.x-rect.size.width-1));
 
1649
 
 
1650
        if (*hamount==0)
 
1651
            *hamount = 1;
 
1652
 
 
1653
        *hamount = -*hamount;
 
1654
    }
 
1655
 
 
1656
    if (yroot <= (rect.pos.y + 1) && menuY1 < rect.pos.y) {
 
1657
        /* scroll down */
 
1658
        *vamount = WMIN(MENU_SCROLL_STEP, abs(menuY1));
 
1659
 
 
1660
    } else if (yroot >= (rect.pos.y + rect.size.height - 2) &&
 
1661
               menuY2 > (rect.pos.y + rect.size.height - 1)) {
 
1662
        /* scroll up */
 
1663
        *vamount = WMIN(MENU_SCROLL_STEP, abs(menuY2-rect.pos.y-rect.size.height-2));
 
1664
 
 
1665
        *vamount = -*vamount;
 
1666
    }
 
1667
}
 
1668
 
 
1669
 
 
1670
static void
 
1671
dragScrollMenuCallback(void *data)
 
1672
{
 
1673
    WMenu *menu = (WMenu*)data;
 
1674
    WScreen *scr = menu->menu->screen_ptr;
 
1675
    WMenu *parent = parentMenu(menu);
 
1676
    int hamount, vamount;
 
1677
    int x, y;
 
1678
    int newSelectedEntry;
 
1679
 
 
1680
    getScrollAmount(menu, &hamount, &vamount);
 
1681
 
 
1682
 
 
1683
    if (hamount != 0 || vamount != 0) {
 
1684
        wMenuMove(parent, parent->frame_x + hamount,
 
1685
                  parent->frame_y + vamount, True);
 
1686
        if (findMenu(scr, &x, &y)) {
 
1687
            newSelectedEntry = getEntryAt(menu, x, y);
 
1688
            selectEntry(menu, newSelectedEntry);
 
1689
        } else {
 
1690
            /* Pointer fell outside of menu. If the selected entry is
 
1691
             * not a submenu, unselect it */
 
1692
            if (menu->selected_entry >= 0
 
1693
                && menu->entries[menu->selected_entry]->cascade<0)
 
1694
                selectEntry(menu, -1);
 
1695
            newSelectedEntry = 0;
 
1696
        }
 
1697
 
 
1698
        /* paranoid check */
 
1699
        if (newSelectedEntry >= 0) {
 
1700
            /* keep scrolling */
 
1701
            menu->timer = WMAddTimerHandler(MENU_SCROLL_DELAY,
 
1702
                                            dragScrollMenuCallback, menu);
 
1703
        } else {
 
1704
            menu->timer = NULL;
 
1705
        }
 
1706
    } else {
 
1707
        /* don't need to scroll anymore */
 
1708
        menu->timer = NULL;
 
1709
        if (findMenu(scr, &x, &y)) {
 
1710
            newSelectedEntry = getEntryAt(menu, x, y);
 
1711
            selectEntry(menu, newSelectedEntry);
 
1712
        }
 
1713
    }
 
1714
}
 
1715
 
 
1716
 
 
1717
static void
 
1718
scrollMenuCallback(void *data)
 
1719
{
 
1720
    WMenu *menu = (WMenu*)data;
 
1721
    WMenu *parent = parentMenu(menu);
 
1722
    int hamount = 0;                   /* amount to scroll */
 
1723
    int vamount = 0;
 
1724
 
 
1725
#ifdef VIRTUAL_DESKTOP
 
1726
    /* don't scroll if it is in vdesk mode */
 
1727
    if (!wPreferences.vdesk_enable)
 
1728
#endif
 
1729
        getScrollAmount(menu, &hamount, &vamount);
 
1730
 
 
1731
    if (hamount != 0 || vamount != 0) {
 
1732
        wMenuMove(parent, parent->frame_x + hamount,
 
1733
                  parent->frame_y + vamount, True);
 
1734
 
 
1735
        /* keep scrolling */
 
1736
        menu->timer = WMAddTimerHandler(MENU_SCROLL_DELAY,
 
1737
                                        scrollMenuCallback, menu);
 
1738
    } else {
 
1739
        /* don't need to scroll anymore */
 
1740
        menu->timer = NULL;
 
1741
    }
 
1742
}
 
1743
 
 
1744
 
 
1745
 
 
1746
#define MENU_SCROLL_BORDER   5
 
1747
 
 
1748
static int
 
1749
isPointNearBoder(WMenu *menu, int x, int y)
 
1750
{
 
1751
    int menuX1 = menu->frame_x;
 
1752
    int menuY1 = menu->frame_y;
 
1753
    int menuX2 = menu->frame_x + MENUW(menu);
 
1754
    int menuY2 = menu->frame_y + MENUH(menu);
 
1755
    int flag = 0;
 
1756
    int head = wGetHeadForPoint(menu->frame->screen_ptr, wmkpoint(x, y));
 
1757
    WMRect rect = wGetRectForHead(menu->frame->screen_ptr, head);
 
1758
 
 
1759
    /* XXX: handle screen joins properly !! */
 
1760
 
 
1761
    if (x >= menuX1 && x <= menuX2 &&
 
1762
        (y < rect.pos.y + MENU_SCROLL_BORDER ||
 
1763
         y >= rect.pos.y + rect.size.height - MENU_SCROLL_BORDER))
 
1764
        flag = 1;
 
1765
    else if (y >= menuY1 && y <= menuY2 &&
 
1766
             (x < rect.pos.x + MENU_SCROLL_BORDER ||
 
1767
              x >= rect.pos.x + rect.size.width - MENU_SCROLL_BORDER))
 
1768
        flag = 1;
 
1769
 
 
1770
    return flag;
 
1771
}
 
1772
 
 
1773
 
 
1774
typedef struct _delay {
 
1775
    WMenu *menu;
 
1776
    int ox, oy;
 
1777
} _delay;
 
1778
 
 
1779
 
 
1780
static void
 
1781
leaving(_delay *dl)
 
1782
{
 
1783
    wMenuMove(dl->menu, dl->ox, dl->oy, True);
 
1784
    dl->menu->jump_back = NULL;
 
1785
    dl->menu->menu->screen_ptr->flags.jump_back_pending = 0;
 
1786
    wfree(dl);
 
1787
}
 
1788
 
 
1789
 
 
1790
void
 
1791
wMenuScroll(WMenu *menu, XEvent *event)
 
1792
{
 
1793
    WMenu *smenu;
 
1794
    WMenu *omenu = parentMenu(menu);
 
1795
    WScreen *scr = menu->frame->screen_ptr;
 
1796
    int done = 0;
 
1797
    int jump_back = 0;
 
1798
    int old_frame_x = omenu->frame_x;
 
1799
    int old_frame_y = omenu->frame_y;
 
1800
    XEvent ev;
 
1801
 
 
1802
    if (omenu->jump_back)
 
1803
        WMDeleteTimerWithClientData(omenu->jump_back);
 
1804
 
 
1805
 
 
1806
    if ((/*omenu->flags.buttoned &&*/ !wPreferences.wrap_menus)
 
1807
        || omenu->flags.app_menu) {
 
1808
        jump_back = 1;
 
1809
    }
 
1810
 
 
1811
    if (!wPreferences.wrap_menus)
 
1812
        raiseMenus(omenu, True);
 
1813
    else
 
1814
        raiseMenus(menu, False);
 
1815
 
 
1816
    if (!menu->timer)
 
1817
        scrollMenuCallback(menu);
 
1818
 
 
1819
    while(!done) {
 
1820
        int x, y, on_border, on_x_edge, on_y_edge, on_title;
 
1821
        WMRect rect;
 
1822
 
 
1823
        WMNextEvent(dpy, &ev);
 
1824
        switch (ev.type) {
 
1825
        case EnterNotify:
 
1826
            WMHandleEvent(&ev);
 
1827
        case MotionNotify:
 
1828
            x = (ev.type==MotionNotify) ? ev.xmotion.x_root : ev.xcrossing.x_root;
 
1829
            y = (ev.type==MotionNotify) ? ev.xmotion.y_root : ev.xcrossing.y_root;
 
1830
 
 
1831
            /* on_border is != 0 if the pointer is between the menu
 
1832
             * and the screen border and is close enough to the border */
 
1833
            on_border = isPointNearBoder(menu, x, y);
 
1834
 
 
1835
            smenu = wMenuUnderPointer(scr);
 
1836
 
 
1837
            if ((smenu==NULL && !on_border) || (smenu && parentMenu(smenu)!=omenu)) {
 
1838
                done = 1;
 
1839
                break;
 
1840
            }
 
1841
 
 
1842
            rect = wGetRectForHead(scr, wGetHeadForPoint(scr, wmkpoint(x, y)));
 
1843
            on_x_edge = x <= rect.pos.x + 1 || x >= rect.pos.x + rect.size.width - 2;
 
1844
            on_y_edge = y <= rect.pos.y + 1 || y >= rect.pos.y + rect.size.height - 2;
 
1845
            on_border = on_x_edge || on_y_edge;
 
1846
 
 
1847
            if (!on_border && !jump_back) {
 
1848
                done = 1;
 
1849
                break;
 
1850
            }
 
1851
 
 
1852
            if (menu->timer && (smenu!=menu || (!on_y_edge && !on_x_edge))) {
 
1853
                WMDeleteTimerHandler(menu->timer);
 
1854
                menu->timer = NULL;
 
1855
            }
 
1856
 
 
1857
            if (smenu != NULL)
 
1858
                menu = smenu;
 
1859
 
 
1860
            if (!menu->timer)
 
1861
                scrollMenuCallback(menu);
 
1862
            break;
 
1863
        case ButtonPress:
 
1864
            /* True if we push on title, or drag the omenu to other position */
 
1865
            on_title = ev.xbutton.x_root >= omenu->frame_x &&
 
1866
                ev.xbutton.x_root <= omenu->frame_x + MENUW(omenu) &&
 
1867
                ev.xbutton.y_root >= omenu->frame_y &&
 
1868
                ev.xbutton.y_root <= omenu->frame_y + omenu->frame->top_width;
 
1869
            WMHandleEvent(&ev);
 
1870
            smenu = wMenuUnderPointer(scr);
 
1871
            if (smenu == NULL || (smenu && smenu->flags.buttoned && smenu != omenu))
 
1872
                done = 1;
 
1873
            else if (smenu==omenu && on_title) {
 
1874
                jump_back = 0;
 
1875
                done = 1;
 
1876
            }
 
1877
            break;
 
1878
        case KeyPress:
 
1879
            done = 1;
 
1880
        default:
 
1881
            WMHandleEvent(&ev);
 
1882
            break;
 
1883
        }
 
1884
    }
 
1885
 
 
1886
    if (menu->timer) {
 
1887
        WMDeleteTimerHandler(menu->timer);
 
1888
        menu->timer = NULL;
 
1889
    }
 
1890
 
 
1891
    if (jump_back) {
 
1892
        _delay *delayer;
 
1893
        if (!omenu->jump_back) {
 
1894
            delayer = wmalloc(sizeof(_delay));
 
1895
            delayer->menu=omenu;
 
1896
            delayer->ox=old_frame_x;
 
1897
            delayer->oy=old_frame_y;
 
1898
            omenu->jump_back = delayer;
 
1899
            scr->flags.jump_back_pending = 1;
 
1900
        }
 
1901
        else delayer = omenu->jump_back;
 
1902
        WMAddTimerHandler(MENU_JUMP_BACK_DELAY,(WMCallback*)leaving, delayer);
 
1903
    }
 
1904
}
 
1905
 
 
1906
 
 
1907
 
 
1908
static void
 
1909
menuExpose(WObjDescriptor *desc, XEvent *event)
 
1910
{
 
1911
    wMenuPaint(desc->parent);
 
1912
}
 
1913
 
 
1914
typedef struct {
 
1915
    int *delayed_select;
 
1916
    WMenu *menu;
 
1917
    WMHandlerID magic;
 
1918
} delay_data;
 
1919
 
 
1920
 
 
1921
static void
 
1922
delaySelection(void *data)
 
1923
{
 
1924
    delay_data *d = (delay_data*)data;
 
1925
    int x, y, entry_no;
 
1926
    WMenu *menu;
 
1927
 
 
1928
    d->magic = NULL;
 
1929
 
 
1930
    menu = findMenu(d->menu->menu->screen_ptr, &x, &y);
 
1931
    if (menu && (d->menu == menu || d->delayed_select)) {
 
1932
        entry_no = getEntryAt(menu, x, y);
 
1933
        selectEntry(menu, entry_no);
 
1934
    }
 
1935
    if (d->delayed_select)
 
1936
        *(d->delayed_select) = 0;
 
1937
}
 
1938
 
 
1939
 
 
1940
static void
 
1941
menuMouseDown(WObjDescriptor *desc, XEvent *event)
 
1942
{
 
1943
    XButtonEvent *bev = &event->xbutton;
 
1944
    WMenu *menu = desc->parent;
 
1945
    WMenu *smenu;
 
1946
    WScreen *scr=menu->frame->screen_ptr;
 
1947
    WMenuEntry *entry=NULL;
 
1948
    XEvent ev;
 
1949
    int close_on_exit=0;
 
1950
    int done=0;
 
1951
    int delayed_select = 0;
 
1952
    int entry_no;
 
1953
    int x, y;
 
1954
    int prevx, prevy;
 
1955
    int old_frame_x = 0;
 
1956
    int old_frame_y = 0;
 
1957
    delay_data d_data = {NULL, NULL, NULL};
 
1958
 
 
1959
    /* Doesn't seem to be needed anymore (if delayed selection handler is
 
1960
     * added only if not present). there seem to be no other side effects
 
1961
     * from removing this and it is also possible that it was only added
 
1962
     * to avoid problems with adding the delayed selection timer handler
 
1963
     * multiple times
 
1964
     */
 
1965
    /*if (menu->flags.inside_handler) {
 
1966
     return;
 
1967
     }*/
 
1968
    menu->flags.inside_handler = 1;
 
1969
 
 
1970
    if (!wPreferences.wrap_menus) {
 
1971
        smenu = parentMenu(menu);
 
1972
        old_frame_x = smenu->frame_x;
 
1973
        old_frame_y = smenu->frame_y;
 
1974
    } else if (event->xbutton.window == menu->frame->core->window) {
 
1975
        /* This is true if the menu was launched with right click on root window */
 
1976
        if (!d_data.magic) {
 
1977
            delayed_select = 1;
 
1978
            d_data.delayed_select = &delayed_select;
 
1979
            d_data.menu = menu;
 
1980
            d_data.magic = WMAddTimerHandler(wPreferences.dblclick_time,
 
1981
                                             delaySelection, &d_data);
 
1982
        }
 
1983
    }
 
1984
 
 
1985
    wRaiseFrame(menu->frame->core);
 
1986
 
 
1987
    close_on_exit = (bev->send_event || menu->flags.brother);
 
1988
 
 
1989
    smenu = findMenu(scr, &x, &y);
 
1990
    if (!smenu) {
 
1991
        x = -1;
 
1992
        y = -1;
 
1993
    } else {
 
1994
        menu = smenu;
 
1995
    }
 
1996
 
 
1997
    if (menu->flags.editing) {
 
1998
        goto byebye;
 
1999
    }
 
2000
    entry_no = getEntryAt(menu, x, y);
 
2001
    if (entry_no>=0) {
 
2002
        entry = menu->entries[entry_no];
 
2003
 
 
2004
        if (!close_on_exit && (bev->state & ControlMask) && smenu
 
2005
            && entry->flags.editable) {
 
2006
            editEntry(smenu, entry);
 
2007
            goto byebye;
 
2008
        } else if (bev->state & ControlMask) {
 
2009
            goto byebye;
 
2010
        }
 
2011
 
 
2012
        if (entry->flags.enabled && entry->cascade>=0 && menu->cascades) {
 
2013
            WMenu *submenu = menu->cascades[entry->cascade];
 
2014
            /* map cascade */
 
2015
            if (submenu->flags.mapped && !submenu->flags.buttoned &&
 
2016
                menu->selected_entry!=entry_no) {
 
2017
                wMenuUnmap(submenu);
 
2018
            }
 
2019
            if (!submenu->flags.mapped && !delayed_select) {
 
2020
                selectEntry(menu, entry_no);
 
2021
            } else if (!submenu->flags.buttoned) {
 
2022
                selectEntry(menu, -1);
 
2023
            }
 
2024
 
 
2025
        } else if (!delayed_select) {
 
2026
            selectEntry(menu, entry_no);
 
2027
        }
 
2028
 
 
2029
        if (!wPreferences.wrap_menus && !wPreferences.scrollable_menus) {
 
2030
            if (!menu->timer)
 
2031
                dragScrollMenuCallback(menu);
 
2032
        }
 
2033
    }
 
2034
 
 
2035
#ifdef VIRTUAL_DESKTOP
 
2036
    if (wPreferences.vdesk_enable) {
 
2037
        wWorkspaceLowerEdge(scr);
 
2038
    }
 
2039
#endif
 
2040
 
 
2041
    prevx = bev->x_root;
 
2042
    prevy = bev->y_root;
 
2043
    while (!done) {
 
2044
        int x, y;
 
2045
 
 
2046
        XAllowEvents(dpy, AsyncPointer|SyncPointer, CurrentTime);
 
2047
 
 
2048
        WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
 
2049
                    |ButtonPressMask, &ev);
 
2050
        switch (ev.type) {
 
2051
        case MotionNotify:
 
2052
            smenu = findMenu(scr, &x, &y);
 
2053
 
 
2054
            if (smenu == NULL) {
 
2055
                /* moved mouse out of menu */
 
2056
 
 
2057
                if (!delayed_select && d_data.magic) {
 
2058
                    WMDeleteTimerHandler(d_data.magic);
 
2059
                    d_data.magic = NULL;
 
2060
                }
 
2061
                if (menu==NULL
 
2062
                    || (menu->selected_entry>=0
 
2063
                        && menu->entries[menu->selected_entry]->cascade>=0)) {
 
2064
                    prevx = ev.xmotion.x_root;
 
2065
                    prevy = ev.xmotion.y_root;
 
2066
 
 
2067
                    break;
 
2068
                }
 
2069
                selectEntry(menu, -1);
 
2070
                menu = smenu;
 
2071
                prevx = ev.xmotion.x_root;
 
2072
                prevy = ev.xmotion.y_root;
 
2073
                break;
 
2074
            } else if (menu && menu!=smenu
 
2075
                       && (menu->selected_entry<0
 
2076
                           || menu->entries[menu->selected_entry]->cascade<0)) {
 
2077
                selectEntry(menu, -1);
 
2078
 
 
2079
                if (!delayed_select && d_data.magic) {
 
2080
                    WMDeleteTimerHandler(d_data.magic);
 
2081
                    d_data.magic = NULL;
 
2082
                }
 
2083
            } else {
 
2084
 
 
2085
                /* hysteresis for item selection */
 
2086
 
 
2087
                /* check if the motion was to the side, indicating that
 
2088
                 * the user may want to cross to a submenu */
 
2089
                if (!delayed_select && menu) {
 
2090
                    int dx;
 
2091
                    Bool moved_to_submenu;/* moved to direction of submenu */
 
2092
 
 
2093
                    dx = abs(prevx - ev.xmotion.x_root);
 
2094
 
 
2095
                    moved_to_submenu = False;
 
2096
                    if (dx > 0 /* if moved enough to the side */
 
2097
                        /* maybe a open submenu */
 
2098
                        && menu->selected_entry>=0
 
2099
                        /* moving to the right direction */
 
2100
                        && (wPreferences.align_menus
 
2101
                            || ev.xmotion.y_root >= prevy)) {
 
2102
                        int index;
 
2103
 
 
2104
                        index = menu->entries[menu->selected_entry]->cascade;
 
2105
                        if (index>=0) {
 
2106
                            if (menu->cascades[index]->frame_x>menu->frame_x) {
 
2107
                                if (prevx < ev.xmotion.x_root)
 
2108
                                    moved_to_submenu = True;
 
2109
                            } else {
 
2110
                                if (prevx > ev.xmotion.x_root)
 
2111
                                    moved_to_submenu = True;
 
2112
                            }
 
2113
                        }
 
2114
                    }
 
2115
 
 
2116
 
 
2117
                    if (menu != smenu) {
 
2118
                        if (d_data.magic) {
 
2119
                            WMDeleteTimerHandler(d_data.magic);
 
2120
                            d_data.magic = NULL;
 
2121
                        }
 
2122
                    } else if (moved_to_submenu) {
 
2123
                        /* while we are moving, postpone the selection */
 
2124
                        if (d_data.magic) {
 
2125
                            WMDeleteTimerHandler(d_data.magic);
 
2126
                        }
 
2127
                        d_data.delayed_select = NULL;
 
2128
                        d_data.menu = menu;
 
2129
                        d_data.magic = WMAddTimerHandler(MENU_SELECT_DELAY,
 
2130
                                                         delaySelection,
 
2131
                                                         &d_data);
 
2132
                        prevx = ev.xmotion.x_root;
 
2133
                        prevy = ev.xmotion.y_root;
 
2134
                        break;
 
2135
                    } else {
 
2136
                        if (d_data.magic) {
 
2137
                            WMDeleteTimerHandler(d_data.magic);
 
2138
                            d_data.magic = NULL;
 
2139
                        }
 
2140
                    }
 
2141
                }
 
2142
            }
 
2143
            prevx = ev.xmotion.x_root;
 
2144
            prevy = ev.xmotion.y_root;
 
2145
            if (menu!=smenu) {
 
2146
                /* pointer crossed menus */
 
2147
                if (menu && menu->timer) {
 
2148
                    WMDeleteTimerHandler(menu->timer);
 
2149
                    menu->timer = NULL;
 
2150
                }
 
2151
                if (smenu)
 
2152
                    dragScrollMenuCallback(smenu);
 
2153
            }
 
2154
            menu = smenu;
 
2155
            if (!menu->timer)
 
2156
                dragScrollMenuCallback(menu);
 
2157
 
 
2158
            if (!delayed_select) {
 
2159
                entry_no = getEntryAt(menu, x, y);
 
2160
                if (entry_no>=0) {
 
2161
                    entry = menu->entries[entry_no];
 
2162
                    if (entry->flags.enabled && entry->cascade>=0 &&
 
2163
                        menu->cascades) {
 
2164
                        WMenu *submenu = menu->cascades[entry->cascade];
 
2165
                        if (submenu->flags.mapped && !submenu->flags.buttoned
 
2166
                            && menu->selected_entry!=entry_no) {
 
2167
                            wMenuUnmap(submenu);
 
2168
                        }
 
2169
                    }
 
2170
                }
 
2171
                selectEntry(menu, entry_no);
 
2172
            }
 
2173
            break;
 
2174
 
 
2175
        case ButtonPress:
 
2176
            break;
 
2177
 
 
2178
        case ButtonRelease:
 
2179
            if (ev.xbutton.button == event->xbutton.button)
 
2180
                done=1;
 
2181
            break;
 
2182
 
 
2183
        case Expose:
 
2184
            WMHandleEvent(&ev);
 
2185
#ifdef VIRTUAL_DESKTOP
 
2186
            /* since expose will raise edge up.. I need another ugly hack here */
 
2187
            if (wPreferences.vdesk_enable) {
 
2188
                wWorkspaceLowerEdge(scr);
 
2189
            }
 
2190
#endif
 
2191
            break;
 
2192
        }
 
2193
    }
 
2194
 
 
2195
    if (menu && menu->timer) {
 
2196
        WMDeleteTimerHandler(menu->timer);
 
2197
        menu->timer = NULL;
 
2198
    }
 
2199
    if (d_data.magic!=NULL) {
 
2200
        WMDeleteTimerHandler(d_data.magic);
 
2201
        d_data.magic = NULL;
 
2202
    }
 
2203
 
 
2204
    if (menu && menu->selected_entry>=0) {
 
2205
        entry = menu->entries[menu->selected_entry];
 
2206
        if (entry->callback!=NULL && entry->flags.enabled
 
2207
            && entry->cascade < 0) {
 
2208
            /* blink and erase menu selection */
 
2209
#if (MENU_BLINK_DELAY > 0)
 
2210
            int sel = menu->selected_entry;
 
2211
            int i;
 
2212
 
 
2213
            for (i=0; i<MENU_BLINK_COUNT; i++) {
 
2214
                paintEntry(menu, sel, False);
 
2215
                XSync(dpy, 0);
 
2216
                wusleep(MENU_BLINK_DELAY);
 
2217
                paintEntry(menu, sel, True);
 
2218
                XSync(dpy, 0);
 
2219
                wusleep(MENU_BLINK_DELAY);
 
2220
            }
 
2221
#endif
 
2222
            /* unmap the menu, it's parents and call the callback */
 
2223
            if (!menu->flags.buttoned &&
 
2224
                (!menu->flags.app_menu||menu->parent!=NULL)) {
 
2225
                closeCascade(menu);
 
2226
            } else {
 
2227
                selectEntry(menu, -1);
 
2228
            }
 
2229
            (*entry->callback)(menu, entry);
 
2230
 
 
2231
            /* If the user double clicks an entry, the entry will
 
2232
             * be executed twice, which is not good for things like
 
2233
             * the root menu. So, ignore any clicks that were generated
 
2234
             * while the entry was being executed */
 
2235
            while (XCheckTypedWindowEvent(dpy, menu->menu->window,
 
2236
                                          ButtonPress, &ev));
 
2237
        } else if (entry->callback!=NULL && entry->cascade<0) {
 
2238
            selectEntry(menu, -1);
 
2239
        } else {
 
2240
            if (entry->cascade>=0 && menu->cascades
 
2241
                && menu->cascades[entry->cascade]->flags.brother) {
 
2242
                selectEntry(menu, -1);
 
2243
            }
 
2244
        }
 
2245
    }
 
2246
 
 
2247
    if (((WMenu*)desc->parent)->flags.brother || close_on_exit || !smenu)
 
2248
        closeCascade(desc->parent);
 
2249
 
 
2250
    /* close the cascade windows that should not remain opened */
 
2251
    closeBrotherCascadesOf(desc->parent);
 
2252
 
 
2253
    if (!wPreferences.wrap_menus)
 
2254
        wMenuMove(parentMenu(desc->parent), old_frame_x, old_frame_y, True);
 
2255
 
 
2256
byebye:
 
2257
    /* Just to be sure in case we skip the 2 above because of a goto byebye */
 
2258
    if (menu && menu->timer) {
 
2259
        WMDeleteTimerHandler(menu->timer);
 
2260
        menu->timer = NULL;
 
2261
    }
 
2262
    if (d_data.magic!=NULL) {
 
2263
        WMDeleteTimerHandler(d_data.magic);
 
2264
        d_data.magic = NULL;
 
2265
    }
 
2266
 
 
2267
    ((WMenu*)desc->parent)->flags.inside_handler = 0;
 
2268
#ifdef VIRTUAL_DESKTOP
 
2269
    wWorkspaceRaiseEdge(scr);
 
2270
#endif
 
2271
}
 
2272
 
 
2273
 
 
2274
void
 
2275
wMenuMove(WMenu *menu, int x, int y, int submenus)
 
2276
{
 
2277
    WMenu *submenu;
 
2278
    int i;
 
2279
 
 
2280
    if (!menu) return;
 
2281
 
 
2282
    menu->frame_x = x;
 
2283
    menu->frame_y = y;
 
2284
    XMoveWindow(dpy, menu->frame->core->window, x, y);
 
2285
 
 
2286
    if (submenus>0 && menu->selected_entry>=0) {
 
2287
        i = menu->entries[menu->selected_entry]->cascade;
 
2288
 
 
2289
        if (i>=0 && menu->cascades) {
 
2290
            submenu = menu->cascades[i];
 
2291
            if (submenu->flags.mapped && !submenu->flags.buttoned) {
 
2292
                if (wPreferences.align_menus) {
 
2293
                    wMenuMove(submenu, x + MENUW(menu), y, submenus);
 
2294
                } else {
 
2295
                    wMenuMove(submenu, x+ MENUW(menu),
 
2296
                              y + submenu->entry_height*menu->selected_entry,
 
2297
                              submenus);
 
2298
                }
 
2299
            }
 
2300
        }
 
2301
    }
 
2302
    if (submenus<0 && menu->parent!=NULL && menu->parent->flags.mapped &&
 
2303
        !menu->parent->flags.buttoned) {
 
2304
        if (wPreferences.align_menus) {
 
2305
            wMenuMove(menu->parent, x - MENUW(menu->parent), y, submenus);
 
2306
        } else {
 
2307
            wMenuMove(menu->parent, x - MENUW(menu->parent), menu->frame_y
 
2308
                      - menu->parent->entry_height*menu->parent->selected_entry,
 
2309
                      submenus);
 
2310
        }
 
2311
    }
 
2312
}
 
2313
 
 
2314
 
 
2315
static void
 
2316
changeMenuLevels(WMenu *menu, int lower)
 
2317
{
 
2318
    int i;
 
2319
 
 
2320
    if (!lower) {
 
2321
        ChangeStackingLevel(menu->frame->core, (!menu->parent ? WMMainMenuLevel
 
2322
                                                : WMSubmenuLevel));
 
2323
        wRaiseFrame(menu->frame->core);
 
2324
        menu->flags.lowered = 0;
 
2325
    } else {
 
2326
        ChangeStackingLevel(menu->frame->core, WMNormalLevel);
 
2327
        wLowerFrame(menu->frame->core);
 
2328
        menu->flags.lowered = 1;
 
2329
    }
 
2330
    for (i=0; i<menu->cascade_no; i++) {
 
2331
        if (menu->cascades[i]
 
2332
            && !menu->cascades[i]->flags.buttoned
 
2333
            && menu->cascades[i]->flags.lowered!=lower) {
 
2334
            changeMenuLevels(menu->cascades[i], lower);
 
2335
        }
 
2336
    }
 
2337
}
 
2338
 
 
2339
 
 
2340
 
 
2341
static void
 
2342
menuTitleDoubleClick(WCoreWindow *sender, void *data, XEvent *event)
 
2343
{
 
2344
    WMenu *menu = data;
 
2345
    int lower;
 
2346
 
 
2347
    if (event->xbutton.state & MOD_MASK) {
 
2348
        if (menu->flags.lowered) {
 
2349
            lower = 0;
 
2350
        } else {
 
2351
            lower = 1;
 
2352
        }
 
2353
        changeMenuLevels(menu, lower);
 
2354
    }
 
2355
}
 
2356
 
 
2357
 
 
2358
static void
 
2359
menuTitleMouseDown(WCoreWindow *sender, void *data, XEvent *event)
 
2360
{
 
2361
    WMenu *menu = data;
 
2362
    WMenu *tmp;
 
2363
    XEvent ev;
 
2364
    int x=menu->frame_x, y=menu->frame_y;
 
2365
    int dx=event->xbutton.x_root, dy=event->xbutton.y_root;
 
2366
    int i, lower;
 
2367
    Bool started;
 
2368
 
 
2369
    /* can't touch the menu copy */
 
2370
    if (menu->flags.brother)
 
2371
        return;
 
2372
 
 
2373
    if (event->xbutton.button != Button1 && event->xbutton.button != Button2)
 
2374
        return;
 
2375
 
 
2376
    if (event->xbutton.state & MOD_MASK) {
 
2377
        wLowerFrame(menu->frame->core);
 
2378
        lower = 1;
 
2379
    } else {
 
2380
        wRaiseFrame(menu->frame->core);
 
2381
        lower = 0;
 
2382
    }
 
2383
    tmp = menu;
 
2384
 
 
2385
    /* lower/raise all submenus */
 
2386
    while (1) {
 
2387
        if (tmp->selected_entry>=0 && tmp->cascades
 
2388
            && tmp->entries[tmp->selected_entry]->cascade>=0) {
 
2389
            tmp = tmp->cascades[tmp->entries[tmp->selected_entry]->cascade];
 
2390
            if (!tmp || !tmp->flags.mapped)
 
2391
                break;
 
2392
            if (lower)
 
2393
                wLowerFrame(tmp->frame->core);
 
2394
            else
 
2395
                wRaiseFrame(tmp->frame->core);
 
2396
        } else {
 
2397
            break;
 
2398
        }
 
2399
    }
 
2400
 
 
2401
    /* tear off the menu if it's a root menu or a cascade
 
2402
     application menu */
 
2403
    if (!menu->flags.buttoned && !menu->flags.brother
 
2404
        && (!menu->flags.app_menu||menu->parent!=NULL)) {
 
2405
        menu->flags.buttoned=1;
 
2406
        wFrameWindowShowButton(menu->frame, WFF_RIGHT_BUTTON);
 
2407
        if (menu->parent) {
 
2408
            /* turn off selected menu entry in parent menu */
 
2409
            selectEntry(menu->parent, -1);
 
2410
 
 
2411
            /* make parent map the copy in place of the original */
 
2412
            for (i=0; i<menu->parent->cascade_no; i++) {
 
2413
                if (menu->parent->cascades[i] == menu) {
 
2414
                    menu->parent->cascades[i] = menu->brother;
 
2415
                    break;
 
2416
                }
 
2417
            }
 
2418
        }
 
2419
    }
 
2420
 
 
2421
    started = False;
 
2422
    while(1) {
 
2423
        WMMaskEvent(dpy, ButtonMotionMask|ButtonReleaseMask|ButtonPressMask
 
2424
                    |ExposureMask, &ev);
 
2425
        switch (ev.type) {
 
2426
        case MotionNotify:
 
2427
            if (started) {
 
2428
                x += ev.xmotion.x_root - dx;
 
2429
                y += ev.xmotion.y_root - dy;
 
2430
                dx = ev.xmotion.x_root;
 
2431
                dy = ev.xmotion.y_root;
 
2432
                wMenuMove(menu, x, y, True);
 
2433
            } else {
 
2434
                if (abs(ev.xmotion.x_root - dx) > MOVE_THRESHOLD
 
2435
                    || abs(ev.xmotion.y_root - dy) > MOVE_THRESHOLD) {
 
2436
                    started = True;
 
2437
                    XGrabPointer(dpy, menu->frame->titlebar->window, False,
 
2438
                                 ButtonMotionMask|ButtonReleaseMask
 
2439
                                 |ButtonPressMask,
 
2440
                                 GrabModeAsync, GrabModeAsync, None,
 
2441
                                 wCursor[WCUR_MOVE], CurrentTime);
 
2442
                }
 
2443
            }
 
2444
            break;
 
2445
 
 
2446
        case ButtonPress:
 
2447
            break;
 
2448
 
 
2449
        case ButtonRelease:
 
2450
            if (ev.xbutton.button != event->xbutton.button)
 
2451
                break;
 
2452
            XUngrabPointer(dpy, CurrentTime);
 
2453
            return;
 
2454
 
 
2455
        default:
 
2456
            WMHandleEvent(&ev);
 
2457
            break;
 
2458
        }
 
2459
    }
 
2460
}
 
2461
 
 
2462
/*
 
2463
 *----------------------------------------------------------------------
 
2464
 * menuCloseClick--
 
2465
 *      Handles mouse click on the close button of menus. The menu is
 
2466
 * closed when the button is clicked.
 
2467
 *
 
2468
 * Side effects:
 
2469
 *      The closed menu is reinserted at it's parent menus
 
2470
 * cascade list.
 
2471
 *----------------------------------------------------------------------
 
2472
 */
 
2473
static void
 
2474
menuCloseClick(WCoreWindow *sender, void *data, XEvent *event)
 
2475
{
 
2476
    WMenu *menu = (WMenu*)data;
 
2477
    WMenu *parent = menu->parent;
 
2478
    int i;
 
2479
 
 
2480
    if (parent) {
 
2481
        for (i=0; i<parent->cascade_no; i++) {
 
2482
            /* find the entry that points to the copy */
 
2483
            if (parent->cascades[i] == menu->brother) {
 
2484
                /* make it point to the original */
 
2485
                parent->cascades[i] = menu;
 
2486
                menu->parent = parent;
 
2487
                break;
 
2488
            }
 
2489
        }
 
2490
    }
 
2491
    wMenuUnmap(menu);
 
2492
}
 
2493
 
 
2494
 
 
2495
static void
 
2496
saveMenuInfo(WMPropList *dict, WMenu *menu, WMPropList *key)
 
2497
{
 
2498
    WMPropList *value, *list;
 
2499
    char buffer[256];
 
2500
 
 
2501
    snprintf(buffer, sizeof(buffer), "%i,%i", menu->frame_x, menu->frame_y);
 
2502
    value = WMCreatePLString(buffer);
 
2503
    list = WMCreatePLArray(value, NULL);
 
2504
    if (menu->flags.lowered)
 
2505
        WMAddToPLArray(list, WMCreatePLString("lowered"));
 
2506
    WMPutInPLDictionary(dict, key, list);
 
2507
    WMReleasePropList(value);
 
2508
    WMReleasePropList(list);
 
2509
}
 
2510
 
 
2511
 
 
2512
void
 
2513
wMenuSaveState(WScreen *scr)
 
2514
{
 
2515
    WMPropList *menus, *key;
 
2516
    int save_menus = 0;
 
2517
 
 
2518
    menus = WMCreatePLDictionary(NULL, NULL);
 
2519
 
 
2520
#ifndef LITE
 
2521
    if (scr->switch_menu && scr->switch_menu->flags.buttoned) {
 
2522
        key = WMCreatePLString("SwitchMenu");
 
2523
        saveMenuInfo(menus, scr->switch_menu, key);
 
2524
        WMReleasePropList(key);
 
2525
        save_menus = 1;
 
2526
    }
 
2527
 
 
2528
    if (saveMenuRecurs(menus, scr, scr->root_menu))
 
2529
        save_menus = 1;
 
2530
 
 
2531
#endif /* !LITE */
 
2532
    if (scr->workspace_menu && scr->workspace_menu->flags.buttoned) {
 
2533
        key = WMCreatePLString("WorkspaceMenu");
 
2534
        saveMenuInfo(menus, scr->workspace_menu, key);
 
2535
        WMReleasePropList(key);
 
2536
        save_menus = 1;
 
2537
    }
 
2538
 
 
2539
    if (save_menus) {
 
2540
        key = WMCreatePLString("Menus");
 
2541
        WMPutInPLDictionary(scr->session_state, key, menus);
 
2542
        WMReleasePropList(key);
 
2543
    }
 
2544
    WMReleasePropList(menus);
 
2545
}
 
2546
 
 
2547
 
 
2548
#ifndef LITE
 
2549
 
 
2550
static Bool
 
2551
getMenuPath(WMenu *menu, char *buffer, int bufSize)
 
2552
{
 
2553
    Bool ok = True;
 
2554
    int len = 0;
 
2555
 
 
2556
    if (!menu->flags.titled || !menu->frame->title[0])
 
2557
        return False;
 
2558
 
 
2559
    len = strlen(menu->frame->title);
 
2560
    if (len >= bufSize)
 
2561
        return False;
 
2562
 
 
2563
    if (menu->parent) {
 
2564
        ok = getMenuPath(menu->parent, buffer, bufSize - len - 1);
 
2565
        if (!ok)
 
2566
            return False;
 
2567
    }
 
2568
 
 
2569
    strcat(buffer, "\\");
 
2570
    strcat(buffer, menu->frame->title);
 
2571
 
 
2572
    return True;
 
2573
}
 
2574
 
 
2575
 
 
2576
static Bool
 
2577
saveMenuRecurs(WMPropList *menus, WScreen *scr, WMenu *menu)
 
2578
{
 
2579
    WMPropList *key;
 
2580
    int save_menus = 0, i;
 
2581
    char buffer[512];
 
2582
    Bool ok = True;
 
2583
 
 
2584
 
 
2585
    if (menu->flags.brother)
 
2586
        menu = menu->brother;
 
2587
 
 
2588
    if (menu->flags.buttoned && menu != scr->switch_menu) {
 
2589
 
 
2590
        buffer[0] = '\0';
 
2591
        ok = getMenuPath(menu, buffer, 510);
 
2592
 
 
2593
        if (ok) {
 
2594
            key = WMCreatePLString(buffer);
 
2595
            saveMenuInfo(menus, menu, key);
 
2596
            WMReleasePropList(key);
 
2597
            save_menus = 1;
 
2598
        }
 
2599
    }
 
2600
 
 
2601
    if (ok) {
 
2602
        for (i = 0; i < menu->cascade_no; i++) {
 
2603
            if (saveMenuRecurs(menus, scr, menu->cascades[i]))
 
2604
                save_menus = 1;
 
2605
        }
 
2606
    }
 
2607
    return save_menus;
 
2608
}
 
2609
#endif /* !LITE */
 
2610
 
 
2611
 
 
2612
#define COMPLAIN(key) wwarning(_("bad value in menus state info:%s"), key)
 
2613
 
 
2614
 
 
2615
static Bool
 
2616
getMenuInfo(WMPropList *info, int *x, int *y, Bool *lowered)
 
2617
{
 
2618
    WMPropList *pos;
 
2619
 
 
2620
    *lowered = False;
 
2621
 
 
2622
    if (WMIsPLArray(info)) {
 
2623
        WMPropList *flags;
 
2624
        pos = WMGetFromPLArray(info, 0);
 
2625
        flags = WMGetFromPLArray(info, 1);
 
2626
        if (flags != NULL && WMIsPLString(flags) && WMGetFromPLString(flags) != NULL
 
2627
            && strcmp(WMGetFromPLString(flags), "lowered") == 0) {
 
2628
            *lowered = True;
 
2629
        }
 
2630
    } else {
 
2631
        pos = info;
 
2632
    }
 
2633
 
 
2634
    if (pos != NULL && WMIsPLString(pos)) {
 
2635
        if (sscanf(WMGetFromPLString(pos), "%i,%i", x, y)!=2)
 
2636
            COMPLAIN("Position");
 
2637
    } else {
 
2638
        COMPLAIN("(position, flags...)");
 
2639
        return False;
 
2640
    }
 
2641
 
 
2642
    return True;
 
2643
}
 
2644
 
 
2645
 
 
2646
static int
 
2647
restoreMenu(WScreen *scr, WMPropList *menu, int which)
 
2648
{
 
2649
    int x, y;
 
2650
    Bool lowered = False;
 
2651
    WMenu *pmenu = NULL;
 
2652
 
 
2653
    if (!menu)
 
2654
        return False;
 
2655
 
 
2656
    if (!getMenuInfo(menu, &x, &y, &lowered))
 
2657
        return False;
 
2658
 
 
2659
 
 
2660
#ifndef LITE
 
2661
    if (which & WSS_SWITCHMENU) {
 
2662
        OpenSwitchMenu(scr, x, y, False);
 
2663
        pmenu = scr->switch_menu;
 
2664
    }
 
2665
#endif /* !LITE */
 
2666
 
 
2667
    if (pmenu) {
 
2668
        int width = MENUW(pmenu);
 
2669
        int height = MENUH(pmenu);
 
2670
        WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
 
2671
 
 
2672
        if (lowered) {
 
2673
            changeMenuLevels(pmenu, True);
 
2674
        }
 
2675
 
 
2676
        if (x < rect.pos.x - width) x = rect.pos.x;
 
2677
        if (x > rect.pos.x + rect.size.width) x = rect.pos.x + rect.size.width - width;
 
2678
        if (y < rect.pos.y) y = rect.pos.y;
 
2679
        if (y > rect.pos.y + rect.size.height) y = rect.pos.y + rect.size.height - height;
 
2680
 
 
2681
        wMenuMove(pmenu, x, y, True);
 
2682
        pmenu->flags.buttoned = 1;
 
2683
        wFrameWindowShowButton(pmenu->frame, WFF_RIGHT_BUTTON);
 
2684
        return True;
 
2685
    }
 
2686
    return False;
 
2687
}
 
2688
 
 
2689
 
 
2690
#ifndef LITE
 
2691
static int
 
2692
restoreMenuRecurs(WScreen *scr, WMPropList *menus, WMenu *menu, char *path)
 
2693
{
 
2694
    WMPropList *key, *entry;
 
2695
    char buffer[512];
 
2696
    int i, x, y, res;
 
2697
    Bool lowered;
 
2698
 
 
2699
    if (strlen(path) + strlen(menu->frame->title) > 510)
 
2700
        return False;
 
2701
 
 
2702
    snprintf(buffer, sizeof(buffer), "%s\\%s", path, menu->frame->title);
 
2703
    key = WMCreatePLString(buffer);
 
2704
    entry = WMGetFromPLDictionary(menus, key);
 
2705
    res = False;
 
2706
 
 
2707
    if (entry && getMenuInfo(entry, &x, &y, &lowered)) {
 
2708
 
 
2709
        if (!menu->flags.mapped) {
 
2710
            int width = MENUW(menu);
 
2711
            int height = MENUH(menu);
 
2712
            WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
 
2713
 
 
2714
            wMenuMapAt(menu, x, y, False);
 
2715
 
 
2716
            if (menu->parent) {
 
2717
                /* make parent map the copy in place of the original */
 
2718
                for (i=0; i<menu->parent->cascade_no; i++) {
 
2719
                    if (menu->parent->cascades[i] == menu) {
 
2720
                        menu->parent->cascades[i] = menu->brother;
 
2721
                        break;
 
2722
                    }
 
2723
                }
 
2724
            }
 
2725
            if (lowered) {
 
2726
                changeMenuLevels(menu, True);
 
2727
            }
 
2728
 
 
2729
            if (x < rect.pos.x - width) x = rect.pos.x;
 
2730
            if (x > rect.pos.x + rect.size.width) x = rect.pos.x + rect.size.width - width;
 
2731
            if (y < rect.pos.y) y = rect.pos.y;
 
2732
            if (y > rect.pos.y + rect.size.height) y = rect.pos.y + rect.size.height - height;
 
2733
 
 
2734
            wMenuMove(menu, x, y, True);
 
2735
            menu->flags.buttoned = 1;
 
2736
            wFrameWindowShowButton(menu->frame, WFF_RIGHT_BUTTON);
 
2737
            res = True;
 
2738
        }
 
2739
    }
 
2740
 
 
2741
    WMReleasePropList(key);
 
2742
 
 
2743
    for (i=0; i<menu->cascade_no; i++) {
 
2744
        if (restoreMenuRecurs(scr, menus, menu->cascades[i], buffer) != False)
 
2745
            res = True;
 
2746
    }
 
2747
 
 
2748
    return res;
 
2749
}
 
2750
#endif /* !LITE */
 
2751
 
 
2752
 
 
2753
void
 
2754
wMenuRestoreState(WScreen *scr)
 
2755
{
 
2756
    WMPropList *menus, *menu, *key, *skey;
 
2757
 
 
2758
    if (!scr->session_state) {
 
2759
        return;
 
2760
    }
 
2761
 
 
2762
    key = WMCreatePLString("Menus");
 
2763
    menus = WMGetFromPLDictionary(scr->session_state, key);
 
2764
    WMReleasePropList(key);
 
2765
 
 
2766
    if (!menus)
 
2767
        return;
 
2768
 
 
2769
    /* restore menus */
 
2770
 
 
2771
    skey = WMCreatePLString("SwitchMenu");
 
2772
    menu = WMGetFromPLDictionary(menus, skey);
 
2773
    WMReleasePropList(skey);
 
2774
    restoreMenu(scr, menu, WSS_SWITCHMENU);
 
2775
 
 
2776
#ifndef LITE
 
2777
    if (!scr->root_menu) {
 
2778
        OpenRootMenu(scr, scr->scr_width*2, 0, False);
 
2779
        wMenuUnmap(scr->root_menu);
 
2780
    }
 
2781
    restoreMenuRecurs(scr, menus, scr->root_menu, "");
 
2782
#endif /* !LITE */
 
2783
}
 
2784
 
 
2785
 
 
2786
void
 
2787
OpenWorkspaceMenu(WScreen *scr, int x, int y)
 
2788
{
 
2789
    WMenu *menu, *parent;
 
2790
    WMenuEntry *entry;
 
2791
 
 
2792
#ifndef LITE
 
2793
    if (!scr->root_menu) {
 
2794
        OpenRootMenu(scr, scr->scr_width*2, 0, False);
 
2795
        wMenuUnmap(scr->root_menu);
 
2796
    }
 
2797
#endif
 
2798
    menu = scr->workspace_menu;
 
2799
    if (menu) {
 
2800
        if (menu->flags.mapped) {
 
2801
            if (!menu->flags.buttoned) {
 
2802
                wMenuUnmap(menu);
 
2803
                parent = menu->parent;
 
2804
                if (parent && parent->selected_entry >= 0) {
 
2805
                    entry = parent->entries[parent->selected_entry];
 
2806
                    if (parent->cascades[entry->cascade] == menu) {
 
2807
                        selectEntry(parent, -1);
 
2808
                        wMenuMapAt(menu, x, y, False);
 
2809
                    }
 
2810
                }
 
2811
            } else {
 
2812
                wRaiseFrame(menu->frame->core);
 
2813
                wMenuMapCopyAt(menu, x, y);
 
2814
            }
 
2815
        } else {
 
2816
            wMenuMapAt(menu, x, y, False);
 
2817
        }
 
2818
    }
 
2819
}
 
2820
 
 
2821