Change to the linux kernel coding style
[wmaker-crm.git] / WPrefs.app / editmenu.c
dissimilarity index 93%
index 508f48e..f5b84aa 100644 (file)
-/* editmenu.c - editable menus
- *
- *  WPrefs - Window Maker Preferences Program
- *
- *  Copyright (c) 2000-2003 Alfredo K. Kojima
- *
- *  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 <WINGs/WINGsP.h>
-#include <WINGs/WUtil.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <assert.h>
-#include <ctype.h>
-
-#include "editmenu.h"
-
-typedef struct W_EditMenuItem {
-    W_Class widgetClass;
-    WMView *view;
-
-    struct W_EditMenu *parent;
-
-    char *label;
-    WMPixmap *pixmap;                 /* pixmap to show at left */
-
-    void *data;
-    WMCallback *destroyData;
-
-    struct W_EditMenu *submenu;               /* if it's a cascade, NULL otherwise */
-
-    struct {
-        unsigned isTitle:1;
-        unsigned isHighlighted:1;
-    } flags;
-} EditMenuItem;
-
-
-typedef struct W_EditMenu {
-    W_Class widgetClass;
-    WMView *view;
-
-    struct W_EditMenu *parent;
-
-    WMArray *items; /* EditMenuItem */
-
-    EditMenuItem *selectedItem;
-
-    WMTextField *tfield;
-
-    WMButton *closeB;
-
-    int titleHeight;
-    int itemHeight;
-
-    WEditMenuDelegate *delegate;
-
-    WMTextFieldDelegate *tdelegate;
-
-    /* item dragging */
-    int draggedItem;
-    int dragX, dragY;
-
-    /* only for non-standalone menu */
-    WMSize maxSize;
-    WMSize minSize;
-
-    struct {
-        unsigned standalone:1;
-        unsigned isTitled:1;
-
-        unsigned acceptsDrop:1;
-        unsigned isFactory:1;
-        unsigned isSelectable:1;
-        unsigned isEditable:1;
-
-        unsigned isTornOff:1;
-
-        unsigned isDragging:1;
-        unsigned isEditing:1;
-
-        unsigned wasMapped:1;
-    } flags;
-} EditMenu;
-
-
-
-/******************** WEditMenuItem ********************/
-
-static void destroyEditMenuItem(WEditMenuItem *iPtr);
-static void paintEditMenuItem(WEditMenuItem *iPtr);
-static void handleItemEvents(XEvent *event, void *data);
-
-static void handleItemClick(XEvent *event, void *data);
-
-
-static W_Class EditMenuItemClass = 0;
-
-
-W_Class
-InitEditMenuItem(WMScreen *scr)
-{
-    /* register our widget with WINGs and get our widget class ID */
-    if (!EditMenuItemClass) {
-        EditMenuItemClass = W_RegisterUserWidget();
-    }
-
-    return EditMenuItemClass;
-}
-
-
-WEditMenuItem*
-WCreateEditMenuItem(WMWidget *parent, char *title, Bool isTitle)
-{
-    WEditMenuItem *iPtr;
-    WMScreen *scr = WMWidgetScreen(parent);
-
-    if (!EditMenuItemClass)
-        InitEditMenuItem(scr);
-
-
-    iPtr = wmalloc(sizeof(WEditMenuItem));
-
-    memset(iPtr, 0, sizeof(WEditMenuItem));
-
-    iPtr->widgetClass = EditMenuItemClass;
-
-    iPtr->view = W_CreateView(W_VIEW(parent));
-    if (!iPtr->view) {
-        wfree(iPtr);
-        return NULL;
-    }
-    iPtr->view->self = iPtr;
-
-    iPtr->parent = parent;
-
-    iPtr->label = wstrdup(title);
-
-    iPtr->flags.isTitle = isTitle;
-
-    if (isTitle) {
-        WMSetWidgetBackgroundColor(iPtr, WMBlackColor(scr));
-    }
-
-    WMCreateEventHandler(iPtr->view, ExposureMask|StructureNotifyMask,
-                         handleItemEvents, iPtr);
-
-    WMCreateEventHandler(iPtr->view, ButtonPressMask|ButtonReleaseMask
-                         |ButtonMotionMask, handleItemClick, iPtr);
-
-
-    return iPtr;
-}
-
-char*
-WGetEditMenuItemTitle(WEditMenuItem *item)
-{
-    return item->label;
-}
-
-void*
-WGetEditMenuItemData(WEditMenuItem *item)
-{
-    return item->data;
-}
-
-
-void
-WSetEditMenuItemData(WEditMenuItem *item, void *data, WMCallback *destroyer)
-{
-    item->data = data;
-    item->destroyData = destroyer;
-}
-
-
-void
-WSetEditMenuItemImage(WEditMenuItem *item, WMPixmap *pixmap)
-{
-    if (item->pixmap)
-        WMReleasePixmap(item->pixmap);
-    item->pixmap = WMRetainPixmap(pixmap);
-}
-
-
-static void
-paintEditMenuItem(WEditMenuItem *iPtr)
-{
-    WMScreen *scr = WMWidgetScreen(iPtr);
-    WMColor *color;
-    Window win = W_VIEW(iPtr)->window;
-    int w = W_VIEW(iPtr)->size.width;
-    int h = W_VIEW(iPtr)->size.height;
-    WMFont *font = (iPtr->flags.isTitle ? scr->boldFont : scr->normalFont);
-
-    if (!iPtr->view->flags.realized)
-        return;
-
-    color = scr->black;
-    if (iPtr->flags.isTitle && !iPtr->flags.isHighlighted) {
-        color = scr->white;
-    }
-
-
-    XClearWindow(scr->display, win);
-
-    W_DrawRelief(scr, win, 0, 0, w+1, h, WRRaised);
-
-    WMDrawString(scr, win, color, font, 5, 3, iPtr->label, strlen(iPtr->label));
-
-    if (iPtr->pixmap) {
-        WMSize size = WMGetPixmapSize(iPtr->pixmap);
-
-        WMDrawPixmap(iPtr->pixmap, win, w - size.width - 5,
-                     (h - size.height)/2);
-    } else if (iPtr->submenu) {
-        /* draw the cascade indicator */
-        XDrawLine(scr->display,win,WMColorGC(scr->darkGray),
-                  w-11, 6, w-6, h/2-1);
-        XDrawLine(scr->display,win,WMColorGC(scr->white),
-                  w-11, h-8, w-6, h/2-1);
-        XDrawLine(scr->display,win,WMColorGC(scr->black),
-                  w-12, 6, w-12, h-8);
-    }
-}
-
-
-static void
-highlightItem(WEditMenuItem *iPtr, Bool high)
-{
-    if (iPtr->flags.isTitle)
-        return;
-
-    iPtr->flags.isHighlighted = high;
-
-    if (high) {
-        WMSetWidgetBackgroundColor(iPtr, WMWhiteColor(WMWidgetScreen(iPtr)));
-    } else {
-        if (!iPtr->flags.isTitle) {
-            WMSetWidgetBackgroundColor(iPtr,
-                                       WMGrayColor(WMWidgetScreen(iPtr)));
-        } else {
-            WMSetWidgetBackgroundColor(iPtr,
-                                       WMBlackColor(WMWidgetScreen(iPtr)));
-        }
-    }
-}
-
-
-static int
-getItemTextWidth(WEditMenuItem *iPtr)
-{
-    WMScreen *scr = WMWidgetScreen(iPtr);
-
-    if (iPtr->flags.isTitle) {
-        return WMWidthOfString(scr->boldFont, iPtr->label,
-                               strlen(iPtr->label));
-    } else {
-        return WMWidthOfString(scr->normalFont, iPtr->label,
-                               strlen(iPtr->label));
-    }
-}
-
-
-
-static void
-handleItemEvents(XEvent *event, void *data)
-{
-    WEditMenuItem *iPtr = (WEditMenuItem*)data;
-
-    switch (event->type) {
-    case Expose:
-        if (event->xexpose.count!=0)
-            break;
-        paintEditMenuItem(iPtr);
-        break;
-
-    case DestroyNotify:
-        destroyEditMenuItem(iPtr);
-        break;
-    }
-}
-
-
-static void
-destroyEditMenuItem(WEditMenuItem *iPtr)
-{
-    if (iPtr->label)
-        wfree(iPtr->label);
-    if (iPtr->data && iPtr->destroyData)
-        (*iPtr->destroyData)(iPtr->data);
-    if (iPtr->submenu)
-        WMDestroyWidget(iPtr->submenu);
-
-    wfree(iPtr);
-}
-
-
-
-/******************** WEditMenu *******************/
-
-static void destroyEditMenu(WEditMenu *mPtr);
-
-static void updateMenuContents(WEditMenu *mPtr);
-
-static void handleEvents(XEvent *event, void *data);
-
-static void editItemLabel(WEditMenuItem *item);
-
-static void stopEditItem(WEditMenu *menu, Bool apply);
-
-static void deselectItem(WEditMenu *menu);
-
-
-static W_Class EditMenuClass = 0;
-
-
-W_Class
-InitEditMenu(WMScreen *scr)
-{
-    /* register our widget with WINGs and get our widget class ID */
-    if (!EditMenuClass) {
-
-        EditMenuClass = W_RegisterUserWidget();
-    }
-
-    return EditMenuClass;
-}
-
-
-
-typedef struct {
-    int flags;
-    int window_style;
-    int window_level;
-    int reserved;
-    Pixmap miniaturize_pixmap;         /* pixmap for miniaturize button */
-    Pixmap close_pixmap;               /* pixmap for close button */
-    Pixmap miniaturize_mask;           /* miniaturize pixmap mask */
-    Pixmap close_mask;                 /* close pixmap mask */
-    int extra_flags;
-} GNUstepWMAttributes;
-
-
-#define GSWindowStyleAttr       (1<<0)
-#define GSWindowLevelAttr       (1<<1)
-
-
-static void
-writeGNUstepWMAttr(WMScreen *scr, Window window, GNUstepWMAttributes *attr)
-{
-    unsigned long data[9];
-
-    /* handle idiot compilers where array of CARD32 != struct of CARD32 */
-    data[0] = attr->flags;
-    data[1] = attr->window_style;
-    data[2] = attr->window_level;
-    data[3] = 0;                       /* reserved */
-    /* The X protocol says XIDs are 32bit */
-    data[4] = attr->miniaturize_pixmap;
-    data[5] = attr->close_pixmap;
-    data[6] = attr->miniaturize_mask;
-    data[7] = attr->close_mask;
-    data[8] = attr->extra_flags;
-    XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
-                    32, PropModeReplace,  (unsigned char *)data, 9);
-}
-
-
-static void
-realizeObserver(void *self, WMNotification *not)
-{
-    WEditMenu *menu = (WEditMenu*)self;
-    GNUstepWMAttributes attribs;
-
-    memset(&attribs, 0, sizeof(GNUstepWMAttributes));
-    attribs.flags = GSWindowStyleAttr|GSWindowLevelAttr;
-    attribs.window_style = WMBorderlessWindowMask;
-    attribs.window_level = WMSubmenuWindowLevel;
-
-    writeGNUstepWMAttr(WMWidgetScreen(menu), menu->view->window, &attribs);
-}
-
-
-static void
-itemSelectObserver(void *self, WMNotification *notif)
-{
-    WEditMenu *menu = (WEditMenu*)self;
-    WEditMenu *rmenu;
-
-    rmenu = (WEditMenu*)WMGetNotificationObject(notif);
-    /* check whether rmenu is from the same hierarchy of menu? */
-
-    if (rmenu == menu) {
-        return;
-    }
-
-    if (menu->selectedItem) {
-        if (!menu->selectedItem->submenu)
-            deselectItem(menu);
-        if (menu->flags.isEditing)
-            stopEditItem(menu, False);
-    }
-}
-
-
-static WEditMenu*
-makeEditMenu(WMScreen *scr, WMWidget *parent, char *title)
-{
-    WEditMenu *mPtr;
-    WEditMenuItem *item;
-
-    if (!EditMenuClass)
-        InitEditMenu(scr);
-
-
-    mPtr = wmalloc(sizeof(WEditMenu));
-    memset(mPtr, 0, sizeof(WEditMenu));
-
-    mPtr->widgetClass = EditMenuClass;
-
-    if (parent) {
-        mPtr->view = W_CreateView(W_VIEW(parent));
-        mPtr->flags.standalone = 0;
-    } else {
-        mPtr->view = W_CreateTopView(scr);
-        mPtr->flags.standalone = 1;
-    }
-    if (!mPtr->view) {
-        wfree(mPtr);
-        return NULL;
-    }
-    mPtr->view->self = mPtr;
-
-    mPtr->flags.isSelectable = 1;
-    mPtr->flags.isEditable = 1;
-
-    W_SetViewBackgroundColor(mPtr->view, scr->darkGray);
-
-    WMAddNotificationObserver(realizeObserver, mPtr,
-                              WMViewRealizedNotification, mPtr->view);
-
-    WMAddNotificationObserver(itemSelectObserver, mPtr,
-                              "EditMenuItemSelected", NULL);
-
-    mPtr->items = WMCreateArray(4);
-
-    WMCreateEventHandler(mPtr->view, ExposureMask|StructureNotifyMask,
-                         handleEvents, mPtr);
-
-
-    if (title != NULL) {
-        item = WCreateEditMenuItem(mPtr, title, True);
-
-        WMMapWidget(item);
-        WMAddToArray(mPtr->items, item);
-
-        mPtr->flags.isTitled = 1;
-    }
-
-    mPtr->itemHeight = WMFontHeight(scr->normalFont) + 6;
-    mPtr->titleHeight = WMFontHeight(scr->boldFont) + 8;
-
-    updateMenuContents(mPtr);
-
-    return mPtr;
-}
-
-
-WEditMenu*
-WCreateEditMenu(WMScreen *scr, char *title)
-{
-    return makeEditMenu(scr, NULL, title);
-}
-
-
-WEditMenu*
-WCreateEditMenuPad(WMWidget *parent)
-{
-    return makeEditMenu(WMWidgetScreen(parent), parent, NULL);
-}
-
-
-void
-WSetEditMenuDelegate(WEditMenu *mPtr, WEditMenuDelegate *delegate)
-{
-    mPtr->delegate = delegate;
-}
-
-
-WEditMenuItem*
-WInsertMenuItemWithTitle(WEditMenu *mPtr, int index, char *title)
-{
-    WEditMenuItem *item;
-
-    item = WCreateEditMenuItem(mPtr, title, False);
-
-    WMMapWidget(item);
-
-    if (index >= WMGetArrayItemCount(mPtr->items)) {
-        WMAddToArray(mPtr->items, item);
-    } else {
-        if (index < 0)
-            index = 0;
-        if (mPtr->flags.isTitled)
-            index++;
-        WMInsertInArray(mPtr->items, index, item);
-    }
-
-    updateMenuContents(mPtr);
-
-    return item;
-}
-
-
-WEditMenuItem*
-WGetEditMenuItem(WEditMenu *mPtr, int index)
-{
-    if (index >= WMGetArrayItemCount(mPtr->items))
-        return NULL;
-
-    return WMGetFromArray(mPtr->items, index + (mPtr->flags.isTitled ? 1 : 0));
-}
-
-
-WEditMenuItem*
-WAddMenuItemWithTitle(WEditMenu *mPtr, char *title)
-{
-    return WInsertMenuItemWithTitle(mPtr, WMGetArrayItemCount(mPtr->items),
-                                    title);
-}
-
-
-
-void
-WSetEditMenuTitle(WEditMenu *mPtr, char *title)
-{
-    WEditMenuItem *item;
-
-    item = WMGetFromArray(mPtr->items, 0);
-
-    wfree(item->label);
-    item->label = wstrdup(title);
-
-    updateMenuContents(mPtr);
-
-    WMRedisplayWidget(item);
-}
-
-
-
-char*
-WGetEditMenuTitle(WEditMenu *mPtr)
-{
-    WEditMenuItem *item;
-
-    item = WMGetFromArray(mPtr->items, 0);
-
-    return item->label;
-}
-
-
-void
-WSetEditMenuAcceptsDrop(WEditMenu *mPtr, Bool flag)
-{
-    mPtr->flags.acceptsDrop = flag;
-}
-
-
-void
-WSetEditMenuSubmenu(WEditMenu *mPtr, WEditMenuItem *item, WEditMenu *submenu)
-{
-    item->submenu = submenu;
-    submenu->parent = mPtr;
-
-    paintEditMenuItem(item);
-}
-
-
-WEditMenu*
-WGetEditMenuSubmenu(WEditMenu *mPtr, WEditMenuItem *item)
-{
-    return item->submenu;
-}
-
-
-void
-WRemoveEditMenuItem(WEditMenu *mPtr, WEditMenuItem *item)
-{
-    if (WMRemoveFromArray(mPtr->items, item) != 0) {
-        updateMenuContents(mPtr);
-    }
-}
-
-
-void
-WSetEditMenuSelectable(WEditMenu *mPtr, Bool flag)
-{
-    mPtr->flags.isSelectable = flag;
-}
-
-
-void
-WSetEditMenuEditable(WEditMenu *mPtr, Bool flag)
-{
-    mPtr->flags.isEditable = flag;
-}
-
-
-void
-WSetEditMenuIsFactory(WEditMenu *mPtr, Bool flag)
-{
-    mPtr->flags.isFactory = flag;
-}
-
-
-void
-WSetEditMenuMinSize(WEditMenu *mPtr, WMSize size)
-{
-    mPtr->minSize.width = size.width;
-    mPtr->minSize.height = size.height;
-}
-
-
-void
-WSetEditMenuMaxSize(WEditMenu *mPtr, WMSize size)
-{
-    mPtr->maxSize.width = size.width;
-    mPtr->maxSize.height = size.height;
-}
-
-
-WMPoint
-WGetEditMenuLocationForSubmenu(WEditMenu *mPtr, WEditMenu *submenu)
-{
-    WMArrayIterator iter;
-    WEditMenuItem *item;
-    int y;
-
-    if (mPtr->flags.isTitled)
-        y = -mPtr->titleHeight;
-    else
-        y = 0;
-    WM_ITERATE_ARRAY(mPtr->items, item, iter) {
-        if (item->submenu == submenu) {
-            WMPoint pt = WMGetViewScreenPosition(mPtr->view);
-
-            return wmkpoint(pt.x + mPtr->view->size.width, pt.y + y);
-        }
-        y += W_VIEW_HEIGHT(item->view);
-    }
-
-    puts("invalid submenu passed to WGetEditMenuLocationForSubmenu()");
-
-    return wmkpoint(0,0);
-}
-
-
-
-static void
-closeMenuAction(WMWidget *w, void *data)
-{
-    WEditMenu *menu = (WEditMenu*)data;
-
-    WMAddIdleHandler(WMDestroyWidget, menu->closeB);
-    menu->closeB = NULL;
-
-    WEditMenuHide(menu);
-}
-
-
-void
-WTearOffEditMenu(WEditMenu *menu, WEditMenu *submenu)
-{
-    WEditMenuItem *item;
-
-    submenu->flags.isTornOff = 1;
-
-    item = (WEditMenuItem*)WMGetFromArray(submenu->items, 0);
-
-    submenu->closeB = WMCreateCommandButton(item);
-    WMResizeWidget(submenu->closeB, 15, 15);
-    WMMoveWidget(submenu->closeB, W_VIEW(submenu)->size.width - 20, 3);
-    WMRealizeWidget(submenu->closeB);
-    WMSetButtonText(submenu->closeB, "X");
-    WMSetButtonAction(submenu->closeB, closeMenuAction, submenu);
-    WMMapWidget(submenu->closeB);
-
-    if (menu->selectedItem && menu->selectedItem->submenu == submenu)
-        deselectItem(menu);
-}
-
-
-
-Bool
-WEditMenuIsTornOff(WEditMenu *mPtr)
-{
-    return mPtr->flags.isTornOff;
-}
-
-
-
-void
-WEditMenuHide(WEditMenu *mPtr)
-{
-    WEditMenuItem *item;
-    int i = 0;
-
-    if (WMWidgetIsMapped(mPtr)) {
-        WMUnmapWidget(mPtr);
-        mPtr->flags.wasMapped = 1;
-    } else {
-        mPtr->flags.wasMapped = 0;
-    }
-    while ((item = WGetEditMenuItem(mPtr, i++))) {
-        WEditMenu *submenu;
-
-        submenu = WGetEditMenuSubmenu(mPtr, item);
-        if (submenu) {
-            WEditMenuHide(submenu);
-        }
-    }
-}
-
-
-
-void
-WEditMenuUnhide(WEditMenu *mPtr)
-{
-    WEditMenuItem *item;
-    int i = 0;
-
-    if (mPtr->flags.wasMapped) {
-        WMMapWidget(mPtr);
-    }
-    while ((item = WGetEditMenuItem(mPtr, i++))) {
-        WEditMenu *submenu;
-
-        submenu = WGetEditMenuSubmenu(mPtr, item);
-        if (submenu) {
-            WEditMenuUnhide(submenu);
-        }
-    }
-}
-
-
-void
-WEditMenuShowAt(WEditMenu *menu, int x, int y)
-{
-    XSizeHints *hints;
-
-    hints = XAllocSizeHints();
-
-    hints->flags = USPosition;
-    hints->x = x;
-    hints->y = y;
-
-    WMMoveWidget(menu, x, y);
-    XSetWMNormalHints(W_VIEW_DISPLAY(menu->view),
-                      W_VIEW_DRAWABLE(menu->view),
-                      hints);
-    XFree(hints);
-
-    WMMapWidget(menu);
-}
-
-
-static void
-updateMenuContents(WEditMenu *mPtr)
-{
-    int newW, newH;
-    int w;
-    int i;
-    int iheight = mPtr->itemHeight;
-    int offs = 1;
-    WMArrayIterator iter;
-    WEditMenuItem *item;
-
-    newW = 0;
-    newH = offs;
-
-    i = 0;
-    WM_ITERATE_ARRAY(mPtr->items, item, iter) {
-        w = getItemTextWidth(item);
-
-        newW = WMAX(w, newW);
-
-        WMMoveWidget(item, offs, newH);
-        if (i == 0 && mPtr->flags.isTitled) {
-            newH += mPtr->titleHeight;
-        } else {
-            newH += iheight;
-        }
-        i = 1;
-    }
-
-    newW += iheight + 10;
-    newH--;
-
-    if (mPtr->minSize.width)
-        newW = WMAX(newW, mPtr->minSize.width);
-    if (mPtr->maxSize.width)
-        newW = WMIN(newW, mPtr->maxSize.width);
-
-    if (mPtr->minSize.height)
-        newH = WMAX(newH, mPtr->minSize.height);
-    if (mPtr->maxSize.height)
-        newH = WMIN(newH, mPtr->maxSize.height);
-
-    if (W_VIEW(mPtr)->size.width == newW && mPtr->view->size.height == newH+1)
-        return;
-
-    W_ResizeView(mPtr->view, newW, newH+1);
-
-    if (mPtr->closeB)
-        WMMoveWidget(mPtr->closeB, newW - 20, 3);
-
-    newW -= 2*offs;
-
-    i = 0;
-    WM_ITERATE_ARRAY(mPtr->items, item, iter) {
-        if (i == 0 && mPtr->flags.isTitled) {
-            WMResizeWidget(item, newW, mPtr->titleHeight);
-        } else {
-            WMResizeWidget(item, newW, iheight);
-        }
-        i = 1;
-    }
-}
-
-
-static void
-deselectItem(WEditMenu *menu)
-{
-    WEditMenu *submenu;
-    WEditMenuItem *item = menu->selectedItem;
-
-    highlightItem(item, False);
-
-    if (menu->delegate && menu->delegate->itemDeselected) {
-        (*menu->delegate->itemDeselected)(menu->delegate, menu, item);
-    }
-    submenu = item->submenu;
-
-    if (submenu && !WEditMenuIsTornOff(submenu)) {
-        WEditMenuHide(submenu);
-    }
-
-    menu->selectedItem = NULL;
-}
-
-
-static void
-selectItem(WEditMenu *menu, WEditMenuItem *item)
-{
-    if (!menu->flags.isSelectable || menu->selectedItem == item) {
-        return;
-    }
-    if (menu->selectedItem) {
-        deselectItem(menu);
-    }
-
-    if (menu->flags.isEditing) {
-        stopEditItem(menu, False);
-    }
-
-    if (item && !item->flags.isTitle) {
-        highlightItem(item, True);
-
-        if (item->submenu && !W_VIEW_MAPPED(item->submenu->view)) {
-            WMPoint pt;
-
-            pt = WGetEditMenuLocationForSubmenu(menu, item->submenu);
-
-            WEditMenuShowAt(item->submenu, pt.x, pt.y);
-
-            item->submenu->flags.isTornOff = 0;
-        }
-
-        WMPostNotificationName("EditMenuItemSelected", menu, NULL);
-
-        if (menu->delegate && menu->delegate->itemSelected) {
-            (*menu->delegate->itemSelected)(menu->delegate, menu, item);
-        }
-    }
-
-    menu->selectedItem = item;
-}
-
-
-static void
-paintMenu(WEditMenu *mPtr)
-{
-    W_View *view = mPtr->view;
-
-    W_DrawRelief(W_VIEW_SCREEN(view), W_VIEW_DRAWABLE(view), 0, 0,
-                 W_VIEW_WIDTH(view), W_VIEW_HEIGHT(view), WRSimple);
-}
-
-
-static void
-handleEvents(XEvent *event, void *data)
-{
-    WEditMenu *mPtr = (WEditMenu*)data;
-
-    switch (event->type) {
-    case DestroyNotify:
-        destroyEditMenu(mPtr);
-        break;
-
-    case Expose:
-        if (event->xexpose.count == 0)
-            paintMenu(mPtr);
-        break;
-    }
-}
-
-
-
-
-/* -------------------------- Menu Label Editing ------------------------ */
-
-
-static void
-stopEditItem(WEditMenu *menu, Bool apply)
-{
-    char *text;
-
-    if (apply) {
-        text = WMGetTextFieldText(menu->tfield);
-
-        wfree(menu->selectedItem->label);
-        menu->selectedItem->label = wstrdup(text);
-
-        updateMenuContents(menu);
-
-        if (menu->delegate && menu->delegate->itemEdited) {
-            (*menu->delegate->itemEdited)(menu->delegate, menu,
-                                          menu->selectedItem);
-        }
-
-    }
-    WMUnmapWidget(menu->tfield);
-    menu->flags.isEditing = 0;
-}
-
-
-static void
-textEndedEditing(struct WMTextFieldDelegate *self, WMNotification *notif)
-{
-    WEditMenu *menu = (WEditMenu*)self->data;
-    int reason;
-    int i;
-    WEditMenuItem *item;
-
-    if (!menu->flags.isEditing)
-        return;
-
-    reason = (int)(uintptr_t)WMGetNotificationClientData(notif);
-
-    switch (reason) {
-    case WMEscapeTextMovement:
-        stopEditItem(menu, False);
-        break;
-
-    case WMReturnTextMovement:
-        stopEditItem(menu, True);
-        break;
-
-    case WMTabTextMovement:
-        stopEditItem(menu, True);
-
-        i = WMGetFirstInArray(menu->items, menu->selectedItem);
-        item = WMGetFromArray(menu->items, i+1);
-        if (item != NULL) {
-            selectItem(menu, item);
-            editItemLabel(item);
-        }
-        break;
-
-    case WMBacktabTextMovement:
-        stopEditItem(menu, True);
-
-        i = WMGetFirstInArray(menu->items, menu->selectedItem);
-        item = WMGetFromArray(menu->items, i-1);
-        if (item != NULL) {
-            selectItem(menu, item);
-            editItemLabel(item);
-        }
-        break;
-    }
-}
-
-
-
-static WMTextFieldDelegate textFieldDelegate = {
-    NULL,
-    NULL,
-    NULL,
-    textEndedEditing,
-    NULL,
-    NULL
-};
-
-
-static void
-editItemLabel(WEditMenuItem *item)
-{
-    WEditMenu *mPtr = item->parent;
-    WMTextField *tf;
-
-    if (!mPtr->flags.isEditable) {
-        return;
-    }
-
-    if (!mPtr->tfield) {
-        mPtr->tfield = WMCreateTextField(mPtr);
-        WMSetTextFieldBeveled(mPtr->tfield, False);
-        WMRealizeWidget(mPtr->tfield);
-
-        mPtr->tdelegate = wmalloc(sizeof(WMTextFieldDelegate));
-        memcpy(mPtr->tdelegate, &textFieldDelegate,
-               sizeof(WMTextFieldDelegate));
-
-        mPtr->tdelegate->data = mPtr;
-
-        WMSetTextFieldDelegate(mPtr->tfield, mPtr->tdelegate);
-    }
-    tf = mPtr->tfield;
-
-    WMSetTextFieldText(tf, item->label);
-    WMSelectTextFieldRange(tf, wmkrange(0, strlen(item->label)));
-    WMResizeWidget(tf, mPtr->view->size.width, mPtr->itemHeight);
-    WMMoveWidget(tf, 0, item->view->pos.y);
-    WMMapWidget(tf);
-    WMSetFocusToWidget(tf);
-
-    mPtr->flags.isEditing = 1;
-}
-
-
-
-/* -------------------------------------------------- */
-
-
-static void
-slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
-{
-    double x, y, dx, dy;
-    int i;
-    int iterations;
-
-    iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY)));
-
-    x = srcX;
-    y = srcY;
-
-    dx = (double)(dstX-srcX)/iterations;
-    dy = (double)(dstY-srcY)/iterations;
-
-    for (i = 0; i <= iterations; i++) {
-        XMoveWindow(dpy, win, x, y);
-        XFlush(dpy);
-
-        wusleep(800);
-
-        x += dx;
-        y += dy;
-    }
-}
-
-
-static int
-errorHandler(Display *d, XErrorEvent *ev)
-{
-    /* just ignore */
-    return 0;
-}
-
-
-static WEditMenu*
-findMenuInWindow(Display *dpy, Window toplevel, int x, int y)
-{
-    Window foo, bar;
-    Window *children;
-    unsigned nchildren;
-    int i;
-    WEditMenu *menu;
-    WMView *view;
-    int (*oldHandler)(Display *, XErrorEvent *);
-
-    view = W_GetViewForXWindow(dpy, toplevel);
-    if (view && view->self && WMWidgetClass(view->self) == EditMenuClass) {
-        menu = (WEditMenu*)view->self;
-        if (menu->flags.acceptsDrop) {
-            return menu;
-        }
-    }
-
-    if (!XQueryTree(dpy, toplevel, &foo, &bar,
-                    &children, &nchildren) || children == NULL) {
-        return NULL;
-    }
-
-    oldHandler = XSetErrorHandler(errorHandler);
-
-    /* first window that contains the point is the one */
-    for (i = nchildren-1; i >= 0; i--) {
-        XWindowAttributes attr;
-
-        if (XGetWindowAttributes(dpy, children[i], &attr)
-            && attr.map_state == IsViewable
-            && x >= attr.x && y >= attr.y
-            && x < attr.x + attr.width && y < attr.y + attr.height) {
-            Window tmp;
-
-            tmp = children[i];
-
-            menu = findMenuInWindow(dpy, tmp, x - attr.x, y - attr.y);
-            if (menu) {
-                XFree(children);
-                return menu;
-            }
-        }
-    }
-
-    XSetErrorHandler(oldHandler);
-
-    XFree(children);
-    return NULL;
-}
-
-
-static void
-handleDragOver(WEditMenu *menu, WMView *view, WEditMenuItem *item,
-               int x, int y)
-{
-    WMScreen *scr = W_VIEW_SCREEN(menu->view);
-    Window blaw;
-    int mx, my;
-    int offs;
-
-    XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
-                          scr->rootWin, 0, 0, &mx, &my, &blaw);
-
-    offs = menu->flags.standalone ? 0 : 1;
-
-    W_MoveView(view, mx + offs, y);
-    if (view->size.width != menu->view->size.width) {
-        W_ResizeView(view, menu->view->size.width - 2*offs,
-                     menu->itemHeight);
-        W_ResizeView(item->view, menu->view->size.width - 2*offs,
-                     menu->itemHeight);
-    }
-}
-
-
-static void
-handleItemDrop(WEditMenu *menu, WEditMenuItem *item, int x, int y)
-{
-    WMScreen *scr = W_VIEW_SCREEN(menu->view);
-    Window blaw;
-    int mx, my;
-    int index;
-
-    XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
-                          scr->rootWin, 0, 0, &mx, &my, &blaw);
-
-    index = y - my;
-    if (menu->flags.isTitled) {
-        index -= menu->titleHeight;
-    }
-    index = (index + menu->itemHeight/2) / menu->itemHeight;
-    if (index < 0)
-        index = 0;
-
-    if (menu->flags.isTitled) {
-        index++;
-    }
-
-    if (index > WMGetArrayItemCount(menu->items)) {
-        WMAddToArray(menu->items, item);
-    } else {
-        WMInsertInArray(menu->items, index, item);
-    }
-
-    W_ReparentView(item->view, menu->view, 0, index*menu->itemHeight);
-
-    item->parent = menu;
-    if (item->submenu) {
-        item->submenu->parent = menu;
-    }
-
-    updateMenuContents(menu);
-}
-
-
-static void
-dragMenu(WEditMenu *menu)
-{
-    WMScreen *scr = W_VIEW_SCREEN(menu->view);
-    XEvent ev;
-    Bool done = False;
-    int dx, dy;
-    unsigned blau;
-    Window blaw;
-
-    XGetGeometry(scr->display, W_VIEW_DRAWABLE(menu->view), &blaw, &dx, &dy,
-                 &blau, &blau, &blau, &blau);
-
-    XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
-                          scr->rootWin, dx, dy, &dx, &dy, &blaw);
-
-    dx = menu->dragX - dx;
-    dy = menu->dragY - dy;
-
-    XGrabPointer(scr->display, scr->rootWin, False,
-                 ButtonReleaseMask|ButtonMotionMask,
-                 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
-                 CurrentTime);
-
-    if (menu->parent)
-        WTearOffEditMenu(menu->parent, menu);
-
-    while (!done) {
-        WMNextEvent(scr->display, &ev);
-
-        switch (ev.type) {
-        case ButtonRelease:
-            done = True;
-            break;
-
-        case MotionNotify:
-            while (XCheckMaskEvent(scr->display, ButtonMotionMask, &ev)) ;
-
-            WMMoveWidget(menu, ev.xmotion.x_root - dx,
-                         ev.xmotion.y_root - dy);
-            break;
-
-        default:
-            WMHandleEvent(&ev);
-            break;
-        }
-    }
-
-    XUngrabPointer(scr->display, CurrentTime);
-}
-
-
-
-static WEditMenuItem*
-duplicateItem(WEditMenuItem *item)
-{
-    WEditMenuItem *nitem;
-
-    nitem = WCreateEditMenuItem(item->parent, item->label, False);
-    if (item->pixmap)
-        nitem->pixmap = WMRetainPixmap(item->pixmap);
-
-    return nitem;
-}
-
-
-
-
-static WEditMenu*
-duplicateMenu(WEditMenu *menu)
-{
-    WEditMenu *nmenu;
-    WEditMenuItem *item;
-    WMArrayIterator iter;
-    Bool first = menu->flags.isTitled;
-
-    nmenu = WCreateEditMenu(WMWidgetScreen(menu), WGetEditMenuTitle(menu));
-
-    memcpy(&nmenu->flags, &menu->flags, sizeof(menu->flags));
-    nmenu->delegate = menu->delegate;
-
-    WM_ITERATE_ARRAY(menu->items, item, iter) {
-        WEditMenuItem *nitem;
-
-        if (first) {
-            first = False;
-            continue;
-        }
-
-        nitem = WAddMenuItemWithTitle(nmenu, item->label);
-        if (item->pixmap)
-            WSetEditMenuItemImage(nitem, item->pixmap);
-
-        if (menu->delegate && menu->delegate->itemCloned) {
-            (*menu->delegate->itemCloned)(menu->delegate, menu,
-                                          item, nitem);
-        }
-    }
-
-    WMRealizeWidget(nmenu);
-
-    return nmenu;
-}
-
-
-
-static void
-dragItem(WEditMenu *menu, WEditMenuItem *item, Bool copy)
-{
-    static XColor black = {0, 0,0,0, DoRed|DoGreen|DoBlue};
-    static XColor green = {0x0045b045, 0x4500,0xb000,0x4500, DoRed|DoGreen|DoBlue};
-    static XColor back = {0, 0xffff,0xffff,0xffff, DoRed|DoGreen|DoBlue};
-    Display *dpy = W_VIEW_DISPLAY(menu->view);
-    WMScreen *scr = W_VIEW_SCREEN(menu->view);
-    int x, y;
-    int dx, dy;
-    Bool done = False;
-    Window blaw;
-    int blai;
-    unsigned blau;
-    Window win;
-    WMView *dview;
-    int orix, oriy;
-    Bool enteredMenu = False;
-    WMSize oldSize = item->view->size;
-    WEditMenuItem *dritem = item;
-    WEditMenu *dmenu = NULL;
-
-
-    if (item->flags.isTitle) {
-        WMRaiseWidget(menu);
-
-        dragMenu(menu);
-
-        return;
-    }
-
-    selectItem(menu, NULL);
-
-    win = scr->rootWin;
-
-    XTranslateCoordinates(dpy, W_VIEW_DRAWABLE(item->view), win,
-                          0, 0, &orix, &oriy, &blaw);
-
-    dview = W_CreateUnmanagedTopView(scr);
-    W_SetViewBackgroundColor(dview, scr->black);
-    W_ResizeView(dview, W_VIEW_WIDTH(item->view), W_VIEW_HEIGHT(item->view));
-    W_MoveView(dview, orix, oriy);
-    W_RealizeView(dview);
-
-    if (menu->flags.isFactory || copy) {
-        dritem = duplicateItem(item);
-
-        W_ReparentView(dritem->view, dview, 0, 0);
-        WMResizeWidget(dritem, oldSize.width, oldSize.height);
-        WMRealizeWidget(dritem);
-        WMMapWidget(dritem);
-    } else {
-        W_ReparentView(item->view, dview, 0, 0);
-    }
-
-    W_MapView(dview);
-
-    dx = menu->dragX - orix;
-    dy = menu->dragY - oriy;
-
-    XGrabPointer(dpy, scr->rootWin, False,
-                 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
-                 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
-                 CurrentTime);
-
-    if (menu->flags.acceptsDrop)
-        XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
-
-    while (!done) {
-        XEvent ev;
-
-        WMNextEvent(dpy, &ev);
-
-        switch (ev.type) {
-        case MotionNotify:
-            while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev)) ;
-
-            XQueryPointer(dpy, win, &blaw, &blaw, &blai, &blai, &x, &y, &blau);
-
-            dmenu = findMenuInWindow(dpy, win, x, y);
-
-            if (dmenu) {
-                handleDragOver(dmenu, dview, dritem, x - dx, y - dy);
-                if (!enteredMenu) {
-                    enteredMenu = True;
-                    XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
-                }
-            } else {
-                if (enteredMenu) {
-                    W_ResizeView(dview, oldSize.width, oldSize.height);
-                    W_ResizeView(dritem->view, oldSize.width, oldSize.height);
-                    enteredMenu = False;
-                    XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
-                }
-                W_MoveView(dview, x - dx, y - dy);
-            }
-
-            break;
-
-        case ButtonRelease:
-            done = True;
-            break;
-
-        default:
-            WMHandleEvent(&ev);
-            break;
-        }
-    }
-    XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
-
-    XUngrabPointer(dpy, CurrentTime);
-
-
-    if (!enteredMenu) {
-        Bool rem = True;
-
-        if (!menu->flags.isFactory && !copy) {
-            W_UnmapView(dview);
-            if (menu->delegate && menu->delegate->shouldRemoveItem) {
-                rem = (*menu->delegate->shouldRemoveItem)(menu->delegate,
-                                                          menu, item);
-            }
-            W_MapView(dview);
-        }
-
-        if (!rem || menu->flags.isFactory || copy) {
-            slideWindow(dpy, W_VIEW_DRAWABLE(dview), x-dx, y-dy, orix, oriy);
-
-            if (!menu->flags.isFactory && !copy) {
-                WRemoveEditMenuItem(menu, dritem);
-                handleItemDrop(dmenu ? dmenu : menu, dritem, orix, oriy);
-            }
-        } else {
-            WRemoveEditMenuItem(menu, dritem);
-        }
-    } else {
-        WRemoveEditMenuItem(menu, dritem);
-
-        if (menu->delegate && menu->delegate->itemCloned
-            && (menu->flags.isFactory || copy)) {
-            (*menu->delegate->itemCloned)(menu->delegate, menu,
-                                          item, dritem);
-        }
-
-        handleItemDrop(dmenu, dritem, x-dy, y-dy);
-
-        if (item->submenu && (menu->flags.isFactory || copy)) {
-            WEditMenu *submenu;
-
-            submenu = duplicateMenu(item->submenu);
-            WSetEditMenuSubmenu(dmenu, dritem, submenu);
-        }
-    }
-
-    /* can't destroy now because we're being called from
-     * the event handler of the item. and destroying now,
-     * would mean destroying the item too in some cases.
-     */
-    WMAddIdleHandler((WMCallback*)W_DestroyView, dview);
-}
-
-
-
-static void
-handleItemClick(XEvent *event, void *data)
-{
-    WEditMenuItem *item = (WEditMenuItem*)data;
-    WEditMenu *menu = item->parent;
-
-    switch (event->type) {
-    case ButtonPress:
-        selectItem(menu, item);
-
-        if (WMIsDoubleClick(event)) {
-            editItemLabel(item);
-        }
-
-        menu->flags.isDragging = 1;
-        menu->dragX = event->xbutton.x_root;
-        menu->dragY = event->xbutton.y_root;
-        break;
-
-    case ButtonRelease:
-        menu->flags.isDragging = 0;
-        break;
-
-    case MotionNotify:
-        if (menu->flags.isDragging) {
-            if (abs(event->xbutton.x_root - menu->dragX) > 5
-                || abs(event->xbutton.y_root - menu->dragY) > 5) {
-                menu->flags.isDragging = 0;
-                dragItem(menu, item, event->xbutton.state & ControlMask);
-            }
-        }
-        break;
-    }
-}
-
-
-static void
-destroyEditMenu(WEditMenu *mPtr)
-{
-    WMRemoveNotificationObserver(mPtr);
-
-    WMFreeArray(mPtr->items);
-
-    wfree(mPtr->tdelegate);
-
-    wfree(mPtr);
-}
-
-
-
+/* editmenu.c - editable menus
+ *
+ *  WPrefs - Window Maker Preferences Program
+ *
+ *  Copyright (c) 2000-2003 Alfredo K. Kojima
+ *
+ *  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 <WINGs/WINGsP.h>
+#include <WINGs/WUtil.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "editmenu.h"
+
+typedef struct W_EditMenuItem {
+       W_Class widgetClass;
+       WMView *view;
+
+       struct W_EditMenu *parent;
+
+       char *label;
+       WMPixmap *pixmap;       /* pixmap to show at left */
+
+       void *data;
+       WMCallback *destroyData;
+
+       struct W_EditMenu *submenu;     /* if it's a cascade, NULL otherwise */
+
+       struct {
+               unsigned isTitle:1;
+               unsigned isHighlighted:1;
+       } flags;
+} EditMenuItem;
+
+typedef struct W_EditMenu {
+       W_Class widgetClass;
+       WMView *view;
+
+       struct W_EditMenu *parent;
+
+       WMArray *items;         /* EditMenuItem */
+
+       EditMenuItem *selectedItem;
+
+       WMTextField *tfield;
+
+       WMButton *closeB;
+
+       int titleHeight;
+       int itemHeight;
+
+       WEditMenuDelegate *delegate;
+
+       WMTextFieldDelegate *tdelegate;
+
+       /* item dragging */
+       int draggedItem;
+       int dragX, dragY;
+
+       /* only for non-standalone menu */
+       WMSize maxSize;
+       WMSize minSize;
+
+       struct {
+               unsigned standalone:1;
+               unsigned isTitled:1;
+
+               unsigned acceptsDrop:1;
+               unsigned isFactory:1;
+               unsigned isSelectable:1;
+               unsigned isEditable:1;
+
+               unsigned isTornOff:1;
+
+               unsigned isDragging:1;
+               unsigned isEditing:1;
+
+               unsigned wasMapped:1;
+       } flags;
+} EditMenu;
+
+/******************** WEditMenuItem ********************/
+
+static void destroyEditMenuItem(WEditMenuItem * iPtr);
+static void paintEditMenuItem(WEditMenuItem * iPtr);
+static void handleItemEvents(XEvent * event, void *data);
+
+static void handleItemClick(XEvent * event, void *data);
+
+static W_Class EditMenuItemClass = 0;
+
+W_Class InitEditMenuItem(WMScreen * scr)
+{
+       /* register our widget with WINGs and get our widget class ID */
+       if (!EditMenuItemClass) {
+               EditMenuItemClass = W_RegisterUserWidget();
+       }
+
+       return EditMenuItemClass;
+}
+
+WEditMenuItem *WCreateEditMenuItem(WMWidget * parent, char *title, Bool isTitle)
+{
+       WEditMenuItem *iPtr;
+       WMScreen *scr = WMWidgetScreen(parent);
+
+       if (!EditMenuItemClass)
+               InitEditMenuItem(scr);
+
+       iPtr = wmalloc(sizeof(WEditMenuItem));
+
+       memset(iPtr, 0, sizeof(WEditMenuItem));
+
+       iPtr->widgetClass = EditMenuItemClass;
+
+       iPtr->view = W_CreateView(W_VIEW(parent));
+       if (!iPtr->view) {
+               wfree(iPtr);
+               return NULL;
+       }
+       iPtr->view->self = iPtr;
+
+       iPtr->parent = parent;
+
+       iPtr->label = wstrdup(title);
+
+       iPtr->flags.isTitle = isTitle;
+
+       if (isTitle) {
+               WMSetWidgetBackgroundColor(iPtr, WMBlackColor(scr));
+       }
+
+       WMCreateEventHandler(iPtr->view, ExposureMask | StructureNotifyMask, handleItemEvents, iPtr);
+
+       WMCreateEventHandler(iPtr->view, ButtonPressMask | ButtonReleaseMask
+                            | ButtonMotionMask, handleItemClick, iPtr);
+
+       return iPtr;
+}
+
+char *WGetEditMenuItemTitle(WEditMenuItem * item)
+{
+       return item->label;
+}
+
+void *WGetEditMenuItemData(WEditMenuItem * item)
+{
+       return item->data;
+}
+
+void WSetEditMenuItemData(WEditMenuItem * item, void *data, WMCallback * destroyer)
+{
+       item->data = data;
+       item->destroyData = destroyer;
+}
+
+void WSetEditMenuItemImage(WEditMenuItem * item, WMPixmap * pixmap)
+{
+       if (item->pixmap)
+               WMReleasePixmap(item->pixmap);
+       item->pixmap = WMRetainPixmap(pixmap);
+}
+
+static void paintEditMenuItem(WEditMenuItem * iPtr)
+{
+       WMScreen *scr = WMWidgetScreen(iPtr);
+       WMColor *color;
+       Window win = W_VIEW(iPtr)->window;
+       int w = W_VIEW(iPtr)->size.width;
+       int h = W_VIEW(iPtr)->size.height;
+       WMFont *font = (iPtr->flags.isTitle ? scr->boldFont : scr->normalFont);
+
+       if (!iPtr->view->flags.realized)
+               return;
+
+       color = scr->black;
+       if (iPtr->flags.isTitle && !iPtr->flags.isHighlighted) {
+               color = scr->white;
+       }
+
+       XClearWindow(scr->display, win);
+
+       W_DrawRelief(scr, win, 0, 0, w + 1, h, WRRaised);
+
+       WMDrawString(scr, win, color, font, 5, 3, iPtr->label, strlen(iPtr->label));
+
+       if (iPtr->pixmap) {
+               WMSize size = WMGetPixmapSize(iPtr->pixmap);
+
+               WMDrawPixmap(iPtr->pixmap, win, w - size.width - 5, (h - size.height) / 2);
+       } else if (iPtr->submenu) {
+               /* draw the cascade indicator */
+               XDrawLine(scr->display, win, WMColorGC(scr->darkGray), w - 11, 6, w - 6, h / 2 - 1);
+               XDrawLine(scr->display, win, WMColorGC(scr->white), w - 11, h - 8, w - 6, h / 2 - 1);
+               XDrawLine(scr->display, win, WMColorGC(scr->black), w - 12, 6, w - 12, h - 8);
+       }
+}
+
+static void highlightItem(WEditMenuItem * iPtr, Bool high)
+{
+       if (iPtr->flags.isTitle)
+               return;
+
+       iPtr->flags.isHighlighted = high;
+
+       if (high) {
+               WMSetWidgetBackgroundColor(iPtr, WMWhiteColor(WMWidgetScreen(iPtr)));
+       } else {
+               if (!iPtr->flags.isTitle) {
+                       WMSetWidgetBackgroundColor(iPtr, WMGrayColor(WMWidgetScreen(iPtr)));
+               } else {
+                       WMSetWidgetBackgroundColor(iPtr, WMBlackColor(WMWidgetScreen(iPtr)));
+               }
+       }
+}
+
+static int getItemTextWidth(WEditMenuItem * iPtr)
+{
+       WMScreen *scr = WMWidgetScreen(iPtr);
+
+       if (iPtr->flags.isTitle) {
+               return WMWidthOfString(scr->boldFont, iPtr->label, strlen(iPtr->label));
+       } else {
+               return WMWidthOfString(scr->normalFont, iPtr->label, strlen(iPtr->label));
+       }
+}
+
+static void handleItemEvents(XEvent * event, void *data)
+{
+       WEditMenuItem *iPtr = (WEditMenuItem *) data;
+
+       switch (event->type) {
+       case Expose:
+               if (event->xexpose.count != 0)
+                       break;
+               paintEditMenuItem(iPtr);
+               break;
+
+       case DestroyNotify:
+               destroyEditMenuItem(iPtr);
+               break;
+       }
+}
+
+static void destroyEditMenuItem(WEditMenuItem * iPtr)
+{
+       if (iPtr->label)
+               wfree(iPtr->label);
+       if (iPtr->data && iPtr->destroyData)
+               (*iPtr->destroyData) (iPtr->data);
+       if (iPtr->submenu)
+               WMDestroyWidget(iPtr->submenu);
+
+       wfree(iPtr);
+}
+
+/******************** WEditMenu *******************/
+
+static void destroyEditMenu(WEditMenu * mPtr);
+
+static void updateMenuContents(WEditMenu * mPtr);
+
+static void handleEvents(XEvent * event, void *data);
+
+static void editItemLabel(WEditMenuItem * item);
+
+static void stopEditItem(WEditMenu * menu, Bool apply);
+
+static void deselectItem(WEditMenu * menu);
+
+static W_Class EditMenuClass = 0;
+
+W_Class InitEditMenu(WMScreen * scr)
+{
+       /* register our widget with WINGs and get our widget class ID */
+       if (!EditMenuClass) {
+
+               EditMenuClass = W_RegisterUserWidget();
+       }
+
+       return EditMenuClass;
+}
+
+typedef struct {
+       int flags;
+       int window_style;
+       int window_level;
+       int reserved;
+       Pixmap miniaturize_pixmap;      /* pixmap for miniaturize button */
+       Pixmap close_pixmap;    /* pixmap for close button */
+       Pixmap miniaturize_mask;        /* miniaturize pixmap mask */
+       Pixmap close_mask;      /* close pixmap mask */
+       int extra_flags;
+} GNUstepWMAttributes;
+
+#define GSWindowStyleAttr       (1<<0)
+#define GSWindowLevelAttr       (1<<1)
+
+static void writeGNUstepWMAttr(WMScreen * scr, Window window, GNUstepWMAttributes * attr)
+{
+       unsigned long data[9];
+
+       /* handle idiot compilers where array of CARD32 != struct of CARD32 */
+       data[0] = attr->flags;
+       data[1] = attr->window_style;
+       data[2] = attr->window_level;
+       data[3] = 0;            /* reserved */
+       /* The X protocol says XIDs are 32bit */
+       data[4] = attr->miniaturize_pixmap;
+       data[5] = attr->close_pixmap;
+       data[6] = attr->miniaturize_mask;
+       data[7] = attr->close_mask;
+       data[8] = attr->extra_flags;
+       XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
+                       32, PropModeReplace, (unsigned char *)data, 9);
+}
+
+static void realizeObserver(void *self, WMNotification * not)
+{
+       WEditMenu *menu = (WEditMenu *) self;
+       GNUstepWMAttributes attribs;
+
+       memset(&attribs, 0, sizeof(GNUstepWMAttributes));
+       attribs.flags = GSWindowStyleAttr | GSWindowLevelAttr;
+       attribs.window_style = WMBorderlessWindowMask;
+       attribs.window_level = WMSubmenuWindowLevel;
+
+       writeGNUstepWMAttr(WMWidgetScreen(menu), menu->view->window, &attribs);
+}
+
+static void itemSelectObserver(void *self, WMNotification * notif)
+{
+       WEditMenu *menu = (WEditMenu *) self;
+       WEditMenu *rmenu;
+
+       rmenu = (WEditMenu *) WMGetNotificationObject(notif);
+       /* check whether rmenu is from the same hierarchy of menu? */
+
+       if (rmenu == menu) {
+               return;
+       }
+
+       if (menu->selectedItem) {
+               if (!menu->selectedItem->submenu)
+                       deselectItem(menu);
+               if (menu->flags.isEditing)
+                       stopEditItem(menu, False);
+       }
+}
+
+static WEditMenu *makeEditMenu(WMScreen * scr, WMWidget * parent, char *title)
+{
+       WEditMenu *mPtr;
+       WEditMenuItem *item;
+
+       if (!EditMenuClass)
+               InitEditMenu(scr);
+
+       mPtr = wmalloc(sizeof(WEditMenu));
+       memset(mPtr, 0, sizeof(WEditMenu));
+
+       mPtr->widgetClass = EditMenuClass;
+
+       if (parent) {
+               mPtr->view = W_CreateView(W_VIEW(parent));
+               mPtr->flags.standalone = 0;
+       } else {
+               mPtr->view = W_CreateTopView(scr);
+               mPtr->flags.standalone = 1;
+       }
+       if (!mPtr->view) {
+               wfree(mPtr);
+               return NULL;
+       }
+       mPtr->view->self = mPtr;
+
+       mPtr->flags.isSelectable = 1;
+       mPtr->flags.isEditable = 1;
+
+       W_SetViewBackgroundColor(mPtr->view, scr->darkGray);
+
+       WMAddNotificationObserver(realizeObserver, mPtr, WMViewRealizedNotification, mPtr->view);
+
+       WMAddNotificationObserver(itemSelectObserver, mPtr, "EditMenuItemSelected", NULL);
+
+       mPtr->items = WMCreateArray(4);
+
+       WMCreateEventHandler(mPtr->view, ExposureMask | StructureNotifyMask, handleEvents, mPtr);
+
+       if (title != NULL) {
+               item = WCreateEditMenuItem(mPtr, title, True);
+
+               WMMapWidget(item);
+               WMAddToArray(mPtr->items, item);
+
+               mPtr->flags.isTitled = 1;
+       }
+
+       mPtr->itemHeight = WMFontHeight(scr->normalFont) + 6;
+       mPtr->titleHeight = WMFontHeight(scr->boldFont) + 8;
+
+       updateMenuContents(mPtr);
+
+       return mPtr;
+}
+
+WEditMenu *WCreateEditMenu(WMScreen * scr, char *title)
+{
+       return makeEditMenu(scr, NULL, title);
+}
+
+WEditMenu *WCreateEditMenuPad(WMWidget * parent)
+{
+       return makeEditMenu(WMWidgetScreen(parent), parent, NULL);
+}
+
+void WSetEditMenuDelegate(WEditMenu * mPtr, WEditMenuDelegate * delegate)
+{
+       mPtr->delegate = delegate;
+}
+
+WEditMenuItem *WInsertMenuItemWithTitle(WEditMenu * mPtr, int index, char *title)
+{
+       WEditMenuItem *item;
+
+       item = WCreateEditMenuItem(mPtr, title, False);
+
+       WMMapWidget(item);
+
+       if (index >= WMGetArrayItemCount(mPtr->items)) {
+               WMAddToArray(mPtr->items, item);
+       } else {
+               if (index < 0)
+                       index = 0;
+               if (mPtr->flags.isTitled)
+                       index++;
+               WMInsertInArray(mPtr->items, index, item);
+       }
+
+       updateMenuContents(mPtr);
+
+       return item;
+}
+
+WEditMenuItem *WGetEditMenuItem(WEditMenu * mPtr, int index)
+{
+       if (index >= WMGetArrayItemCount(mPtr->items))
+               return NULL;
+
+       return WMGetFromArray(mPtr->items, index + (mPtr->flags.isTitled ? 1 : 0));
+}
+
+WEditMenuItem *WAddMenuItemWithTitle(WEditMenu * mPtr, char *title)
+{
+       return WInsertMenuItemWithTitle(mPtr, WMGetArrayItemCount(mPtr->items), title);
+}
+
+void WSetEditMenuTitle(WEditMenu * mPtr, char *title)
+{
+       WEditMenuItem *item;
+
+       item = WMGetFromArray(mPtr->items, 0);
+
+       wfree(item->label);
+       item->label = wstrdup(title);
+
+       updateMenuContents(mPtr);
+
+       WMRedisplayWidget(item);
+}
+
+char *WGetEditMenuTitle(WEditMenu * mPtr)
+{
+       WEditMenuItem *item;
+
+       item = WMGetFromArray(mPtr->items, 0);
+
+       return item->label;
+}
+
+void WSetEditMenuAcceptsDrop(WEditMenu * mPtr, Bool flag)
+{
+       mPtr->flags.acceptsDrop = flag;
+}
+
+void WSetEditMenuSubmenu(WEditMenu * mPtr, WEditMenuItem * item, WEditMenu * submenu)
+{
+       item->submenu = submenu;
+       submenu->parent = mPtr;
+
+       paintEditMenuItem(item);
+}
+
+WEditMenu *WGetEditMenuSubmenu(WEditMenu * mPtr, WEditMenuItem * item)
+{
+       return item->submenu;
+}
+
+void WRemoveEditMenuItem(WEditMenu * mPtr, WEditMenuItem * item)
+{
+       if (WMRemoveFromArray(mPtr->items, item) != 0) {
+               updateMenuContents(mPtr);
+       }
+}
+
+void WSetEditMenuSelectable(WEditMenu * mPtr, Bool flag)
+{
+       mPtr->flags.isSelectable = flag;
+}
+
+void WSetEditMenuEditable(WEditMenu * mPtr, Bool flag)
+{
+       mPtr->flags.isEditable = flag;
+}
+
+void WSetEditMenuIsFactory(WEditMenu * mPtr, Bool flag)
+{
+       mPtr->flags.isFactory = flag;
+}
+
+void WSetEditMenuMinSize(WEditMenu * mPtr, WMSize size)
+{
+       mPtr->minSize.width = size.width;
+       mPtr->minSize.height = size.height;
+}
+
+void WSetEditMenuMaxSize(WEditMenu * mPtr, WMSize size)
+{
+       mPtr->maxSize.width = size.width;
+       mPtr->maxSize.height = size.height;
+}
+
+WMPoint WGetEditMenuLocationForSubmenu(WEditMenu * mPtr, WEditMenu * submenu)
+{
+       WMArrayIterator iter;
+       WEditMenuItem *item;
+       int y;
+
+       if (mPtr->flags.isTitled)
+               y = -mPtr->titleHeight;
+       else
+               y = 0;
+       WM_ITERATE_ARRAY(mPtr->items, item, iter) {
+               if (item->submenu == submenu) {
+                       WMPoint pt = WMGetViewScreenPosition(mPtr->view);
+
+                       return wmkpoint(pt.x + mPtr->view->size.width, pt.y + y);
+               }
+               y += W_VIEW_HEIGHT(item->view);
+       }
+
+       puts("invalid submenu passed to WGetEditMenuLocationForSubmenu()");
+
+       return wmkpoint(0, 0);
+}
+
+static void closeMenuAction(WMWidget * w, void *data)
+{
+       WEditMenu *menu = (WEditMenu *) data;
+
+       WMAddIdleHandler(WMDestroyWidget, menu->closeB);
+       menu->closeB = NULL;
+
+       WEditMenuHide(menu);
+}
+
+void WTearOffEditMenu(WEditMenu * menu, WEditMenu * submenu)
+{
+       WEditMenuItem *item;
+
+       submenu->flags.isTornOff = 1;
+
+       item = (WEditMenuItem *) WMGetFromArray(submenu->items, 0);
+
+       submenu->closeB = WMCreateCommandButton(item);
+       WMResizeWidget(submenu->closeB, 15, 15);
+       WMMoveWidget(submenu->closeB, W_VIEW(submenu)->size.width - 20, 3);
+       WMRealizeWidget(submenu->closeB);
+       WMSetButtonText(submenu->closeB, "X");
+       WMSetButtonAction(submenu->closeB, closeMenuAction, submenu);
+       WMMapWidget(submenu->closeB);
+
+       if (menu->selectedItem && menu->selectedItem->submenu == submenu)
+               deselectItem(menu);
+}
+
+Bool WEditMenuIsTornOff(WEditMenu * mPtr)
+{
+       return mPtr->flags.isTornOff;
+}
+
+void WEditMenuHide(WEditMenu * mPtr)
+{
+       WEditMenuItem *item;
+       int i = 0;
+
+       if (WMWidgetIsMapped(mPtr)) {
+               WMUnmapWidget(mPtr);
+               mPtr->flags.wasMapped = 1;
+       } else {
+               mPtr->flags.wasMapped = 0;
+       }
+       while ((item = WGetEditMenuItem(mPtr, i++))) {
+               WEditMenu *submenu;
+
+               submenu = WGetEditMenuSubmenu(mPtr, item);
+               if (submenu) {
+                       WEditMenuHide(submenu);
+               }
+       }
+}
+
+void WEditMenuUnhide(WEditMenu * mPtr)
+{
+       WEditMenuItem *item;
+       int i = 0;
+
+       if (mPtr->flags.wasMapped) {
+               WMMapWidget(mPtr);
+       }
+       while ((item = WGetEditMenuItem(mPtr, i++))) {
+               WEditMenu *submenu;
+
+               submenu = WGetEditMenuSubmenu(mPtr, item);
+               if (submenu) {
+                       WEditMenuUnhide(submenu);
+               }
+       }
+}
+
+void WEditMenuShowAt(WEditMenu * menu, int x, int y)
+{
+       XSizeHints *hints;
+
+       hints = XAllocSizeHints();
+
+       hints->flags = USPosition;
+       hints->x = x;
+       hints->y = y;
+
+       WMMoveWidget(menu, x, y);
+       XSetWMNormalHints(W_VIEW_DISPLAY(menu->view), W_VIEW_DRAWABLE(menu->view), hints);
+       XFree(hints);
+
+       WMMapWidget(menu);
+}
+
+static void updateMenuContents(WEditMenu * mPtr)
+{
+       int newW, newH;
+       int w;
+       int i;
+       int iheight = mPtr->itemHeight;
+       int offs = 1;
+       WMArrayIterator iter;
+       WEditMenuItem *item;
+
+       newW = 0;
+       newH = offs;
+
+       i = 0;
+       WM_ITERATE_ARRAY(mPtr->items, item, iter) {
+               w = getItemTextWidth(item);
+
+               newW = WMAX(w, newW);
+
+               WMMoveWidget(item, offs, newH);
+               if (i == 0 && mPtr->flags.isTitled) {
+                       newH += mPtr->titleHeight;
+               } else {
+                       newH += iheight;
+               }
+               i = 1;
+       }
+
+       newW += iheight + 10;
+       newH--;
+
+       if (mPtr->minSize.width)
+               newW = WMAX(newW, mPtr->minSize.width);
+       if (mPtr->maxSize.width)
+               newW = WMIN(newW, mPtr->maxSize.width);
+
+       if (mPtr->minSize.height)
+               newH = WMAX(newH, mPtr->minSize.height);
+       if (mPtr->maxSize.height)
+               newH = WMIN(newH, mPtr->maxSize.height);
+
+       if (W_VIEW(mPtr)->size.width == newW && mPtr->view->size.height == newH + 1)
+               return;
+
+       W_ResizeView(mPtr->view, newW, newH + 1);
+
+       if (mPtr->closeB)
+               WMMoveWidget(mPtr->closeB, newW - 20, 3);
+
+       newW -= 2 * offs;
+
+       i = 0;
+       WM_ITERATE_ARRAY(mPtr->items, item, iter) {
+               if (i == 0 && mPtr->flags.isTitled) {
+                       WMResizeWidget(item, newW, mPtr->titleHeight);
+               } else {
+                       WMResizeWidget(item, newW, iheight);
+               }
+               i = 1;
+       }
+}
+
+static void deselectItem(WEditMenu * menu)
+{
+       WEditMenu *submenu;
+       WEditMenuItem *item = menu->selectedItem;
+
+       highlightItem(item, False);
+
+       if (menu->delegate && menu->delegate->itemDeselected) {
+               (*menu->delegate->itemDeselected) (menu->delegate, menu, item);
+       }
+       submenu = item->submenu;
+
+       if (submenu && !WEditMenuIsTornOff(submenu)) {
+               WEditMenuHide(submenu);
+       }
+
+       menu->selectedItem = NULL;
+}
+
+static void selectItem(WEditMenu * menu, WEditMenuItem * item)
+{
+       if (!menu->flags.isSelectable || menu->selectedItem == item) {
+               return;
+       }
+       if (menu->selectedItem) {
+               deselectItem(menu);
+       }
+
+       if (menu->flags.isEditing) {
+               stopEditItem(menu, False);
+       }
+
+       if (item && !item->flags.isTitle) {
+               highlightItem(item, True);
+
+               if (item->submenu && !W_VIEW_MAPPED(item->submenu->view)) {
+                       WMPoint pt;
+
+                       pt = WGetEditMenuLocationForSubmenu(menu, item->submenu);
+
+                       WEditMenuShowAt(item->submenu, pt.x, pt.y);
+
+                       item->submenu->flags.isTornOff = 0;
+               }
+
+               WMPostNotificationName("EditMenuItemSelected", menu, NULL);
+
+               if (menu->delegate && menu->delegate->itemSelected) {
+                       (*menu->delegate->itemSelected) (menu->delegate, menu, item);
+               }
+       }
+
+       menu->selectedItem = item;
+}
+
+static void paintMenu(WEditMenu * mPtr)
+{
+       W_View *view = mPtr->view;
+
+       W_DrawRelief(W_VIEW_SCREEN(view), W_VIEW_DRAWABLE(view), 0, 0,
+                    W_VIEW_WIDTH(view), W_VIEW_HEIGHT(view), WRSimple);
+}
+
+static void handleEvents(XEvent * event, void *data)
+{
+       WEditMenu *mPtr = (WEditMenu *) data;
+
+       switch (event->type) {
+       case DestroyNotify:
+               destroyEditMenu(mPtr);
+               break;
+
+       case Expose:
+               if (event->xexpose.count == 0)
+                       paintMenu(mPtr);
+               break;
+       }
+}
+
+/* -------------------------- Menu Label Editing ------------------------ */
+
+static void stopEditItem(WEditMenu * menu, Bool apply)
+{
+       char *text;
+
+       if (apply) {
+               text = WMGetTextFieldText(menu->tfield);
+
+               wfree(menu->selectedItem->label);
+               menu->selectedItem->label = wstrdup(text);
+
+               updateMenuContents(menu);
+
+               if (menu->delegate && menu->delegate->itemEdited) {
+                       (*menu->delegate->itemEdited) (menu->delegate, menu, menu->selectedItem);
+               }
+
+       }
+       WMUnmapWidget(menu->tfield);
+       menu->flags.isEditing = 0;
+}
+
+static void textEndedEditing(struct WMTextFieldDelegate *self, WMNotification * notif)
+{
+       WEditMenu *menu = (WEditMenu *) self->data;
+       int reason;
+       int i;
+       WEditMenuItem *item;
+
+       if (!menu->flags.isEditing)
+               return;
+
+       reason = (int)(uintptr_t) WMGetNotificationClientData(notif);
+
+       switch (reason) {
+       case WMEscapeTextMovement:
+               stopEditItem(menu, False);
+               break;
+
+       case WMReturnTextMovement:
+               stopEditItem(menu, True);
+               break;
+
+       case WMTabTextMovement:
+               stopEditItem(menu, True);
+
+               i = WMGetFirstInArray(menu->items, menu->selectedItem);
+               item = WMGetFromArray(menu->items, i + 1);
+               if (item != NULL) {
+                       selectItem(menu, item);
+                       editItemLabel(item);
+               }
+               break;
+
+       case WMBacktabTextMovement:
+               stopEditItem(menu, True);
+
+               i = WMGetFirstInArray(menu->items, menu->selectedItem);
+               item = WMGetFromArray(menu->items, i - 1);
+               if (item != NULL) {
+                       selectItem(menu, item);
+                       editItemLabel(item);
+               }
+               break;
+       }
+}
+
+static WMTextFieldDelegate textFieldDelegate = {
+       NULL,
+       NULL,
+       NULL,
+       textEndedEditing,
+       NULL,
+       NULL
+};
+
+static void editItemLabel(WEditMenuItem * item)
+{
+       WEditMenu *mPtr = item->parent;
+       WMTextField *tf;
+
+       if (!mPtr->flags.isEditable) {
+               return;
+       }
+
+       if (!mPtr->tfield) {
+               mPtr->tfield = WMCreateTextField(mPtr);
+               WMSetTextFieldBeveled(mPtr->tfield, False);
+               WMRealizeWidget(mPtr->tfield);
+
+               mPtr->tdelegate = wmalloc(sizeof(WMTextFieldDelegate));
+               memcpy(mPtr->tdelegate, &textFieldDelegate, sizeof(WMTextFieldDelegate));
+
+               mPtr->tdelegate->data = mPtr;
+
+               WMSetTextFieldDelegate(mPtr->tfield, mPtr->tdelegate);
+       }
+       tf = mPtr->tfield;
+
+       WMSetTextFieldText(tf, item->label);
+       WMSelectTextFieldRange(tf, wmkrange(0, strlen(item->label)));
+       WMResizeWidget(tf, mPtr->view->size.width, mPtr->itemHeight);
+       WMMoveWidget(tf, 0, item->view->pos.y);
+       WMMapWidget(tf);
+       WMSetFocusToWidget(tf);
+
+       mPtr->flags.isEditing = 1;
+}
+
+/* -------------------------------------------------- */
+
+static void slideWindow(Display * dpy, Window win, int srcX, int srcY, int dstX, int dstY)
+{
+       double x, y, dx, dy;
+       int i;
+       int iterations;
+
+       iterations = WMIN(25, WMAX(abs(dstX - srcX), abs(dstY - srcY)));
+
+       x = srcX;
+       y = srcY;
+
+       dx = (double)(dstX - srcX) / iterations;
+       dy = (double)(dstY - srcY) / iterations;
+
+       for (i = 0; i <= iterations; i++) {
+               XMoveWindow(dpy, win, x, y);
+               XFlush(dpy);
+
+               wusleep(800);
+
+               x += dx;
+               y += dy;
+       }
+}
+
+static int errorHandler(Display * d, XErrorEvent * ev)
+{
+       /* just ignore */
+       return 0;
+}
+
+static WEditMenu *findMenuInWindow(Display * dpy, Window toplevel, int x, int y)
+{
+       Window foo, bar;
+       Window *children;
+       unsigned nchildren;
+       int i;
+       WEditMenu *menu;
+       WMView *view;
+       int (*oldHandler) (Display *, XErrorEvent *);
+
+       view = W_GetViewForXWindow(dpy, toplevel);
+       if (view && view->self && WMWidgetClass(view->self) == EditMenuClass) {
+               menu = (WEditMenu *) view->self;
+               if (menu->flags.acceptsDrop) {
+                       return menu;
+               }
+       }
+
+       if (!XQueryTree(dpy, toplevel, &foo, &bar, &children, &nchildren) || children == NULL) {
+               return NULL;
+       }
+
+       oldHandler = XSetErrorHandler(errorHandler);
+
+       /* first window that contains the point is the one */
+       for (i = nchildren - 1; i >= 0; i--) {
+               XWindowAttributes attr;
+
+               if (XGetWindowAttributes(dpy, children[i], &attr)
+                   && attr.map_state == IsViewable
+                   && x >= attr.x && y >= attr.y && x < attr.x + attr.width && y < attr.y + attr.height) {
+                       Window tmp;
+
+                       tmp = children[i];
+
+                       menu = findMenuInWindow(dpy, tmp, x - attr.x, y - attr.y);
+                       if (menu) {
+                               XFree(children);
+                               return menu;
+                       }
+               }
+       }
+
+       XSetErrorHandler(oldHandler);
+
+       XFree(children);
+       return NULL;
+}
+
+static void handleDragOver(WEditMenu * menu, WMView * view, WEditMenuItem * item, int x, int y)
+{
+       WMScreen *scr = W_VIEW_SCREEN(menu->view);
+       Window blaw;
+       int mx, my;
+       int offs;
+
+       XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view), scr->rootWin, 0, 0, &mx, &my, &blaw);
+
+       offs = menu->flags.standalone ? 0 : 1;
+
+       W_MoveView(view, mx + offs, y);
+       if (view->size.width != menu->view->size.width) {
+               W_ResizeView(view, menu->view->size.width - 2 * offs, menu->itemHeight);
+               W_ResizeView(item->view, menu->view->size.width - 2 * offs, menu->itemHeight);
+       }
+}
+
+static void handleItemDrop(WEditMenu * menu, WEditMenuItem * item, int x, int y)
+{
+       WMScreen *scr = W_VIEW_SCREEN(menu->view);
+       Window blaw;
+       int mx, my;
+       int index;
+
+       XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view), scr->rootWin, 0, 0, &mx, &my, &blaw);
+
+       index = y - my;
+       if (menu->flags.isTitled) {
+               index -= menu->titleHeight;
+       }
+       index = (index + menu->itemHeight / 2) / menu->itemHeight;
+       if (index < 0)
+               index = 0;
+
+       if (menu->flags.isTitled) {
+               index++;
+       }
+
+       if (index > WMGetArrayItemCount(menu->items)) {
+               WMAddToArray(menu->items, item);
+       } else {
+               WMInsertInArray(menu->items, index, item);
+       }
+
+       W_ReparentView(item->view, menu->view, 0, index * menu->itemHeight);
+
+       item->parent = menu;
+       if (item->submenu) {
+               item->submenu->parent = menu;
+       }
+
+       updateMenuContents(menu);
+}
+
+static void dragMenu(WEditMenu * menu)
+{
+       WMScreen *scr = W_VIEW_SCREEN(menu->view);
+       XEvent ev;
+       Bool done = False;
+       int dx, dy;
+       unsigned blau;
+       Window blaw;
+
+       XGetGeometry(scr->display, W_VIEW_DRAWABLE(menu->view), &blaw, &dx, &dy, &blau, &blau, &blau, &blau);
+
+       XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view), scr->rootWin, dx, dy, &dx, &dy, &blaw);
+
+       dx = menu->dragX - dx;
+       dy = menu->dragY - dy;
+
+       XGrabPointer(scr->display, scr->rootWin, False,
+                    ButtonReleaseMask | ButtonMotionMask,
+                    GrabModeAsync, GrabModeAsync, None, scr->defaultCursor, CurrentTime);
+
+       if (menu->parent)
+               WTearOffEditMenu(menu->parent, menu);
+
+       while (!done) {
+               WMNextEvent(scr->display, &ev);
+
+               switch (ev.type) {
+               case ButtonRelease:
+                       done = True;
+                       break;
+
+               case MotionNotify:
+                       while (XCheckMaskEvent(scr->display, ButtonMotionMask, &ev)) ;
+
+                       WMMoveWidget(menu, ev.xmotion.x_root - dx, ev.xmotion.y_root - dy);
+                       break;
+
+               default:
+                       WMHandleEvent(&ev);
+                       break;
+               }
+       }
+
+       XUngrabPointer(scr->display, CurrentTime);
+}
+
+static WEditMenuItem *duplicateItem(WEditMenuItem * item)
+{
+       WEditMenuItem *nitem;
+
+       nitem = WCreateEditMenuItem(item->parent, item->label, False);
+       if (item->pixmap)
+               nitem->pixmap = WMRetainPixmap(item->pixmap);
+
+       return nitem;
+}
+
+static WEditMenu *duplicateMenu(WEditMenu * menu)
+{
+       WEditMenu *nmenu;
+       WEditMenuItem *item;
+       WMArrayIterator iter;
+       Bool first = menu->flags.isTitled;
+
+       nmenu = WCreateEditMenu(WMWidgetScreen(menu), WGetEditMenuTitle(menu));
+
+       memcpy(&nmenu->flags, &menu->flags, sizeof(menu->flags));
+       nmenu->delegate = menu->delegate;
+
+       WM_ITERATE_ARRAY(menu->items, item, iter) {
+               WEditMenuItem *nitem;
+
+               if (first) {
+                       first = False;
+                       continue;
+               }
+
+               nitem = WAddMenuItemWithTitle(nmenu, item->label);
+               if (item->pixmap)
+                       WSetEditMenuItemImage(nitem, item->pixmap);
+
+               if (menu->delegate && menu->delegate->itemCloned) {
+                       (*menu->delegate->itemCloned) (menu->delegate, menu, item, nitem);
+               }
+       }
+
+       WMRealizeWidget(nmenu);
+
+       return nmenu;
+}
+
+static void dragItem(WEditMenu * menu, WEditMenuItem * item, Bool copy)
+{
+       static XColor black = { 0, 0, 0, 0, DoRed | DoGreen | DoBlue };
+       static XColor green = { 0x0045b045, 0x4500, 0xb000, 0x4500, DoRed | DoGreen | DoBlue };
+       static XColor back = { 0, 0xffff, 0xffff, 0xffff, DoRed | DoGreen | DoBlue };
+       Display *dpy = W_VIEW_DISPLAY(menu->view);
+       WMScreen *scr = W_VIEW_SCREEN(menu->view);
+       int x, y;
+       int dx, dy;
+       Bool done = False;
+       Window blaw;
+       int blai;
+       unsigned blau;
+       Window win;
+       WMView *dview;
+       int orix, oriy;
+       Bool enteredMenu = False;
+       WMSize oldSize = item->view->size;
+       WEditMenuItem *dritem = item;
+       WEditMenu *dmenu = NULL;
+
+       if (item->flags.isTitle) {
+               WMRaiseWidget(menu);
+
+               dragMenu(menu);
+
+               return;
+       }
+
+       selectItem(menu, NULL);
+
+       win = scr->rootWin;
+
+       XTranslateCoordinates(dpy, W_VIEW_DRAWABLE(item->view), win, 0, 0, &orix, &oriy, &blaw);
+
+       dview = W_CreateUnmanagedTopView(scr);
+       W_SetViewBackgroundColor(dview, scr->black);
+       W_ResizeView(dview, W_VIEW_WIDTH(item->view), W_VIEW_HEIGHT(item->view));
+       W_MoveView(dview, orix, oriy);
+       W_RealizeView(dview);
+
+       if (menu->flags.isFactory || copy) {
+               dritem = duplicateItem(item);
+
+               W_ReparentView(dritem->view, dview, 0, 0);
+               WMResizeWidget(dritem, oldSize.width, oldSize.height);
+               WMRealizeWidget(dritem);
+               WMMapWidget(dritem);
+       } else {
+               W_ReparentView(item->view, dview, 0, 0);
+       }
+
+       W_MapView(dview);
+
+       dx = menu->dragX - orix;
+       dy = menu->dragY - oriy;
+
+       XGrabPointer(dpy, scr->rootWin, False,
+                    ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
+                    GrabModeAsync, GrabModeAsync, None, scr->defaultCursor, CurrentTime);
+
+       if (menu->flags.acceptsDrop)
+               XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
+
+       while (!done) {
+               XEvent ev;
+
+               WMNextEvent(dpy, &ev);
+
+               switch (ev.type) {
+               case MotionNotify:
+                       while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev)) ;
+
+                       XQueryPointer(dpy, win, &blaw, &blaw, &blai, &blai, &x, &y, &blau);
+
+                       dmenu = findMenuInWindow(dpy, win, x, y);
+
+                       if (dmenu) {
+                               handleDragOver(dmenu, dview, dritem, x - dx, y - dy);
+                               if (!enteredMenu) {
+                                       enteredMenu = True;
+                                       XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
+                               }
+                       } else {
+                               if (enteredMenu) {
+                                       W_ResizeView(dview, oldSize.width, oldSize.height);
+                                       W_ResizeView(dritem->view, oldSize.width, oldSize.height);
+                                       enteredMenu = False;
+                                       XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
+                               }
+                               W_MoveView(dview, x - dx, y - dy);
+                       }
+
+                       break;
+
+               case ButtonRelease:
+                       done = True;
+                       break;
+
+               default:
+                       WMHandleEvent(&ev);
+                       break;
+               }
+       }
+       XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
+
+       XUngrabPointer(dpy, CurrentTime);
+
+       if (!enteredMenu) {
+               Bool rem = True;
+
+               if (!menu->flags.isFactory && !copy) {
+                       W_UnmapView(dview);
+                       if (menu->delegate && menu->delegate->shouldRemoveItem) {
+                               rem = (*menu->delegate->shouldRemoveItem) (menu->delegate, menu, item);
+                       }
+                       W_MapView(dview);
+               }
+
+               if (!rem || menu->flags.isFactory || copy) {
+                       slideWindow(dpy, W_VIEW_DRAWABLE(dview), x - dx, y - dy, orix, oriy);
+
+                       if (!menu->flags.isFactory && !copy) {
+                               WRemoveEditMenuItem(menu, dritem);
+                               handleItemDrop(dmenu ? dmenu : menu, dritem, orix, oriy);
+                       }
+               } else {
+                       WRemoveEditMenuItem(menu, dritem);
+               }
+       } else {
+               WRemoveEditMenuItem(menu, dritem);
+
+               if (menu->delegate && menu->delegate->itemCloned && (menu->flags.isFactory || copy)) {
+                       (*menu->delegate->itemCloned) (menu->delegate, menu, item, dritem);
+               }
+
+               handleItemDrop(dmenu, dritem, x - dy, y - dy);
+
+               if (item->submenu && (menu->flags.isFactory || copy)) {
+                       WEditMenu *submenu;
+
+                       submenu = duplicateMenu(item->submenu);
+                       WSetEditMenuSubmenu(dmenu, dritem, submenu);
+               }
+       }
+
+       /* can't destroy now because we're being called from
+        * the event handler of the item. and destroying now,
+        * would mean destroying the item too in some cases.
+        */
+       WMAddIdleHandler((WMCallback *) W_DestroyView, dview);
+}
+
+static void handleItemClick(XEvent * event, void *data)
+{
+       WEditMenuItem *item = (WEditMenuItem *) data;
+       WEditMenu *menu = item->parent;
+
+       switch (event->type) {
+       case ButtonPress:
+               selectItem(menu, item);
+
+               if (WMIsDoubleClick(event)) {
+                       editItemLabel(item);
+               }
+
+               menu->flags.isDragging = 1;
+               menu->dragX = event->xbutton.x_root;
+               menu->dragY = event->xbutton.y_root;
+               break;
+
+       case ButtonRelease:
+               menu->flags.isDragging = 0;
+               break;
+
+       case MotionNotify:
+               if (menu->flags.isDragging) {
+                       if (abs(event->xbutton.x_root - menu->dragX) > 5
+                           || abs(event->xbutton.y_root - menu->dragY) > 5) {
+                               menu->flags.isDragging = 0;
+                               dragItem(menu, item, event->xbutton.state & ControlMask);
+                       }
+               }
+               break;
+       }
+}
+
+static void destroyEditMenu(WEditMenu * mPtr)
+{
+       WMRemoveNotificationObserver(mPtr);
+
+       WMFreeArray(mPtr->items);
+
+       wfree(mPtr->tdelegate);
+
+       wfree(mPtr);
+}