1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-19 20:38:08 +01:00
Files
wmaker/WPrefs.app/editmenu.c
Tobias Stoeckmann cc30444dda No need to call memset after wmalloc
memset is the last function call in wmalloc, just before it returns the
newly allocated memory.  Therefore it is not needed to call it again
after wmalloc call.  Although I would prefer to switch wmalloc to a
calloc-based wcalloc function, the compatibility of WINGs for old apps
should be kept.
2012-05-04 18:41:01 -03:00

1337 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(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));
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));
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;
uintptr_t reason;
int i;
WEditMenuItem *item;
if (!menu->flags.isEditing)
return;
reason = (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, 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, 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);
}