1
0
mirror of https://github.com/gryf/wmaker.git synced 2026-01-12 01:14:21 +01:00
Files
wmaker/src/menu.c
dan 36e46831e0 For libwraster:
---------------

- Added retain/release mechanism to RImage by adding RRetainImage() and
  RReleaseImage(). RDestroyImage() is an alias to RReleaseImage() now, but
  will be removed in a future release because it no longer fits with the
  semantics. Will be kept for a while to allow a smoother transition.
  More about in wrlib/NEWS


For WINGs:
----------

- Small API change:
  1. Renamed WMSetApplicationIconImage(), WMGetApplicationIconImage() and
     WMSetWindowMiniwindowImage() to respectively WMSetApplicationIconPixmap(),
     WMGetApplicationIconPixmap() and WMSetWindowMiniwindowPixmap()
     They operate on a WMPixmap which is practically an X Pixmap with no alpha
     channel information and the new name is more suggestive and also leaves
     room for the new functions added for operating on images with alpha info.
  2. Added WMSetApplicationIconImage() and WMGetApplicationIconImage() which
     operate on an RImage and store alpha information too.
  3. Added WMGetApplicationIconBlendedPixmap() which will take the image with
     alpha set by WMSetApplicationIconImage() and will blend it with a color.
     If color is NULL it will blend using the default panel color (#aeaaae)
  All these changes will allow WINGs to handle images with alpha blending
  correctly in panels and wherever else needed. More about in WINGs/NEWS.
- updated panels to use the newly available RImages if present and fallback
  to old WMPixmaps if not, to properly show alpha blended images.
- replaced some still left malloc's with wmalloc's.


For Window Maker:
-----------------
- Fixed wrong mapping position of the "Docked Applications Panel" for some
  icons.
- Smoother animation for the smiley =)
- Made images with alpha blending be shown correctly in the panels and the
  icon chooser.
- The icon image set to be shown in panels ("Logo.WMPanel") will be
  automatically updated if its entry in WMWindowAttributes changes (without
  a need to restart as until now).


*** Note!!! ***

If you are developing applications with one of libwraster or libWINGs
then you should look to wrlib/NEWS and WINGs/NEWS to see what changed
and how should you update your code.
2001-04-21 07:12:21 +00:00

2770 lines
68 KiB
C

/* menu.c- generic menu, used for root menu, application menus etc.
*
* Window Maker window manager
*
* Copyright (c) 1997, 1998 Alfredo K. Kojima
* Copyright (c) 1997, 1998 Dan Pascu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include "wconfig.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#if 0
#include <nana.h>
#endif
#include "WindowMaker.h"
#include "wcore.h"
#include "framewin.h"
#include "menu.h"
#include "actions.h"
#include "funcs.h"
#include "stacking.h"
#include "text.h"
/****** Global Variables ******/
extern Cursor wCursor[WCUR_LAST];
extern XContext wWinContext;
extern WPreferences wPreferences;
#define MOD_MASK wPreferences.modifier_mask
#define MENU_SCROLL_STEP menuScrollParameters[(int)wPreferences.menu_scroll_speed].steps
#define MENU_SCROLL_DELAY menuScrollParameters[(int)wPreferences.menu_scroll_speed].delay
#define MENUW(m) ((m)->frame->core->width+2*FRAME_BORDER_WIDTH)
#define MENUH(m) ((m)->frame->core->height+2*FRAME_BORDER_WIDTH)
/***** Local Stuff ******/
static struct {
int steps;
int delay;
} menuScrollParameters[5] = {
{MENU_SCROLL_STEPS_UF, MENU_SCROLL_DELAY_UF},
{MENU_SCROLL_STEPS_F, MENU_SCROLL_DELAY_F},
{MENU_SCROLL_STEPS_M, MENU_SCROLL_DELAY_M},
{MENU_SCROLL_STEPS_S, MENU_SCROLL_DELAY_S},
{MENU_SCROLL_STEPS_U, MENU_SCROLL_DELAY_U}};
static void menuMouseDown(WObjDescriptor *desc, XEvent *event);
static void menuExpose(WObjDescriptor *desc, XEvent *event);
static void menuTitleDoubleClick(WCoreWindow *sender, void *data, XEvent *event);
static void menuTitleMouseDown(WCoreWindow *sender, void *data, XEvent *event);
static void menuCloseClick(WCoreWindow *sender, void *data, XEvent *event);
static void updateTexture(WMenu *menu);
#ifndef LITE
static int saveMenuRecurs(proplist_t menus, WScreen *scr, WMenu *menu);
static int restoreMenuRecurs(WScreen *scr, proplist_t menus, WMenu *menu, char *path);
#endif /* !LITE */
static void selectEntry(WMenu *menu, int entry_no);
static void closeCascade(WMenu *menu);
/****** Notification Observers ******/
static void
appearanceObserver(void *self, WMNotification *notif)
{
WMenu *menu = (WMenu*)self;
int flags = (int)WMGetNotificationClientData(notif);
if (!menu->flags.realized)
return;
if (WMGetNotificationName(notif) == WNMenuAppearanceSettingsChanged) {
if (flags & WFontSettings) {
menu->flags.realized = 0;
wMenuRealize(menu);
}
if (flags & WTextureSettings) {
if (!menu->flags.brother)
updateTexture(menu);
}
if (flags & (WTextureSettings|WColorSettings)) {
wMenuPaint(menu);
}
} else if (menu->flags.titled) {
if (flags & WFontSettings) {
menu->flags.realized = 0;
wMenuRealize(menu);
}
if (flags & WTextureSettings) {
menu->frame->flags.need_texture_remake = 1;
}
if (flags & (WColorSettings|WTextureSettings)) {
wFrameWindowPaint(menu->frame);
}
}
}
/************************************/
/*
*----------------------------------------------------------------------
* wMenuCreate--
* Creates a new empty menu with the specified title. If main_menu
* is True, the created menu will be a main menu, which has some special
* properties such as being placed over other normal menus.
* If title is NULL, the menu will have no titlebar.
*
* Returns:
* The created menu.
*----------------------------------------------------------------------
*/
WMenu*
wMenuCreate(WScreen *screen, char *title, int main_menu)
{
WMenu *menu;
static int brother=0;
int tmp, flags;
menu = wmalloc(sizeof(WMenu));
memset(menu, 0, sizeof(WMenu));
#ifdef SINGLE_MENULEVEL
tmp = WMSubmenuLevel;
#else
tmp = (main_menu ? WMMainMenuLevel : WMSubmenuLevel);
#endif
flags = WFF_SINGLE_STATE|WFF_BORDER;
if (title) {
flags |= WFF_TITLEBAR|WFF_RIGHT_BUTTON;
menu->flags.titled = 1;
}
menu->frame =
wFrameWindowCreate(screen, tmp, 8, 2, 1, 1, &wPreferences.menu_title_clearance, flags,
screen->menu_title_texture, NULL,
screen->menu_title_pixel,
&screen->menu_title_gc,
&screen->menu_title_font);
menu->frame->core->descriptor.parent = menu;
menu->frame->core->descriptor.parent_type = WCLASS_MENU;
menu->frame->core->descriptor.handle_mousedown = menuMouseDown;
wFrameWindowHideButton(menu->frame, WFF_RIGHT_BUTTON);
if (title) {
menu->frame->title = wstrdup(title);
}
menu->frame->flags.justification = WTJ_LEFT;
menu->frame->rbutton_image = screen->b_pixmaps[WBUT_CLOSE];
menu->entry_no = 0;
menu->alloced_entries = 0;
menu->selected_entry = -1;
menu->entries = NULL;
menu->frame_x = screen->app_menu_x;
menu->frame_y = screen->app_menu_y;
menu->frame->child = menu;
menu->flags.lowered = 0;
/* create borders */
if (title) {
/* setup object descriptors */
menu->frame->on_mousedown_titlebar = menuTitleMouseDown;
menu->frame->on_dblclick_titlebar = menuTitleDoubleClick;
}
menu->frame->on_click_right = menuCloseClick;
menu->menu = wCoreCreate(menu->frame->core, 0, menu->frame->top_width,
menu->frame->core->width, 10);
menu->menu->descriptor.parent = menu;
menu->menu->descriptor.parent_type = WCLASS_MENU;
menu->menu->descriptor.handle_expose = menuExpose;
menu->menu->descriptor.handle_mousedown = menuMouseDown;
menu->menu_texture_data = None;
XMapWindow(dpy, menu->menu->window);
XFlush(dpy);
if (!brother) {
brother = 1;
menu->brother = wMenuCreate(screen, title, main_menu);
brother = 0;
menu->brother->flags.brother = 1;
menu->brother->brother = menu;
}
WMAddNotificationObserver(appearanceObserver, menu,
WNMenuAppearanceSettingsChanged, menu);
WMAddNotificationObserver(appearanceObserver, menu,
WNMenuTitleAppearanceSettingsChanged, menu);
return menu;
}
WMenu*
wMenuCreateForApp(WScreen *screen, char *title, int main_menu)
{
WMenu *menu;
menu = wMenuCreate(screen, title, main_menu);
if (!menu)
return NULL;
menu->flags.app_menu = 1;
menu->brother->flags.app_menu = 1;
return menu;
}
static void
insertEntry(WMenu *menu, WMenuEntry *entry, int index)
{
int i;
for (i = menu->entry_no-1; i >= index; i--) {
menu->entries[i]->order++;
menu->entries[i+1] = menu->entries[i];
}
menu->entries[index] = entry;
}
WMenuEntry*
wMenuInsertCallback(WMenu *menu, int index, char *text,
void (*callback)(WMenu *menu, WMenuEntry *entry),
void *clientdata)
{
WMenuEntry *entry;
menu->flags.realized = 0;
menu->brother->flags.realized = 0;
/* reallocate array if it's too small */
if (menu->entry_no >= menu->alloced_entries) {
void *tmp;
tmp = wrealloc(menu->entries,
sizeof(WMenuEntry)*(menu->alloced_entries+5));
menu->entries = tmp;
menu->alloced_entries += 5;
menu->brother->entries = tmp;
menu->brother->alloced_entries = menu->alloced_entries;
}
entry = wmalloc(sizeof(WMenuEntry));
memset(entry, 0, sizeof(WMenuEntry));
entry->flags.enabled = 1;
entry->text = wstrdup(text);
entry->cascade = -1;
entry->clientdata = clientdata;
entry->callback = callback;
if (index<0 || index>=menu->entry_no) {
entry->order = menu->entry_no;
menu->entries[menu->entry_no] = entry;
} else {
entry->order = index;
insertEntry(menu, entry, index);
}
menu->entry_no++;
menu->brother->entry_no = menu->entry_no;
return entry;
}
void
wMenuEntrySetCascade(WMenu *menu, WMenuEntry *entry, WMenu *cascade)
{
WMenu *brother = menu->brother;
int i, done;
assert(menu->flags.brother==0);
if (entry->cascade>=0) {
menu->flags.realized = 0;
brother->flags.realized = 0;
}
cascade->parent = menu;
cascade->brother->parent = brother;
done = 0;
for (i=0; i<menu->cascade_no; i++) {
if (menu->cascades[i]==NULL) {
menu->cascades[i] = cascade;
brother->cascades[i] = cascade->brother;
done = 1;
entry->cascade = i;
break;
}
}
if (!done) {
entry->cascade = menu->cascade_no;
menu->cascades = wrealloc(menu->cascades,
sizeof(WMenu)*(menu->cascade_no+1));
menu->cascades[menu->cascade_no++] = cascade;
brother->cascades = wrealloc(brother->cascades,
sizeof(WMenu)*(brother->cascade_no+1));
brother->cascades[brother->cascade_no++] = cascade->brother;
}
if (menu->flags.lowered) {
cascade->flags.lowered = 1;
ChangeStackingLevel(cascade->frame->core, WMNormalLevel);
cascade->brother->flags.lowered = 1;
ChangeStackingLevel(cascade->brother->frame->core, WMNormalLevel);
}
if (!menu->flags.realized)
wMenuRealize(menu);
}
void
wMenuEntryRemoveCascade(WMenu *menu, WMenuEntry *entry)
{
assert(menu->flags.brother==0);
/* destroy cascade menu */
if (entry->cascade>=0 && menu->cascades
&& menu->cascades[entry->cascade]!=NULL) {
wMenuDestroy(menu->cascades[entry->cascade], True);
menu->cascades[entry->cascade] = NULL;
menu->brother->cascades[entry->cascade] = NULL;
entry->cascade = -1;
}
}
void
wMenuRemoveItem(WMenu *menu, int index)
{
int i;
if (menu->flags.brother) {
wMenuRemoveItem(menu->brother, index);
return;
}
if (index>=menu->entry_no) return;
/* destroy cascade menu */
wMenuEntryRemoveCascade(menu, menu->entries[index]);
/* destroy unshared data */
if (menu->entries[index]->text)
wfree(menu->entries[index]->text);
if (menu->entries[index]->rtext)
wfree(menu->entries[index]->rtext);
if (menu->entries[index]->free_cdata && menu->entries[index]->clientdata)
(*menu->entries[index]->free_cdata)(menu->entries[index]->clientdata);
wfree(menu->entries[index]);
for (i=index; i<menu->entry_no-1; i++) {
menu->entries[i+1]->order--;
menu->entries[i]=menu->entries[i+1];
}
menu->entry_no--;
menu->brother->entry_no--;
}
static Pixmap
renderTexture(WMenu *menu)
{
RImage *img;
Pixmap pix;
int i;
RColor light;
RColor dark;
RColor mid;
WScreen *scr = menu->menu->screen_ptr;
WTexture *texture = scr->menu_item_texture;
if (wPreferences.menu_style == MS_NORMAL) {
img = wTextureRenderImage(texture, menu->menu->width,
menu->entry_height, WREL_MENUENTRY);
} else {
img = wTextureRenderImage(texture, menu->menu->width,
menu->menu->height+1, WREL_MENUENTRY);
}
if (!img) {
wwarning(_("could not render texture: %s"),
RMessageForError(RErrorCode));
return None;
}
if (wPreferences.menu_style == MS_SINGLE_TEXTURE) {
light.alpha = 0;
light.red = light.green = light.blue = 80;
dark.alpha = 255;
dark.red = dark.green = dark.blue = 0;
mid.alpha = 0;
mid.red = mid.green = mid.blue = 40;
for (i = 1; i < menu->entry_no; i++) {
ROperateLine(img, RSubtractOperation, 0, i*menu->entry_height-2,
menu->menu->width-1, i*menu->entry_height-2, &mid);
RDrawLine(img, 0, i*menu->entry_height-1,
menu->menu->width-1, i*menu->entry_height-1, &dark);
ROperateLine(img, RAddOperation, 0, i*menu->entry_height,
menu->menu->width-1, i*menu->entry_height,
&light);
}
}
if (!RConvertImage(scr->rcontext, img, &pix)) {
wwarning(_("error rendering image:%s"), RMessageForError(RErrorCode));
}
RReleaseImage(img);
return pix;
}
static void
updateTexture(WMenu *menu)
{
WScreen *scr = menu->menu->screen_ptr;
/* setup background texture */
if (scr->menu_item_texture->any.type != WTEX_SOLID) {
if (!menu->flags.brother) {
FREE_PIXMAP(menu->menu_texture_data);
menu->menu_texture_data = renderTexture(menu);
XSetWindowBackgroundPixmap(dpy, menu->menu->window,
menu->menu_texture_data);
XClearWindow(dpy, menu->menu->window);
XSetWindowBackgroundPixmap(dpy, menu->brother->menu->window,
menu->menu_texture_data);
XClearWindow(dpy, menu->brother->menu->window);
}
} else {
XSetWindowBackground(dpy, menu->menu->window,
scr->menu_item_texture->any.color.pixel);
XClearWindow(dpy, menu->menu->window);
}
}
void
wMenuRealize(WMenu *menu)
{
int i;
int width, rwidth, mrwidth, mwidth;
int theight, twidth, eheight;
WScreen *scr = menu->frame->screen_ptr;
static int brother_done=0;
int flags;
if (!brother_done) {
brother_done = 1;
wMenuRealize(menu->brother);
brother_done = 0;
}
flags = WFF_SINGLE_STATE|WFF_BORDER;
if (menu->flags.titled)
flags |= WFF_TITLEBAR|WFF_RIGHT_BUTTON;
wFrameWindowUpdateBorders(menu->frame, flags);
if (menu->flags.titled) {
twidth = WMWidthOfString(scr->menu_title_font, menu->frame->title,
strlen(menu->frame->title));
theight = menu->frame->top_width;
twidth += theight + (wPreferences.new_style ? 16 : 8);
} else {
twidth = 0;
theight = 0;
}
eheight = WMFontHeight(scr->menu_entry_font) + 6 + wPreferences.menu_text_clearance * 2;
menu->entry_height = eheight;
mrwidth = 0;
mwidth = 0;
for (i=0; i<menu->entry_no; i++) {
char *text;
/* search widest text */
text = menu->entries[i]->text;
width = WMWidthOfString(scr->menu_entry_font, text, strlen(text))+10;
if (menu->entries[i]->flags.indicator) {
width += MENU_INDICATOR_SPACE;
}
if (width > mwidth)
mwidth = width;
/* search widest text on right */
text = menu->entries[i]->rtext;
if (text)
rwidth = WMWidthOfString(scr->menu_entry_font, text, strlen(text))
+ 5;
else if (menu->entries[i]->cascade>=0)
rwidth = 16;
else
rwidth = 4;
if (rwidth > mrwidth)
mrwidth = rwidth;
}
mwidth += mrwidth;
if (mwidth < twidth)
mwidth = twidth;
wCoreConfigure(menu->menu, 0, theight, mwidth, menu->entry_no*eheight -1);
wFrameWindowResize(menu->frame, mwidth, menu->entry_no*eheight-1
+ menu->frame->top_width + menu->frame->bottom_width);
updateTexture(menu);
menu->flags.realized = 1;
if (menu->flags.mapped)
wMenuPaint(menu);
if (menu->brother->flags.mapped)
wMenuPaint(menu->brother);
}
void
wMenuDestroy(WMenu *menu, int recurse)
{
int i;
WMRemoveNotificationObserver(menu);
/* remove any pending timers */
if (menu->timer)
WMDeleteTimerHandler(menu->timer);
menu->timer = NULL;
/* call destroy handler */
if (menu->on_destroy)
(*menu->on_destroy)(menu);
/* Destroy items if this menu own them. If this is the "brother" menu,
* leave them alone as it is shared by them.
*/
if (!menu->flags.brother) {
for (i=0; i<menu->entry_no; i++) {
wfree(menu->entries[i]->text);
if (menu->entries[i]->rtext)
wfree(menu->entries[i]->rtext);
#ifdef USER_MENU
if (menu->entries[i]->instances){
PLRelease(menu->entries[i]->instances);
}
#endif /* USER_MENU */
if (menu->entries[i]->free_cdata && menu->entries[i]->clientdata) {
(*menu->entries[i]->free_cdata)(menu->entries[i]->clientdata);
}
wfree(menu->entries[i]);
}
if (recurse) {
for (i=0; i<menu->cascade_no; i++) {
if (menu->cascades[i]) {
if (menu->cascades[i]->flags.brother)
wMenuDestroy(menu->cascades[i]->brother, recurse);
else
wMenuDestroy(menu->cascades[i], recurse);
}
}
}
if (menu->entries)
wfree(menu->entries);
}
FREE_PIXMAP(menu->menu_texture_data);
if (menu->cascades)
wfree(menu->cascades);
wCoreDestroy(menu->menu);
wFrameWindowDestroy(menu->frame);
/* destroy copy of this menu */
if (!menu->flags.brother && menu->brother)
wMenuDestroy(menu->brother, False);
wfree(menu);
}
#define F_NORMAL 0
#define F_TOP 1
#define F_BOTTOM 2
#define F_NONE 3
static void
drawFrame(WScreen *scr, Drawable win, int y, int w, int h, int type)
{
XSegment segs[2];
int i;
i = 0;
segs[i].x1 = segs[i].x2 = w-1;
segs[i].y1 = y;
segs[i].y2 = y + h - 1;
i++;
if (type != F_TOP && type != F_NONE) {
segs[i].x1 = 1;
segs[i].y1 = segs[i].y2 = y + h-2;
segs[i].x2 = w-1;
i++;
}
XDrawSegments(dpy, win, scr->menu_item_auxtexture->dim_gc, segs, i);
i = 0;
segs[i].x1 = 0;
segs[i].y1 = y;
segs[i].x2 = 0;
segs[i].y2 = y + h - 1;
i++;
if (type != F_BOTTOM && type != F_NONE) {
segs[i].x1 = 0;
segs[i].y1 = y;
segs[i].x2 = w-1;
segs[i].y2 = y;
i++;
}
XDrawSegments(dpy, win, scr->menu_item_auxtexture->light_gc, segs, i);
if (type != F_TOP && type != F_NONE)
XDrawLine(dpy, win, scr->menu_item_auxtexture->dark_gc, 0, y+h-1,
w-1, y+h-1);
}
static void
paintEntry(WMenu *menu, int index, int selected)
{
int x, y, w, h, tw;
int type;
GC light, dim, dark, textGC;
WScreen *scr=menu->frame->screen_ptr;
Window win = menu->menu->window;
WMenuEntry *entry=menu->entries[index];
if (!menu->flags.realized) return;
h = menu->entry_height;
w = menu->menu->width;
y = index * h;
light = scr->menu_item_auxtexture->light_gc;
dim = scr->menu_item_auxtexture->dim_gc;
dark = scr->menu_item_auxtexture->dark_gc;
if (wPreferences.menu_style == MS_FLAT && menu->entry_no > 1) {
if (index == 0)
type = F_TOP;
else if (index == menu->entry_no - 1)
type = F_BOTTOM;
else
type = F_NONE;
} else {
type = F_NORMAL;
}
/* paint background */
if (selected) {
XSetForeground(dpy, scr->select_menu_gc, scr->select_pixel);
XFillRectangle(dpy, win, scr->select_menu_gc, 1, y+1, w-2, h-3);
if (scr->menu_item_texture->any.type == WTEX_SOLID)
drawFrame(scr, win, y, w, h, type);
} else {
if (scr->menu_item_texture->any.type == WTEX_SOLID) {
XClearArea(dpy, win, 0, y + 1, w - 1, h - 3, False);
/* draw the frame */
drawFrame(scr, win, y, w, h, type);
} else {
XClearArea(dpy, win, 0, y, w, h, False);
}
}
if (selected) {
textGC = scr->select_menu_gc;
if (entry->flags.enabled)
XSetForeground(dpy, textGC, scr->select_text_pixel);
else
XSetForeground(dpy, textGC, scr->dtext_pixel);
} else if (!entry->flags.enabled) {
textGC = scr->disabled_menu_entry_gc;
} else {
textGC = scr->menu_entry_gc;
}
/* draw text */
x = 5;
if (entry->flags.indicator)
x += MENU_INDICATOR_SPACE + 2;
WMDrawString(scr->wmscreen, win, textGC, scr->menu_entry_font,
x, 3 + y + wPreferences.menu_text_clearance, entry->text, strlen(entry->text));
if (entry->cascade>=0) {
/* draw the cascade indicator */
XDrawLine(dpy,win,dim, w-11, y+6, w-6, y+h/2-1);
XDrawLine(dpy,win,light, w-11, y+h-8, w-6, y+h/2-1);
XDrawLine(dpy,win,dark, w-12, y+6, w-12, y+h-8);
}
/* draw indicator */
if (entry->flags.indicator && entry->flags.indicator_on) {
int iw, ih;
WPixmap *indicator;
switch (entry->flags.indicator_type) {
case MI_CHECK:
indicator = scr->menu_check_indicator;
break;
case MI_MINIWINDOW:
indicator = scr->menu_mini_indicator;
break;
case MI_HIDDEN:
indicator = scr->menu_hide_indicator;
break;
case MI_SHADED:
indicator = scr->menu_shade_indicator;
break;
case MI_DIAMOND:
default:
indicator = scr->menu_radio_indicator;
break;
}
iw = indicator->width;
ih = indicator->height;
XSetClipMask(dpy, scr->copy_gc, indicator->mask);
XSetClipOrigin(dpy, scr->copy_gc, 5, y+(h-ih)/2);
if (selected)
XSetForeground(dpy, scr->copy_gc, scr->black_pixel);
else
XSetForeground(dpy, scr->copy_gc, scr->mtext_pixel);
XFillRectangle(dpy, win, scr->copy_gc, 5, y+(h-ih)/2, iw, ih);
/*
XCopyArea(dpy, indicator->image, win, scr->copy_gc, 0, 0,
iw, ih, 5, y+(h-ih)/2);
*/
XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
}
/* draw right text */
if (entry->rtext && entry->cascade<0) {
tw = WMWidthOfString(scr->menu_entry_font, entry->rtext,
strlen(entry->rtext));
WMDrawString(scr->wmscreen, win, textGC, scr->menu_entry_font, w-6-tw,
y + 3 + wPreferences.menu_text_clearance, entry->rtext, strlen(entry->rtext));
}
}
static void
move_menus(WMenu *menu, int x, int y)
{
while (menu->parent) {
menu = menu->parent;
x -= MENUW(menu);
if (!wPreferences.align_menus && menu->selected_entry>=0) {
y -= menu->selected_entry*menu->entry_height;
}
}
wMenuMove(menu, x, y, True);
}
static void
makeVisible(WMenu *menu)
{
WScreen *scr = menu->frame->screen_ptr;
int x1, y1, x2, y2, new_x, new_y, move;
if (menu->entry_no<0) return;
x1 = menu->frame_x;
y1 = menu->frame_y+menu->frame->top_width
+ menu->selected_entry*menu->entry_height;
x2 = x1 + MENUW(menu);
y2 = y1 + menu->entry_height;
new_x = x1;
new_y = y1;
move = 0;
if (x1 < 0) {
new_x = 0;
move = 1;
} else if (x2 >= scr->scr_width) {
new_x = scr->scr_width - MENUW(menu) - 1;
move = 1;
}
if (y1 < 0) {
new_y = 0;
move = 1;
} else if (y2 >= scr->scr_height) {
new_y = scr->scr_height - menu->entry_height - 1;
move = 1;
}
new_y = new_y - menu->frame->top_width
- menu->selected_entry*menu->entry_height;
move_menus(menu, new_x, new_y);
}
static int
check_key(WMenu *menu, XKeyEvent *event)
{
int i, ch, s;
char buffer[32];
if (XLookupString(event, buffer, 32, NULL, NULL)<1)
return -1;
ch = toupper(buffer[0]);
s = (menu->selected_entry>=0 ? menu->selected_entry+1 : 0);
again:
for (i=s; i<menu->entry_no; i++) {
if (ch==toupper(menu->entries[i]->text[0])) {
return i;
}
}
/* no match. Retry from start, if previous started from a selected entry */
if (s!=0) {
s = 0;
goto again;
}
return -1;
}
static int
keyboardMenu(WMenu *menu)
{
XEvent event;
KeySym ksym=NoSymbol;
int done=0;
int index;
WMenuEntry *entry;
int old_pos_x = menu->frame_x;
int old_pos_y = menu->frame_y;
int new_x = old_pos_x, new_y = old_pos_y;
int scr_width = menu->frame->screen_ptr->scr_width;
int scr_height = menu->frame->screen_ptr->scr_height;
if (menu->flags.editing)
return False;
XGrabKeyboard(dpy, menu->frame->core->window, True, GrabModeAsync,
GrabModeAsync, CurrentTime);
if (menu->frame_y+menu->frame->top_width >= scr_height)
new_y = scr_height - menu->frame->top_width;
if (menu->frame_x+MENUW(menu) >= scr_width)
new_x = scr_width-MENUW(menu)-1;
move_menus(menu, new_x, new_y);
while (!done && menu->flags.mapped) {
XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonPressMask
|ButtonReleaseMask|KeyPressMask|KeyReleaseMask
|SubstructureNotifyMask, &event);
switch (event.type) {
case KeyPress:
ksym = XLookupKeysym(&event.xkey, 0);
switch (ksym) {
case XK_Escape:
done = 1;
break;
case XK_Home:
#ifdef XK_KP_Home
case XK_KP_Home:
#endif
selectEntry(menu, 0);
makeVisible(menu);
break;
case XK_End:
#ifdef XK_KP_End
case XK_KP_End:
#endif
selectEntry(menu, menu->entry_no-1);
makeVisible(menu);
break;
case XK_Up:
#ifdef ARROWLESS_KBD
case XK_k:
#endif
#ifdef XK_KP_Up
case XK_KP_Up:
#endif
if (menu->selected_entry <= 0)
selectEntry(menu, menu->entry_no-1);
else
selectEntry(menu, menu->selected_entry-1);
makeVisible(menu);
break;
case XK_Down:
#ifdef ARROWLESS_KBD
case XK_j:
#endif
#ifdef XK_KP_Down
case XK_KP_Down:
#endif
if (menu->selected_entry<0)
selectEntry(menu, 0);
else if (menu->selected_entry == menu->entry_no-1)
selectEntry(menu, 0);
else if (menu->selected_entry < menu->entry_no-1)
selectEntry(menu, menu->selected_entry+1);
makeVisible(menu);
break;
case XK_Right:
#ifdef ARROWLESS_KBD
case XK_l:
#endif
#ifdef XK_KP_Right
case XK_KP_Right:
#endif
if (menu->selected_entry>=0) {
WMenuEntry *entry;
entry = menu->entries[menu->selected_entry];
if (entry->cascade >= 0 && menu->cascades
&& menu->cascades[entry->cascade]->entry_no > 0) {
XUngrabKeyboard(dpy, CurrentTime);
selectEntry(menu->cascades[entry->cascade], 0);
if (!keyboardMenu(menu->cascades[entry->cascade]))
done = 1;
XGrabKeyboard(dpy, menu->frame->core->window, True,
GrabModeAsync, GrabModeAsync,
CurrentTime);
}
}
break;
case XK_Left:
#ifdef ARROWLESS_KBD
case XK_h:
#endif
#ifdef XK_KP_Left
case XK_KP_Left:
#endif
if (menu->parent!=NULL && menu->parent->selected_entry>=0) {
selectEntry(menu, -1);
move_menus(menu, old_pos_x, old_pos_y);
return True;
}
break;
case XK_Return:
done = 2;
break;
default:
index = check_key(menu, &event.xkey);
if (index>=0) {
selectEntry(menu, index);
}
}
break;
default:
if (event.type==ButtonPress)
done = 1;
WMHandleEvent(&event);
}
}
XUngrabKeyboard(dpy, CurrentTime);
if (done==2 && menu->selected_entry>=0) {
entry = menu->entries[menu->selected_entry];
} else {
entry = NULL;
}
if (entry && entry->callback!=NULL && entry->flags.enabled
&& entry->cascade < 0) {
#if (MENU_BLINK_COUNT > 0)
int sel = menu->selected_entry;
int i;
for (i=0; i<MENU_BLINK_COUNT; i++) {
paintEntry(menu, sel, False);
XSync(dpy, 0);
wusleep(MENU_BLINK_DELAY);
paintEntry(menu, sel, True);
XSync(dpy, 0);
wusleep(MENU_BLINK_DELAY);
}
#endif
selectEntry(menu, -1);
if (!menu->flags.buttoned) {
wMenuUnmap(menu);
move_menus(menu, old_pos_x, old_pos_y);
}
closeCascade(menu);
(*entry->callback)(menu, entry);
} else {
if (!menu->flags.buttoned) {
wMenuUnmap(menu);
move_menus(menu, old_pos_x, old_pos_y);
}
selectEntry(menu, -1);
}
/* returns True if returning from a submenu to a parent menu,
* False if exiting from menu */
return False;
}
void
wMenuMapAt(WMenu *menu, int x, int y, int keyboard)
{
int scr_width = menu->frame->screen_ptr->scr_width;
int scr_height = menu->frame->screen_ptr->scr_height;
if (!menu->flags.realized) {
menu->flags.realized=1;
wMenuRealize(menu);
}
if (!menu->flags.mapped) {
if (wPreferences.wrap_menus) {
if (x<0) x = 0;
if (y<0) y = 0;
if (x+MENUW(menu) > scr_width)
x = scr_width - MENUW(menu);
if (y+MENUH(menu) > scr_height)
y = scr_height - MENUH(menu);
}
XMoveWindow(dpy, menu->frame->core->window, x, y);
menu->frame_x = x;
menu->frame_y = y;
XMapWindow(dpy, menu->frame->core->window);
wRaiseFrame(menu->frame->core);
menu->flags.mapped = 1;
} else {
selectEntry(menu, 0);
}
if (keyboard)
keyboardMenu(menu);
}
void
wMenuMap(WMenu *menu)
{
if (!menu->flags.realized) {
menu->flags.realized=1;
wMenuRealize(menu);
}
if (menu->flags.app_menu && menu->parent==NULL) {
menu->frame_x = menu->frame->screen_ptr->app_menu_x;
menu->frame_y = menu->frame->screen_ptr->app_menu_y;
XMoveWindow(dpy, menu->frame->core->window, menu->frame_x, menu->frame_y);
}
XMapWindow(dpy, menu->frame->core->window);
wRaiseFrame(menu->frame->core);
menu->flags.mapped = 1;
}
void
wMenuUnmap(WMenu *menu)
{
int i;
XUnmapWindow(dpy, menu->frame->core->window);
if (menu->flags.titled && menu->flags.buttoned) {
wFrameWindowHideButton(menu->frame, WFF_RIGHT_BUTTON);
}
menu->flags.buttoned = 0;
menu->flags.mapped = 0;
menu->flags.open_to_left = 0;
for (i=0; i<menu->cascade_no; i++) {
if (menu->cascades[i]!=NULL
&& menu->cascades[i]->flags.mapped
&& !menu->cascades[i]->flags.buttoned) {
wMenuUnmap(menu->cascades[i]);
}
}
menu->selected_entry = -1;
}
void
wMenuPaint(WMenu *menu)
{
int i;
if (!menu->flags.mapped) {
return;
}
/* paint entries */
for (i=0; i<menu->entry_no; i++) {
paintEntry(menu, i, i==menu->selected_entry);
}
}
void
wMenuSetEnabled(WMenu *menu, int index, int enable)
{
if (index>=menu->entry_no) return;
menu->entries[index]->flags.enabled=enable;
paintEntry(menu, index, index==menu->selected_entry);
paintEntry(menu->brother, index, index==menu->selected_entry);
}
/* ====================================================================== */
static void
editEntry(WMenu *menu, WMenuEntry *entry)
{
WTextInput *text;
XEvent event;
WObjDescriptor *desc;
char *t;
int done = 0;
Window old_focus;
int old_revert;
menu->flags.editing = 1;
text = wTextCreate(menu->menu, 1, menu->entry_height * entry->order,
menu->menu->width - 2, menu->entry_height - 1);
wTextPutText(text, entry->text);
XGetInputFocus(dpy, &old_focus, &old_revert);
XSetInputFocus(dpy, text->core->window, RevertToNone, CurrentTime);
if (XGrabKeyboard(dpy, text->core->window, True, GrabModeAsync,
GrabModeAsync, CurrentTime)!=GrabSuccess) {
wwarning(_("could not grab keyboard"));
wTextDestroy(text);
wSetFocusTo(menu->frame->screen_ptr,
menu->frame->screen_ptr->focused_window);
return;
}
while (!done && !text->done) {
XSync(dpy, 0);
XAllowEvents(dpy, AsyncKeyboard|AsyncPointer, CurrentTime);
XSync(dpy, 0);
WMNextEvent(dpy, &event);
if (XFindContext(dpy, event.xany.window, wWinContext,
(XPointer *)&desc)==XCNOENT)
desc = NULL;
if ((desc != NULL) && (desc->handle_anything != NULL)) {
(*desc->handle_anything)(desc, &event);
} else {
switch (event.type) {
case ButtonPress:
XAllowEvents(dpy, ReplayPointer, CurrentTime);
done = 1;
default:
WMHandleEvent(&event);
break;
}
}
}
XSetInputFocus(dpy, old_focus, old_revert, CurrentTime);
wSetFocusTo(menu->frame->screen_ptr,
menu->frame->screen_ptr->focused_window);
t = wTextGetText(text);
/* if !t, the user has canceled editing */
if (t) {
if (entry->text)
wfree(entry->text);
entry->text = wstrdup(t);
menu->flags.realized = 0;
}
wTextDestroy(text);
XUngrabKeyboard(dpy, CurrentTime);
if (t && menu->on_edit)
(*menu->on_edit)(menu, entry);
menu->flags.editing = 0;
if (!menu->flags.realized)
wMenuRealize(menu);
}
static void
selectEntry(WMenu *menu, int entry_no)
{
WMenuEntry *entry;
WMenu *submenu;
int old_entry;
if (menu->entries==NULL)
return;
if (entry_no >= menu->entry_no)
return;
old_entry = menu->selected_entry;
menu->selected_entry = entry_no;
if (old_entry!=entry_no) {
/* unselect previous entry */
if (old_entry>=0) {
paintEntry(menu, old_entry, False);
entry = menu->entries[old_entry];
/* unmap cascade */
if (entry->cascade>=0 && menu->cascades) {
if (!menu->cascades[entry->cascade]->flags.buttoned) {
wMenuUnmap(menu->cascades[entry->cascade]);
}
}
}
if (entry_no<0) {
menu->selected_entry = -1;
return;
}
entry = menu->entries[entry_no];
if (entry->cascade>=0 && menu->cascades && entry->flags.enabled) {
/* Callback for when the submenu is opened.
*/
submenu = menu->cascades[entry->cascade];
if (submenu && submenu->flags.brother)
submenu = submenu->brother;
if (entry->callback) {
/* Only call the callback if the submenu is not yet mapped.
*/
if (menu->flags.brother) {
if (!submenu || !submenu->flags.mapped)
(*entry->callback)(menu->brother, entry);
} else {
if (!submenu || !submenu->flags.buttoned)
(*entry->callback)(menu, entry);
}
}
/* the submenu menu might have changed */
submenu = menu->cascades[entry->cascade];
/* map cascade */
if (!submenu->flags.mapped) {
int x, y;
if (!submenu->flags.realized)
wMenuRealize(submenu);
if (wPreferences.wrap_menus) {
if (menu->flags.open_to_left)
submenu->flags.open_to_left = 1;
if (submenu->flags.open_to_left) {
x = menu->frame_x - MENUW(submenu);
if (x<0) {
x = 0;
submenu->flags.open_to_left = 0;
}
} else {
x = menu->frame_x + MENUW(menu);
if (x + MENUW(submenu)
>= menu->frame->screen_ptr->scr_width) {
x = menu->frame_x - MENUW(submenu);
submenu->flags.open_to_left = 1;
}
}
} else {
x = menu->frame_x + MENUW(menu);
}
if (wPreferences.align_menus) {
y = menu->frame_y;
} else {
y = menu->frame_y + menu->entry_height*entry_no;
if (menu->flags.titled)
y += menu->frame->top_width;
if (menu->cascades[entry->cascade]->flags.titled)
y -= menu->cascades[entry->cascade]->frame->top_width;
}
wMenuMapAt(menu->cascades[entry->cascade], x, y, False);
menu->cascades[entry->cascade]->parent = menu;
} else {
return;
}
}
paintEntry(menu, entry_no, True);
}
}
static WMenu*
findMenu(WScreen *scr, int *x_ret, int *y_ret)
{
WMenu *menu;
WObjDescriptor *desc;
Window root_ret, win, junk_win;
int x, y, wx, wy;
unsigned int mask;
XQueryPointer(dpy, scr->root_win, &root_ret, &win, &x, &y, &wx, &wy,
&mask);
if (win==None) return NULL;
if (XFindContext(dpy, win, wWinContext, (XPointer *)&desc)==XCNOENT)
return NULL;
if (desc->parent_type == WCLASS_MENU) {
menu = (WMenu*)desc->parent;
XTranslateCoordinates(dpy, root_ret, menu->menu->window, wx, wy,
x_ret, y_ret, &junk_win);
return menu;
}
return NULL;
}
static void
closeCascade(WMenu *menu)
{
WMenu *parent=menu->parent;
if (menu->flags.brother
|| (!menu->flags.buttoned
&& (!menu->flags.app_menu||menu->parent!=NULL))) {
selectEntry(menu, -1);
XSync(dpy, 0);
#if (MENU_BLINK_DELAY > 2)
wusleep(MENU_BLINK_DELAY/2);
#endif
wMenuUnmap(menu);
while (parent!=NULL
&& (parent->parent!=NULL || !parent->flags.app_menu
|| parent->flags.brother)
&& !parent->flags.buttoned) {
selectEntry(parent, -1);
wMenuUnmap(parent);
parent = parent->parent;
}
if (parent)
selectEntry(parent, -1);
}
}
static void
closeBrotherCascadesOf(WMenu *menu)
{
WMenu *tmp;
int i;
for (i=0; i<menu->cascade_no; i++) {
if (menu->cascades[i]->flags.brother) {
tmp = menu->cascades[i];
} else {
tmp = menu->cascades[i]->brother;
}
if (tmp->flags.mapped) {
selectEntry(tmp->parent, -1);
closeBrotherCascadesOf(tmp);
break;
}
}
}
#define getEntryAt(menu, x, y) ((y)<0 ? -1 : (y)/(menu->entry_height))
static WMenu*
parentMenu(WMenu *menu)
{
WMenu *parent;
WMenuEntry *entry;
if (menu->flags.buttoned)
return menu;
while (menu->parent && menu->parent->flags.mapped) {
parent = menu->parent;
if (parent->selected_entry < 0)
break;
entry = parent->entries[parent->selected_entry];
if (!entry->flags.enabled || entry->cascade<0 || !parent->cascades ||
parent->cascades[entry->cascade] != menu)
break;
menu = parent;
if (menu->flags.buttoned)
break;
}
return menu;
}
/*
* Will raise the passed menu, if submenu = 0
* If submenu > 0 will also raise all mapped submenus
* until the first buttoned one
* If submenu < 0 will also raise all mapped parent menus
* until the first buttoned one
*/
static void
raiseMenus(WMenu *menu, int submenus)
{
WMenu *submenu;
int i;
if(!menu) return;
wRaiseFrame(menu->frame->core);
if (submenus>0 && menu->selected_entry>=0) {
i = menu->entries[menu->selected_entry]->cascade;
if (i>=0 && menu->cascades) {
submenu = menu->cascades[i];
if (submenu->flags.mapped && !submenu->flags.buttoned)
raiseMenus(submenu, submenus);
}
}
if (submenus<0 && !menu->flags.buttoned &&
menu->parent && menu->parent->flags.mapped)
raiseMenus(menu->parent, submenus);
}
WMenu*
wMenuUnderPointer(WScreen *screen)
{
WObjDescriptor *desc;
Window root_ret, win;
int dummy;
unsigned int mask;
XQueryPointer(dpy, screen->root_win, &root_ret, &win, &dummy, &dummy,
&dummy, &dummy, &mask);
if (win==None) return NULL;
if (XFindContext(dpy, win, wWinContext, (XPointer *)&desc)==XCNOENT)
return NULL;
if (desc->parent_type == WCLASS_MENU)
return (WMenu *)desc->parent;
return NULL;
}
static void
getPointerPosition(WScreen *scr, int *x, int *y)
{
Window root_ret, win;
int wx, wy;
unsigned int mask;
XQueryPointer(dpy, scr->root_win, &root_ret, &win, x, y, &wx, &wy, &mask);
}
static void
getScrollAmount(WMenu *menu, int *hamount, int *vamount)
{
WScreen *scr = menu->menu->screen_ptr;
int menuX1 = menu->frame_x;
int menuY1 = menu->frame_y;
int menuX2 = menu->frame_x + MENUW(menu);
int menuY2 = menu->frame_y + MENUH(menu);
int screenW = scr->scr_width;
int screenH = scr->scr_height;
int xroot, yroot;
*hamount = 0;
*vamount = 0;
getPointerPosition(scr, &xroot, &yroot);
if (xroot <= 1 && menuX1 < 0) {
/* scroll to the right */
*hamount = WMIN(MENU_SCROLL_STEP, abs(menuX1));
} else if (xroot >= screenW-2 && menuX2 > screenW-1) {
/* scroll to the left */
*hamount = WMIN(MENU_SCROLL_STEP, abs(menuX2-screenW-1));
if (*hamount==0)
*hamount = 1;
*hamount = -*hamount;
}
if (yroot <= 1 && menuY1 < 0) {
/* scroll down */
*vamount = WMIN(MENU_SCROLL_STEP, abs(menuY1));
} else if (yroot >= screenH-2 && menuY2 > screenH-1) {
/* scroll up */
*vamount = WMIN(MENU_SCROLL_STEP, abs(menuY2-screenH-2));
*vamount = -*vamount;
}
}
static void
dragScrollMenuCallback(void *data)
{
WMenu *menu = (WMenu*)data;
WScreen *scr = menu->menu->screen_ptr;
WMenu *parent = parentMenu(menu);
int hamount, vamount;
int x, y;
int newSelectedEntry;
getScrollAmount(menu, &hamount, &vamount);
if (hamount != 0 || vamount != 0) {
wMenuMove(parent, parent->frame_x + hamount,
parent->frame_y + vamount, True);
if (findMenu(scr, &x, &y)) {
newSelectedEntry = getEntryAt(menu, x, y);
selectEntry(menu, newSelectedEntry);
} else {
/* Pointer fell outside of menu. If the selected entry is
* not a submenu, unselect it */
if (menu->selected_entry >= 0
&& menu->entries[menu->selected_entry]->cascade<0)
selectEntry(menu, -1);
newSelectedEntry = 0;
}
/* paranoid check */
if (newSelectedEntry >= 0) {
/* keep scrolling */
menu->timer = WMAddTimerHandler(MENU_SCROLL_DELAY,
dragScrollMenuCallback, menu);
} else {
menu->timer = NULL;
}
} else {
/* don't need to scroll anymore */
menu->timer = NULL;
if (findMenu(scr, &x, &y)) {
newSelectedEntry = getEntryAt(menu, x, y);
selectEntry(menu, newSelectedEntry);
}
}
}
static void
scrollMenuCallback(void *data)
{
WMenu *menu = (WMenu*)data;
WMenu *parent = parentMenu(menu);
int hamount = 0; /* amount to scroll */
int vamount = 0;
#ifdef VIRTUAL_DESKTOP
/* don't scroll if it is in vdesk mode */
if (!wPreferences.vedge_thickness)
#endif
getScrollAmount(menu, &hamount, &vamount);
if (hamount != 0 || vamount != 0) {
wMenuMove(parent, parent->frame_x + hamount,
parent->frame_y + vamount, True);
/* keep scrolling */
menu->timer = WMAddTimerHandler(MENU_SCROLL_DELAY,
scrollMenuCallback, menu);
} else {
/* don't need to scroll anymore */
menu->timer = NULL;
}
}
#define MENU_SCROLL_BORDER 5
static int
isPointNearBoder(WMenu *menu, int x, int y)
{
int menuX1 = menu->frame_x;
int menuY1 = menu->frame_y;
int menuX2 = menu->frame_x + MENUW(menu);
int menuY2 = menu->frame_y + MENUH(menu);
int scrXe = menu->menu->screen_ptr->scr_width-1;
int scrYe = menu->menu->screen_ptr->scr_height-1;
int flag = 0;
if (x >= menuX1 && x <= menuX2 && (y < MENU_SCROLL_BORDER
|| y > scrYe-MENU_SCROLL_BORDER))
flag = 1;
else if (y >= menuY1 && y <= menuY2 && (x < MENU_SCROLL_BORDER
|| x > scrXe-MENU_SCROLL_BORDER))
flag = 1;
return flag;
}
typedef struct _delay {
WWindow *wwin;
WMenu *menu;
int ox,oy;
} _delay;
static void
_leaving(_delay *dl)
{
wMenuMove(dl->menu, dl->ox, dl->oy, True);
dl->menu->jump_back=NULL;
dl->menu->menu->screen_ptr->flags.jump_back_pending = 0;
wfree(dl);
}
void
wMenuScroll(WMenu *menu, XEvent *event)
{
WMenu *smenu;
WMenu *omenu = parentMenu(menu);
WScreen *scr = menu->frame->screen_ptr;
int done = 0;
int jump_back = 0;
int old_frame_x = omenu->frame_x;
int old_frame_y = omenu->frame_y;
XEvent ev;
if (omenu->jump_back)
WMDeleteTimerWithClientData(omenu->jump_back);
if ((/*omenu->flags.buttoned &&*/ !wPreferences.wrap_menus)
|| omenu->flags.app_menu) {
jump_back = 1;
}
if (!wPreferences.wrap_menus)
raiseMenus(omenu, True);
else
raiseMenus(menu, False);
if (!menu->timer)
scrollMenuCallback(menu);
while(!done) {
int x, y, on_border, on_x_edge, on_y_edge, on_title;
WMNextEvent(dpy, &ev);
switch (ev.type) {
case EnterNotify:
WMHandleEvent(&ev);
case MotionNotify:
x = (ev.type==MotionNotify) ? ev.xmotion.x_root : ev.xcrossing.x_root;
y = (ev.type==MotionNotify) ? ev.xmotion.y_root : ev.xcrossing.y_root;
/* on_border is != 0 if the pointer is between the menu
* and the screen border and is close enough to the border */
on_border = isPointNearBoder(menu, x, y);
smenu = wMenuUnderPointer(scr);
if ((smenu==NULL && !on_border) || (smenu && parentMenu(smenu)!=omenu)) {
done = 1;
break;
}
on_x_edge = x <= 1 || x >= scr->scr_width - 2;
on_y_edge = y <= 1 || y >= scr->scr_height - 2;
on_border = on_x_edge || on_y_edge;
if (!on_border && !jump_back) {
done = 1;
break;
}
if (menu->timer && (smenu!=menu || (!on_y_edge && !on_x_edge))) {
WMDeleteTimerHandler(menu->timer);
menu->timer = NULL;
}
if (smenu != NULL)
menu = smenu;
if (!menu->timer)
scrollMenuCallback(menu);
break;
case ButtonPress:
/* True if we push on title, or drag the omenu to other position */
on_title = ev.xbutton.x_root >= omenu->frame_x &&
ev.xbutton.x_root <= omenu->frame_x + MENUW(omenu) &&
ev.xbutton.y_root >= omenu->frame_y &&
ev.xbutton.y_root <= omenu->frame_y + omenu->frame->top_width;
WMHandleEvent(&ev);
smenu = wMenuUnderPointer(scr);
if (smenu == NULL || (smenu && smenu->flags.buttoned && smenu != omenu))
done = 1;
else if (smenu==omenu && on_title) {
jump_back = 0;
done = 1;
}
break;
case KeyPress:
done = 1;
default:
WMHandleEvent(&ev);
break;
}
}
if (menu->timer) {
WMDeleteTimerHandler(menu->timer);
menu->timer = NULL;
}
if (jump_back) {
_delay *delayer;
if (!omenu->jump_back) {
delayer=wmalloc(sizeof(_delay));
delayer->menu=omenu;
delayer->ox=old_frame_x;
delayer->oy=old_frame_y;
omenu->jump_back=delayer;
scr->flags.jump_back_pending = 1;
}
else delayer = omenu->jump_back;
WMAddTimerHandler(MENU_JUMP_BACK_DELAY,(WMCallback*)_leaving, delayer);
}
}
static void
menuExpose(WObjDescriptor *desc, XEvent *event)
{
wMenuPaint(desc->parent);
}
typedef struct {
int *delayed_select;
WMenu *menu;
WMHandlerID magic;
} delay_data;
static void
delaySelection(void *data)
{
delay_data *d = (delay_data*)data;
int x, y, entry_no;
WMenu *menu;
d->magic = NULL;
menu = findMenu(d->menu->menu->screen_ptr, &x, &y);
if (menu && (d->menu == menu || d->delayed_select)) {
entry_no = getEntryAt(menu, x, y);
selectEntry(menu, entry_no);
}
if (d->delayed_select)
*(d->delayed_select) = 0;
}
static void
menuMouseDown(WObjDescriptor *desc, XEvent *event)
{
XButtonEvent *bev = &event->xbutton;
WMenu *menu = desc->parent;
WMenu *smenu;
WScreen *scr=menu->frame->screen_ptr;
WMenuEntry *entry=NULL;
XEvent ev;
int close_on_exit=0;
int done=0;
int delayed_select = 0;
int entry_no;
int x, y;
int prevx, prevy;
int old_frame_x = 0;
int old_frame_y = 0;
delay_data d_data = {NULL, NULL, NULL};
if (!wPreferences.wrap_menus) {
smenu = parentMenu(menu);
old_frame_x = smenu->frame_x;
old_frame_y = smenu->frame_y;
} else if (event->xbutton.window == menu->frame->core->window) {
/* This is true if the menu was launched with right click on root window */
delayed_select = 1;
d_data.delayed_select = &delayed_select;
d_data.menu = menu;
d_data.magic = WMAddTimerHandler(wPreferences.dblclick_time,
delaySelection, &d_data);
}
if (menu->flags.inside_handler) {
return;
}
menu->flags.inside_handler = 1;
wRaiseFrame(menu->frame->core);
close_on_exit = (bev->send_event || menu->flags.brother);
smenu = findMenu(scr, &x, &y);
if (!smenu) {
x = -1;
y = -1;
} else {
menu = smenu;
}
if (menu->flags.editing) {
goto byebye;
}
entry_no = getEntryAt(menu, x, y);
if (entry_no>=0) {
entry = menu->entries[entry_no];
if (!close_on_exit && (bev->state & ControlMask) && smenu
&& entry->flags.editable) {
editEntry(smenu, entry);
goto byebye;
} else if (bev->state & ControlMask) {
goto byebye;
}
if (entry->flags.enabled && entry->cascade>=0 && menu->cascades) {
WMenu *submenu = menu->cascades[entry->cascade];
/* map cascade */
if (submenu->flags.mapped && !submenu->flags.buttoned &&
menu->selected_entry!=entry_no) {
wMenuUnmap(submenu);
}
if (!submenu->flags.mapped && !delayed_select) {
selectEntry(menu, entry_no);
} else if (!submenu->flags.buttoned) {
selectEntry(menu, -1);
}
} else if (!delayed_select) {
selectEntry(menu, entry_no);
}
if (!wPreferences.wrap_menus && !wPreferences.scrollable_menus) {
if (!menu->timer)
dragScrollMenuCallback(menu);
}
}
#ifdef VIRTUAL_DESKTOP
if (wPreferences.vedge_thickness) {
wWorkspaceLowerEdge(scr);
}
#endif
prevx = bev->x_root;
prevy = bev->y_root;
while (!done) {
int x, y;
XAllowEvents(dpy, AsyncPointer|SyncPointer, CurrentTime);
WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
|ButtonPressMask, &ev);
switch (ev.type) {
case MotionNotify:
smenu = findMenu(scr, &x, &y);
if (smenu == NULL) {
/* moved mouse out of menu */
if (!delayed_select && d_data.magic) {
WMDeleteTimerHandler(d_data.magic);
d_data.magic = NULL;
}
if (menu==NULL
|| (menu->selected_entry>=0
&& menu->entries[menu->selected_entry]->cascade>=0)) {
prevx = ev.xmotion.x_root;
prevy = ev.xmotion.y_root;
break;
}
selectEntry(menu, -1);
menu = smenu;
prevx = ev.xmotion.x_root;
prevy = ev.xmotion.y_root;
break;
} else if (menu && menu!=smenu
&& (menu->selected_entry<0
|| menu->entries[menu->selected_entry]->cascade<0)) {
selectEntry(menu, -1);
if (!delayed_select && d_data.magic) {
WMDeleteTimerHandler(d_data.magic);
d_data.magic = NULL;
}
} else {
/* hysteresis for item selection */
/* check if the motion was to the side, indicating that
* the user may want to cross to a submenu */
if (!delayed_select && menu) {
int dx;
Bool moved_to_submenu;/* moved to direction of submenu */
dx = abs(prevx - ev.xmotion.x_root);
moved_to_submenu = False;
if (dx > 0 /* if moved enough to the side */
/* maybe a open submenu */
&& menu->selected_entry>=0
/* moving to the right direction */
&& (wPreferences.align_menus
|| ev.xmotion.y_root >= prevy)) {
int index;
index = menu->entries[menu->selected_entry]->cascade;
if (index>=0) {
if (menu->cascades[index]->frame_x>menu->frame_x) {
if (prevx < ev.xmotion.x_root)
moved_to_submenu = True;
} else {
if (prevx > ev.xmotion.x_root)
moved_to_submenu = True;
}
}
}
if (menu != smenu) {
if (d_data.magic) {
WMDeleteTimerHandler(d_data.magic);
}
d_data.magic = NULL;
} else if (moved_to_submenu) {
/* while we are moving, postpone the selection */
if (d_data.magic) {
WMDeleteTimerHandler(d_data.magic);
}
d_data.delayed_select = NULL;
d_data.menu = menu;
d_data.magic = WMAddTimerHandler(MENU_SELECT_DELAY,
delaySelection,
&d_data);
prevx = ev.xmotion.x_root;
prevy = ev.xmotion.y_root;
break;
} else {
if (d_data.magic)
WMDeleteTimerHandler(d_data.magic);
d_data.magic = NULL;
}
}
}
prevx = ev.xmotion.x_root;
prevy = ev.xmotion.y_root;
if (menu!=smenu) {
/* pointer crossed menus */
if (menu && menu->timer) {
WMDeleteTimerHandler(menu->timer);
menu->timer = NULL;
}
if (smenu)
dragScrollMenuCallback(smenu);
}
menu = smenu;
if (!menu->timer)
dragScrollMenuCallback(menu);
if (!delayed_select) {
entry_no = getEntryAt(menu, x, y);
if (entry_no>=0) {
entry = menu->entries[entry_no];
if (entry->flags.enabled && entry->cascade>=0 &&
menu->cascades) {
WMenu *submenu = menu->cascades[entry->cascade];
if (submenu->flags.mapped && !submenu->flags.buttoned
&& menu->selected_entry!=entry_no) {
wMenuUnmap(submenu);
}
}
}
selectEntry(menu, entry_no);
}
break;
case ButtonPress:
break;
case ButtonRelease:
if (ev.xbutton.button == event->xbutton.button)
done=1;
break;
case Expose:
WMHandleEvent(&ev);
#ifdef VIRTUAL_DESKTOP
/* since expose will raise edge up.. I need another ugly hack here */
if (wPreferences.vedge_thickness) {
wWorkspaceLowerEdge(scr);
}
#endif
break;
}
}
if (menu && menu->timer) {
WMDeleteTimerHandler(menu->timer);
menu->timer = NULL;
}
if (d_data.magic!=NULL)
WMDeleteTimerHandler(d_data.magic);
if (menu && menu->selected_entry>=0) {
entry = menu->entries[menu->selected_entry];
if (entry->callback!=NULL && entry->flags.enabled
&& entry->cascade < 0) {
/* blink and erase menu selection */
#if (MENU_BLINK_DELAY > 0)
int sel = menu->selected_entry;
int i;
for (i=0; i<MENU_BLINK_COUNT; i++) {
paintEntry(menu, sel, False);
XSync(dpy, 0);
wusleep(MENU_BLINK_DELAY);
paintEntry(menu, sel, True);
XSync(dpy, 0);
wusleep(MENU_BLINK_DELAY);
}
#endif
/* unmap the menu, it's parents and call the callback */
if (!menu->flags.buttoned &&
(!menu->flags.app_menu||menu->parent!=NULL)) {
closeCascade(menu);
} else {
selectEntry(menu, -1);
}
(*entry->callback)(menu, entry);
/* If the user double clicks an entry, the entry will
* be executed twice, which is not good for things like
* the root menu. So, ignore any clicks that were generated
* while the entry was being executed */
while (XCheckTypedWindowEvent(dpy, menu->menu->window,
ButtonPress, &ev));
} else if (entry->callback!=NULL && entry->cascade<0) {
selectEntry(menu, -1);
} else {
if (entry->cascade>=0 && menu->cascades
&& menu->cascades[entry->cascade]->flags.brother) {
selectEntry(menu, -1);
}
}
}
if (((WMenu*)desc->parent)->flags.brother || close_on_exit || !smenu)
closeCascade(desc->parent);
/* close the cascade windows that should not remain opened */
closeBrotherCascadesOf(desc->parent);
if (!wPreferences.wrap_menus)
wMenuMove(parentMenu(desc->parent), old_frame_x, old_frame_y, True);
byebye:
((WMenu*)desc->parent)->flags.inside_handler = 0;
#ifdef VIRTUAL_DESKTOP
if (wPreferences.vedge_thickness) {
wWorkspaceRaiseEdge(scr);
}
#endif
}
void
wMenuMove(WMenu *menu, int x, int y, int submenus)
{
WMenu *submenu;
int i;
if (!menu) return;
menu->frame_x = x;
menu->frame_y = y;
XMoveWindow(dpy, menu->frame->core->window, x, y);
if (submenus>0 && menu->selected_entry>=0) {
i = menu->entries[menu->selected_entry]->cascade;
if (i>=0 && menu->cascades) {
submenu = menu->cascades[i];
if (submenu->flags.mapped && !submenu->flags.buttoned) {
if (wPreferences.align_menus) {
wMenuMove(submenu, x + MENUW(menu), y, submenus);
} else {
wMenuMove(submenu, x+ MENUW(menu),
y + submenu->entry_height*menu->selected_entry,
submenus);
}
}
}
}
if (submenus<0 && menu->parent!=NULL && menu->parent->flags.mapped &&
!menu->parent->flags.buttoned) {
if (wPreferences.align_menus) {
wMenuMove(menu->parent, x - MENUW(menu->parent), y, submenus);
} else {
wMenuMove(menu->parent, x - MENUW(menu->parent), menu->frame_y
- menu->parent->entry_height*menu->parent->selected_entry,
submenus);
}
}
}
static void
changeMenuLevels(WMenu *menu, int lower)
{
int i;
if (!lower) {
ChangeStackingLevel(menu->frame->core, (!menu->parent ? WMMainMenuLevel
: WMSubmenuLevel));
wRaiseFrame(menu->frame->core);
menu->flags.lowered = 0;
} else {
ChangeStackingLevel(menu->frame->core, WMNormalLevel);
wLowerFrame(menu->frame->core);
menu->flags.lowered = 1;
}
for (i=0; i<menu->cascade_no; i++) {
if (menu->cascades[i]
&& !menu->cascades[i]->flags.buttoned
&& menu->cascades[i]->flags.lowered!=lower) {
changeMenuLevels(menu->cascades[i], lower);
}
}
}
static void
menuTitleDoubleClick(WCoreWindow *sender, void *data, XEvent *event)
{
WMenu *menu = data;
int lower;
if (event->xbutton.state & MOD_MASK) {
if (menu->flags.lowered) {
lower = 0;
} else {
lower = 1;
}
changeMenuLevels(menu, lower);
}
}
static void
menuTitleMouseDown(WCoreWindow *sender, void *data, XEvent *event)
{
WMenu *menu = data;
WMenu *tmp;
XEvent ev;
int x=menu->frame_x, y=menu->frame_y;
int dx=event->xbutton.x_root, dy=event->xbutton.y_root;
int i, lower;
Bool started;
/* can't touch the menu copy */
if (menu->flags.brother)
return;
if (event->xbutton.button != Button1 && event->xbutton.button != Button2)
return;
if (event->xbutton.state & MOD_MASK) {
wLowerFrame(menu->frame->core);
lower = 1;
} else {
wRaiseFrame(menu->frame->core);
lower = 0;
}
tmp = menu;
/* lower/raise all submenus */
while (1) {
if (tmp->selected_entry>=0 && tmp->cascades
&& tmp->entries[tmp->selected_entry]->cascade>=0) {
tmp = tmp->cascades[tmp->entries[tmp->selected_entry]->cascade];
if (!tmp || !tmp->flags.mapped)
break;
if (lower)
wLowerFrame(tmp->frame->core);
else
wRaiseFrame(tmp->frame->core);
} else {
break;
}
}
/* tear off the menu if it's a root menu or a cascade
application menu */
if (!menu->flags.buttoned && !menu->flags.brother
&& (!menu->flags.app_menu||menu->parent!=NULL)) {
menu->flags.buttoned=1;
wFrameWindowShowButton(menu->frame, WFF_RIGHT_BUTTON);
if (menu->parent) {
/* turn off selected menu entry in parent menu */
selectEntry(menu->parent, -1);
/* make parent map the copy in place of the original */
for (i=0; i<menu->parent->cascade_no; i++) {
if (menu->parent->cascades[i] == menu) {
menu->parent->cascades[i] = menu->brother;
break;
}
}
}
}
started = False;
while(1) {
WMMaskEvent(dpy, ButtonMotionMask|ButtonReleaseMask|ButtonPressMask
|ExposureMask, &ev);
switch (ev.type) {
case MotionNotify:
if (started) {
x += ev.xmotion.x_root - dx;
y += ev.xmotion.y_root - dy;
dx = ev.xmotion.x_root;
dy = ev.xmotion.y_root;
wMenuMove(menu, x, y, True);
} else {
if (abs(ev.xmotion.x_root - dx) > MOVE_THRESHOLD
|| abs(ev.xmotion.y_root - dy) > MOVE_THRESHOLD) {
started = True;
XGrabPointer(dpy, menu->frame->titlebar->window, False,
ButtonMotionMask|ButtonReleaseMask
|ButtonPressMask,
GrabModeAsync, GrabModeAsync, None,
wCursor[WCUR_MOVE], CurrentTime);
}
}
break;
case ButtonPress:
break;
case ButtonRelease:
if (ev.xbutton.button != event->xbutton.button)
break;
XUngrabPointer(dpy, CurrentTime);
return;
default:
WMHandleEvent(&ev);
break;
}
}
}
/*
*----------------------------------------------------------------------
* menuCloseClick--
* Handles mouse click on the close button of menus. The menu is
* closed when the button is clicked.
*
* Side effects:
* The closed menu is reinserted at it's parent menus
* cascade list.
*----------------------------------------------------------------------
*/
static void
menuCloseClick(WCoreWindow *sender, void *data, XEvent *event)
{
WMenu *menu = (WMenu*)data;
WMenu *parent = menu->parent;
int i;
if (parent) {
for (i=0; i<parent->cascade_no; i++) {
/* find the entry that points to the copy */
if (parent->cascades[i] == menu->brother) {
/* make it point to the original */
parent->cascades[i] = menu;
menu->parent = parent;
break;
}
}
}
wMenuUnmap(menu);
}
static void
saveMenuInfo(proplist_t dict, WMenu *menu, proplist_t key)
{
proplist_t value, list;
char buffer[256];
sprintf(buffer, "%i,%i", menu->frame_x, menu->frame_y);
value = PLMakeString(buffer);
list = PLMakeArrayFromElements(value, NULL);
if (menu->flags.lowered)
PLAppendArrayElement(list, PLMakeString("lowered"));
PLInsertDictionaryEntry(dict, key, list);
PLRelease(value);
PLRelease(list);
}
void
wMenuSaveState(WScreen *scr)
{
proplist_t menus, key;
int save_menus = 0;
menus = PLMakeDictionaryFromEntries(NULL, NULL, NULL);
#ifndef LITE
if (scr->switch_menu && scr->switch_menu->flags.buttoned) {
key = PLMakeString("SwitchMenu");
saveMenuInfo(menus, scr->switch_menu, key);
PLRelease(key);
save_menus = 1;
}
if (saveMenuRecurs(menus, scr, scr->root_menu))
save_menus = 1;
#endif /* !LITE */
if (scr->workspace_menu && scr->workspace_menu->flags.buttoned) {
key = PLMakeString("WorkspaceMenu");
saveMenuInfo(menus, scr->workspace_menu, key);
PLRelease(key);
save_menus = 1;
}
if (save_menus) {
key = PLMakeString("Menus");
PLInsertDictionaryEntry(scr->session_state, key, menus);
PLRelease(key);
}
PLRelease(menus);
}
#ifndef LITE
static Bool
getMenuPath(WMenu *menu, char *buffer, int bufSize)
{
Bool ok = True;
int len = 0;
if (!menu->flags.titled || !menu->frame->title[0])
return False;
len = strlen(menu->frame->title);
if (len >= bufSize)
return False;
if (menu->parent) {
ok = getMenuPath(menu->parent, buffer, bufSize - len - 1);
if (!ok)
return False;
}
strcat(buffer, "\\");
strcat(buffer, menu->frame->title);
return True;
}
static Bool
saveMenuRecurs(proplist_t menus, WScreen *scr, WMenu *menu)
{
proplist_t key;
int save_menus = 0, i;
char buffer[512];
Bool ok = True;
if (menu->flags.brother)
menu = menu->brother;
if (menu->flags.buttoned && menu != scr->switch_menu) {
buffer[0] = '\0';
ok = getMenuPath(menu, buffer, 510);
if (ok) {
key = PLMakeString(buffer);
saveMenuInfo(menus, menu, key);
PLRelease(key);
save_menus = 1;
}
}
if (ok) {
for (i = 0; i < menu->cascade_no; i++) {
if (saveMenuRecurs(menus, scr, menu->cascades[i]))
save_menus = 1;
}
}
return save_menus;
}
#endif /* !LITE */
#define COMPLAIN(key) wwarning(_("bad value in menus state info:%s"), key)
static Bool
getMenuInfo(proplist_t info, int *x, int *y, Bool *lowered)
{
proplist_t pos;
*lowered = False;
if (PLIsArray(info)) {
proplist_t flags;
pos = PLGetArrayElement(info, 0);
flags = PLGetArrayElement(info, 1);
if (flags != NULL && PLIsString(flags) && PLGetString(flags) != NULL
&& strcmp(PLGetString(flags), "lowered") == 0) {
*lowered = True;
}
} else {
pos = info;
}
if (pos != NULL && PLIsString(pos)) {
if (sscanf(PLGetString(pos), "%i,%i", x, y)!=2)
COMPLAIN("Position");
} else {
COMPLAIN("(position, flags...)");
return False;
}
return True;
}
static int
restoreMenu(WScreen *scr, proplist_t menu, int which)
{
int x, y;
Bool lowered = False;
WMenu *pmenu = NULL;
if (!menu)
return False;
if (!getMenuInfo(menu, &x, &y, &lowered))
return False;
#ifndef LITE
if (which & WSS_SWITCHMENU) {
OpenSwitchMenu(scr, x, y, False);
pmenu = scr->switch_menu;
}
#endif /* !LITE */
if (pmenu) {
int width = MENUW(pmenu);
int height = MENUH(pmenu);
if (lowered) {
changeMenuLevels(pmenu, True);
}
x = (x < -width) ? 0 : x;
x = (x > scr->scr_width) ? scr->scr_width - width : x;
y = (y < 0) ? 0 : y;
y = (y > scr->scr_height) ? scr->scr_height - height : y;
wMenuMove(pmenu, x, y, True);
pmenu->flags.buttoned = 1;
wFrameWindowShowButton(pmenu->frame, WFF_RIGHT_BUTTON);
return True;
}
return False;
}
#ifndef LITE
static int
restoreMenuRecurs(WScreen *scr, proplist_t menus, WMenu *menu, char *path)
{
proplist_t key, entry;
char buffer[512];
int i, x, y, res;
Bool lowered;
if (strlen(path) + strlen(menu->frame->title) > 510)
return False;
sprintf(buffer, "%s\\%s", path, menu->frame->title);
key = PLMakeString(buffer);
entry = PLGetDictionaryEntry(menus, key);
res = False;
if (entry && getMenuInfo(entry, &x, &y, &lowered)) {
if (!menu->flags.mapped) {
int width = MENUW(menu);
int height = MENUH(menu);
wMenuMapAt(menu, x, y, False);
if (menu->parent) {
/* make parent map the copy in place of the original */
for (i=0; i<menu->parent->cascade_no; i++) {
if (menu->parent->cascades[i] == menu) {
menu->parent->cascades[i] = menu->brother;
break;
}
}
}
if (lowered) {
changeMenuLevels(menu, True);
}
x = (x < -width) ? 0 : x;
x = (x > scr->scr_width) ? scr->scr_width - width : x;
y = (y < 0) ? 0 : y;
y = (y > scr->scr_height) ? scr->scr_height - height : y;
wMenuMove(menu, x, y, True);
menu->flags.buttoned = 1;
wFrameWindowShowButton(menu->frame, WFF_RIGHT_BUTTON);
res = True;
}
}
PLRelease(key);
for (i=0; i<menu->cascade_no; i++) {
if (restoreMenuRecurs(scr, menus, menu->cascades[i], buffer) != False)
res = True;
}
return res;
}
#endif /* !LITE */
void
wMenuRestoreState(WScreen *scr)
{
proplist_t menus, menu, key, skey;
key = PLMakeString("Menus");
menus = PLGetDictionaryEntry(scr->session_state, key);
PLRelease(key);
if (!menus)
return;
/* restore menus */
skey = PLMakeString("SwitchMenu");
menu = PLGetDictionaryEntry(menus, skey);
PLRelease(skey);
restoreMenu(scr, menu, WSS_SWITCHMENU);
#ifndef LITE
if (!scr->root_menu) {
OpenRootMenu(scr, scr->scr_width*2, 0, False);
wMenuUnmap(scr->root_menu);
}
restoreMenuRecurs(scr, menus, scr->root_menu, "");
#endif /* !LITE */
}
void
OpenWorkspaceMenu(WScreen *scr, int x, int y)
{
WMenu *menu, *parent;
WMenuEntry *entry;
#ifndef LITE
if (!scr->root_menu) {
OpenRootMenu(scr, scr->scr_width*2, 0, False);
wMenuUnmap(scr->root_menu);
}
#endif
menu = scr->workspace_menu;
if (menu) {
if (menu->flags.mapped) {
if (!menu->flags.buttoned) {
wMenuUnmap(menu);
parent = menu->parent;
if (parent && parent->selected_entry >= 0) {
entry = parent->entries[parent->selected_entry];
if (parent->cascades[entry->cascade] == menu) {
selectEntry(parent, -1);
wMenuMapAt(menu, x, y, False);
}
}
} else {
wRaiseFrame(menu->frame->core);
wMenuMapCopyAt(menu, x, y);
}
} else {
wMenuMapAt(menu, x, y, False);
}
}
}