forked from vitrine/wmaker
We can reduce the WMNotification API surface area further by getting rid of WMCreateNotification and WMPostNotification. WMNotification remains a first-class object, but it is no longer possible for client code to create a notification object directly. Notifications must now be posted through WMPostNotificationName, which handles notification creation and destruction on its own. This will simplify the notification lifecycle and make the Rust rewrite simpler. (Notifications no longer need to be reference-counted, heap-allocated objects that might be saved somewhere after they are dispatched.) WTextField code which reused the WMNotification struct has been modified to take parameters of the correct type directly, instead of through a WMNotification's void* data field.
1340 lines
29 KiB
C
1340 lines
29 KiB
C
/* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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(void)
|
|
{
|
|
/* register our widget with WINGs and get our widget class ID */
|
|
if (!EditMenuItemClass) {
|
|
EditMenuItemClass = W_RegisterUserWidget();
|
|
}
|
|
|
|
return EditMenuItemClass;
|
|
}
|
|
|
|
WEditMenuItem *WCreateEditMenuItem(WMWidget * parent, const char *title, Bool isTitle)
|
|
{
|
|
WEditMenuItem *iPtr;
|
|
WMScreen *scr = WMWidgetScreen(parent);
|
|
|
|
if (!EditMenuItemClass)
|
|
InitEditMenuItem();
|
|
|
|
iPtr = wmalloc(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(void)
|
|
{
|
|
/* 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;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) not;
|
|
|
|
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, const char *title)
|
|
{
|
|
WEditMenu *mPtr;
|
|
WEditMenuItem *item;
|
|
|
|
if (!EditMenuClass)
|
|
InitEditMenu();
|
|
|
|
mPtr = wmalloc(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, const 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, const 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, const char *title)
|
|
{
|
|
return WInsertMenuItemWithTitle(mPtr, WMGetArrayItemCount(mPtr->items), title);
|
|
}
|
|
|
|
void WSetEditMenuTitle(WEditMenu * mPtr, const 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(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;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) w;
|
|
|
|
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(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(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)
|
|
{
|
|
if (apply) {
|
|
wfree(menu->selectedItem->label);
|
|
menu->selectedItem->label = WMGetTextFieldText(menu->tfield);
|
|
|
|
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, WMTextMovementType reason)
|
|
{
|
|
WEditMenu *menu = (WEditMenu *) self->data;
|
|
int i;
|
|
WEditMenuItem *item;
|
|
|
|
if (!menu->flags.isEditing)
|
|
return;
|
|
|
|
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)
|
|
{
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) d;
|
|
(void) 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 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 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, 0 };
|
|
static XColor green = { 0x0045b045, 0x4500, 0xb000, 0x4500, DoRed | DoGreen | DoBlue, 0 };
|
|
static XColor back = { 0, 0xffff, 0xffff, 0xffff, DoRed | DoGreen | DoBlue, 0 };
|
|
Display *dpy = W_VIEW_DISPLAY(menu->view);
|
|
WMScreen *scr = W_VIEW_SCREEN(menu->view);
|
|
int x = 0, y = 0;
|
|
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, 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, 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, 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);
|
|
}
|