1 /* editmenu.c - editable menus
2  *
3  *  WPrefs - Window Maker Preferences Program
4  *
5  *  Copyright (c) 2000-2003 Alfredo K. Kojima
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include <WINGs/WINGsP.h>
23 #include <WINGs/WUtil.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <assert.h>
27 #include <ctype.h>
28 
29 #include "editmenu.h"
30 
31 typedef struct W_EditMenuItem {
32 	W_Class widgetClass;
33 	WMView *view;
34 
35 	struct W_EditMenu *parent;
36 
37 	char *label;
38 	WMPixmap *pixmap;	/* pixmap to show at left */
39 
40 	void *data;
41 	WMCallback *destroyData;
42 
43 	struct W_EditMenu *submenu;	/* if it's a cascade, NULL otherwise */
44 
45 	struct {
46 		unsigned isTitle:1;
47 		unsigned isHighlighted:1;
48 	} flags;
49 } EditMenuItem;
50 
51 typedef struct W_EditMenu {
52 	W_Class widgetClass;
53 	WMView *view;
54 
55 	struct W_EditMenu *parent;
56 
57 	WMArray *items;		/* EditMenuItem */
58 
59 	EditMenuItem *selectedItem;
60 
61 	WMTextField *tfield;
62 
63 	WMButton *closeB;
64 
65 	int titleHeight;
66 	int itemHeight;
67 
68 	WEditMenuDelegate *delegate;
69 
70 	WMTextFieldDelegate *tdelegate;
71 
72 	/* item dragging */
73 	int draggedItem;
74 	int dragX, dragY;
75 
76 	/* only for non-standalone menu */
77 	WMSize maxSize;
78 	WMSize minSize;
79 
80 	struct {
81 		unsigned standalone:1;
82 		unsigned isTitled:1;
83 
84 		unsigned acceptsDrop:1;
85 		unsigned isFactory:1;
86 		unsigned isSelectable:1;
87 		unsigned isEditable:1;
88 
89 		unsigned isTornOff:1;
90 
91 		unsigned isDragging:1;
92 		unsigned isEditing:1;
93 
94 		unsigned wasMapped:1;
95 	} flags;
96 } EditMenu;
97 
98 /******************** WEditMenuItem ********************/
99 
100 static void destroyEditMenuItem(WEditMenuItem * iPtr);
101 static void paintEditMenuItem(WEditMenuItem * iPtr);
102 static void handleItemEvents(XEvent * event, void *data);
103 
104 static void handleItemClick(XEvent * event, void *data);
105 
106 static W_Class EditMenuItemClass = 0;
107 
InitEditMenuItem(void)108 W_Class InitEditMenuItem(void)
109 {
110 	/* register our widget with WINGs and get our widget class ID */
111 	if (!EditMenuItemClass) {
112 		EditMenuItemClass = W_RegisterUserWidget();
113 	}
114 
115 	return EditMenuItemClass;
116 }
117 
WCreateEditMenuItem(WMWidget * parent,const char * title,Bool isTitle)118 WEditMenuItem *WCreateEditMenuItem(WMWidget * parent, const char *title, Bool isTitle)
119 {
120 	WEditMenuItem *iPtr;
121 	WMScreen *scr = WMWidgetScreen(parent);
122 
123 	if (!EditMenuItemClass)
124 		InitEditMenuItem();
125 
126 	iPtr = wmalloc(sizeof(WEditMenuItem));
127 
128 	iPtr->widgetClass = EditMenuItemClass;
129 
130 	iPtr->view = W_CreateView(W_VIEW(parent));
131 	if (!iPtr->view) {
132 		wfree(iPtr);
133 		return NULL;
134 	}
135 	iPtr->view->self = iPtr;
136 
137 	iPtr->parent = parent;
138 
139 	iPtr->label = wstrdup(title);
140 
141 	iPtr->flags.isTitle = isTitle;
142 
143 	if (isTitle) {
144 		WMSetWidgetBackgroundColor(iPtr, WMBlackColor(scr));
145 	}
146 
147 	WMCreateEventHandler(iPtr->view, ExposureMask | StructureNotifyMask, handleItemEvents, iPtr);
148 
149 	WMCreateEventHandler(iPtr->view, ButtonPressMask | ButtonReleaseMask
150 			     | ButtonMotionMask, handleItemClick, iPtr);
151 
152 	return iPtr;
153 }
154 
WGetEditMenuItemTitle(WEditMenuItem * item)155 char *WGetEditMenuItemTitle(WEditMenuItem * item)
156 {
157 	return item->label;
158 }
159 
WGetEditMenuItemData(WEditMenuItem * item)160 void *WGetEditMenuItemData(WEditMenuItem * item)
161 {
162 	return item->data;
163 }
164 
WSetEditMenuItemData(WEditMenuItem * item,void * data,WMCallback * destroyer)165 void WSetEditMenuItemData(WEditMenuItem * item, void *data, WMCallback * destroyer)
166 {
167 	item->data = data;
168 	item->destroyData = destroyer;
169 }
170 
WSetEditMenuItemImage(WEditMenuItem * item,WMPixmap * pixmap)171 void WSetEditMenuItemImage(WEditMenuItem * item, WMPixmap * pixmap)
172 {
173 	if (item->pixmap)
174 		WMReleasePixmap(item->pixmap);
175 	item->pixmap = WMRetainPixmap(pixmap);
176 }
177 
paintEditMenuItem(WEditMenuItem * iPtr)178 static void paintEditMenuItem(WEditMenuItem * iPtr)
179 {
180 	WMScreen *scr = WMWidgetScreen(iPtr);
181 	WMColor *color;
182 	Window win = W_VIEW(iPtr)->window;
183 	int w = W_VIEW(iPtr)->size.width;
184 	int h = W_VIEW(iPtr)->size.height;
185 	WMFont *font = (iPtr->flags.isTitle ? scr->boldFont : scr->normalFont);
186 
187 	if (!iPtr->view->flags.realized)
188 		return;
189 
190 	color = scr->black;
191 	if (iPtr->flags.isTitle && !iPtr->flags.isHighlighted) {
192 		color = scr->white;
193 	}
194 
195 	XClearWindow(scr->display, win);
196 
197 	W_DrawRelief(scr, win, 0, 0, w + 1, h, WRRaised);
198 
199 	WMDrawString(scr, win, color, font, 5, 3, iPtr->label, strlen(iPtr->label));
200 
201 	if (iPtr->pixmap) {
202 		WMSize size = WMGetPixmapSize(iPtr->pixmap);
203 
204 		WMDrawPixmap(iPtr->pixmap, win, w - size.width - 5, (h - size.height) / 2);
205 	} else if (iPtr->submenu) {
206 		/* draw the cascade indicator */
207 		XDrawLine(scr->display, win, WMColorGC(scr->darkGray), w - 11, 6, w - 6, h / 2 - 1);
208 		XDrawLine(scr->display, win, WMColorGC(scr->white), w - 11, h - 8, w - 6, h / 2 - 1);
209 		XDrawLine(scr->display, win, WMColorGC(scr->black), w - 12, 6, w - 12, h - 8);
210 	}
211 }
212 
highlightItem(WEditMenuItem * iPtr,Bool high)213 static void highlightItem(WEditMenuItem * iPtr, Bool high)
214 {
215 	if (iPtr->flags.isTitle)
216 		return;
217 
218 	iPtr->flags.isHighlighted = high;
219 
220 	if (high) {
221 		WMSetWidgetBackgroundColor(iPtr, WMWhiteColor(WMWidgetScreen(iPtr)));
222 	} else {
223 		if (!iPtr->flags.isTitle) {
224 			WMSetWidgetBackgroundColor(iPtr, WMGrayColor(WMWidgetScreen(iPtr)));
225 		} else {
226 			WMSetWidgetBackgroundColor(iPtr, WMBlackColor(WMWidgetScreen(iPtr)));
227 		}
228 	}
229 }
230 
getItemTextWidth(WEditMenuItem * iPtr)231 static int getItemTextWidth(WEditMenuItem * iPtr)
232 {
233 	WMScreen *scr = WMWidgetScreen(iPtr);
234 
235 	if (iPtr->flags.isTitle) {
236 		return WMWidthOfString(scr->boldFont, iPtr->label, strlen(iPtr->label));
237 	} else {
238 		return WMWidthOfString(scr->normalFont, iPtr->label, strlen(iPtr->label));
239 	}
240 }
241 
handleItemEvents(XEvent * event,void * data)242 static void handleItemEvents(XEvent * event, void *data)
243 {
244 	WEditMenuItem *iPtr = (WEditMenuItem *) data;
245 
246 	switch (event->type) {
247 	case Expose:
248 		if (event->xexpose.count != 0)
249 			break;
250 		paintEditMenuItem(iPtr);
251 		break;
252 
253 	case DestroyNotify:
254 		destroyEditMenuItem(iPtr);
255 		break;
256 	}
257 }
258 
destroyEditMenuItem(WEditMenuItem * iPtr)259 static void destroyEditMenuItem(WEditMenuItem * iPtr)
260 {
261 	if (iPtr->label)
262 		wfree(iPtr->label);
263 	if (iPtr->data && iPtr->destroyData)
264 		(*iPtr->destroyData) (iPtr->data);
265 	if (iPtr->submenu)
266 		WMDestroyWidget(iPtr->submenu);
267 
268 	wfree(iPtr);
269 }
270 
271 /******************** WEditMenu *******************/
272 
273 static void destroyEditMenu(WEditMenu * mPtr);
274 
275 static void updateMenuContents(WEditMenu * mPtr);
276 
277 static void handleEvents(XEvent * event, void *data);
278 
279 static void editItemLabel(WEditMenuItem * item);
280 
281 static void stopEditItem(WEditMenu * menu, Bool apply);
282 
283 static void deselectItem(WEditMenu * menu);
284 
285 static W_Class EditMenuClass = 0;
286 
InitEditMenu(void)287 W_Class InitEditMenu(void)
288 {
289 	/* register our widget with WINGs and get our widget class ID */
290 	if (!EditMenuClass) {
291 
292 		EditMenuClass = W_RegisterUserWidget();
293 	}
294 
295 	return EditMenuClass;
296 }
297 
298 typedef struct {
299 	int flags;
300 	int window_style;
301 	int window_level;
302 	int reserved;
303 	Pixmap miniaturize_pixmap;	/* pixmap for miniaturize button */
304 	Pixmap close_pixmap;	/* pixmap for close button */
305 	Pixmap miniaturize_mask;	/* miniaturize pixmap mask */
306 	Pixmap close_mask;	/* close pixmap mask */
307 	int extra_flags;
308 } GNUstepWMAttributes;
309 
310 #define GSWindowStyleAttr       (1<<0)
311 #define GSWindowLevelAttr       (1<<1)
312 
writeGNUstepWMAttr(WMScreen * scr,Window window,GNUstepWMAttributes * attr)313 static void writeGNUstepWMAttr(WMScreen * scr, Window window, GNUstepWMAttributes * attr)
314 {
315 	unsigned long data[9];
316 
317 	/* handle idiot compilers where array of CARD32 != struct of CARD32 */
318 	data[0] = attr->flags;
319 	data[1] = attr->window_style;
320 	data[2] = attr->window_level;
321 	data[3] = 0;		/* reserved */
322 	/* The X protocol says XIDs are 32bit */
323 	data[4] = attr->miniaturize_pixmap;
324 	data[5] = attr->close_pixmap;
325 	data[6] = attr->miniaturize_mask;
326 	data[7] = attr->close_mask;
327 	data[8] = attr->extra_flags;
328 	XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
329 			32, PropModeReplace, (unsigned char *)data, 9);
330 }
331 
realizeObserver(void * self,WMNotification * not)332 static void realizeObserver(void *self, WMNotification * not)
333 {
334 	WEditMenu *menu = (WEditMenu *) self;
335 	GNUstepWMAttributes attribs;
336 
337 	/* Parameter not used, but tell the compiler that it is ok */
338 	(void) not;
339 
340 	memset(&attribs, 0, sizeof(GNUstepWMAttributes));
341 	attribs.flags = GSWindowStyleAttr | GSWindowLevelAttr;
342 	attribs.window_style = WMBorderlessWindowMask;
343 	attribs.window_level = WMSubmenuWindowLevel;
344 
345 	writeGNUstepWMAttr(WMWidgetScreen(menu), menu->view->window, &attribs);
346 }
347 
itemSelectObserver(void * self,WMNotification * notif)348 static void itemSelectObserver(void *self, WMNotification * notif)
349 {
350 	WEditMenu *menu = (WEditMenu *) self;
351 	WEditMenu *rmenu;
352 
353 	rmenu = (WEditMenu *) WMGetNotificationObject(notif);
354 	/* check whether rmenu is from the same hierarchy of menu? */
355 
356 	if (rmenu == menu) {
357 		return;
358 	}
359 
360 	if (menu->selectedItem) {
361 		if (!menu->selectedItem->submenu)
362 			deselectItem(menu);
363 		if (menu->flags.isEditing)
364 			stopEditItem(menu, False);
365 	}
366 }
367 
makeEditMenu(WMScreen * scr,WMWidget * parent,const char * title)368 static WEditMenu *makeEditMenu(WMScreen * scr, WMWidget * parent, const char *title)
369 {
370 	WEditMenu *mPtr;
371 	WEditMenuItem *item;
372 
373 	if (!EditMenuClass)
374 		InitEditMenu();
375 
376 	mPtr = wmalloc(sizeof(WEditMenu));
377 
378 	mPtr->widgetClass = EditMenuClass;
379 
380 	if (parent) {
381 		mPtr->view = W_CreateView(W_VIEW(parent));
382 		mPtr->flags.standalone = 0;
383 	} else {
384 		mPtr->view = W_CreateTopView(scr);
385 		mPtr->flags.standalone = 1;
386 	}
387 	if (!mPtr->view) {
388 		wfree(mPtr);
389 		return NULL;
390 	}
391 	mPtr->view->self = mPtr;
392 
393 	mPtr->flags.isSelectable = 1;
394 	mPtr->flags.isEditable = 1;
395 
396 	W_SetViewBackgroundColor(mPtr->view, scr->darkGray);
397 
398 	WMAddNotificationObserver(realizeObserver, mPtr, WMViewRealizedNotification, mPtr->view);
399 
400 	WMAddNotificationObserver(itemSelectObserver, mPtr, "EditMenuItemSelected", NULL);
401 
402 	mPtr->items = WMCreateArray(4);
403 
404 	WMCreateEventHandler(mPtr->view, ExposureMask | StructureNotifyMask, handleEvents, mPtr);
405 
406 	if (title != NULL) {
407 		item = WCreateEditMenuItem(mPtr, title, True);
408 
409 		WMMapWidget(item);
410 		WMAddToArray(mPtr->items, item);
411 
412 		mPtr->flags.isTitled = 1;
413 	}
414 
415 	mPtr->itemHeight = WMFontHeight(scr->normalFont) + 6;
416 	mPtr->titleHeight = WMFontHeight(scr->boldFont) + 8;
417 
418 	updateMenuContents(mPtr);
419 
420 	return mPtr;
421 }
422 
WCreateEditMenu(WMScreen * scr,const char * title)423 WEditMenu *WCreateEditMenu(WMScreen * scr, const char *title)
424 {
425 	return makeEditMenu(scr, NULL, title);
426 }
427 
WCreateEditMenuPad(WMWidget * parent)428 WEditMenu *WCreateEditMenuPad(WMWidget * parent)
429 {
430 	return makeEditMenu(WMWidgetScreen(parent), parent, NULL);
431 }
432 
WSetEditMenuDelegate(WEditMenu * mPtr,WEditMenuDelegate * delegate)433 void WSetEditMenuDelegate(WEditMenu * mPtr, WEditMenuDelegate * delegate)
434 {
435 	mPtr->delegate = delegate;
436 }
437 
WInsertMenuItemWithTitle(WEditMenu * mPtr,int index,const char * title)438 WEditMenuItem *WInsertMenuItemWithTitle(WEditMenu * mPtr, int index, const char *title)
439 {
440 	WEditMenuItem *item;
441 
442 	item = WCreateEditMenuItem(mPtr, title, False);
443 
444 	WMMapWidget(item);
445 
446 	if (index >= WMGetArrayItemCount(mPtr->items)) {
447 		WMAddToArray(mPtr->items, item);
448 	} else {
449 		if (index < 0)
450 			index = 0;
451 		if (mPtr->flags.isTitled)
452 			index++;
453 		WMInsertInArray(mPtr->items, index, item);
454 	}
455 
456 	updateMenuContents(mPtr);
457 
458 	return item;
459 }
460 
WGetEditMenuItem(WEditMenu * mPtr,int index)461 WEditMenuItem *WGetEditMenuItem(WEditMenu * mPtr, int index)
462 {
463 	if (index >= WMGetArrayItemCount(mPtr->items))
464 		return NULL;
465 
466 	return WMGetFromArray(mPtr->items, index + (mPtr->flags.isTitled ? 1 : 0));
467 }
468 
WAddMenuItemWithTitle(WEditMenu * mPtr,const char * title)469 WEditMenuItem *WAddMenuItemWithTitle(WEditMenu * mPtr, const char *title)
470 {
471 	return WInsertMenuItemWithTitle(mPtr, WMGetArrayItemCount(mPtr->items), title);
472 }
473 
WSetEditMenuTitle(WEditMenu * mPtr,const char * title)474 void WSetEditMenuTitle(WEditMenu * mPtr, const char *title)
475 {
476 	WEditMenuItem *item;
477 
478 	item = WMGetFromArray(mPtr->items, 0);
479 
480 	wfree(item->label);
481 	item->label = wstrdup(title);
482 
483 	updateMenuContents(mPtr);
484 
485 	WMRedisplayWidget(item);
486 }
487 
WGetEditMenuTitle(WEditMenu * mPtr)488 char *WGetEditMenuTitle(WEditMenu * mPtr)
489 {
490 	WEditMenuItem *item;
491 
492 	item = WMGetFromArray(mPtr->items, 0);
493 
494 	return item->label;
495 }
496 
WSetEditMenuAcceptsDrop(WEditMenu * mPtr,Bool flag)497 void WSetEditMenuAcceptsDrop(WEditMenu * mPtr, Bool flag)
498 {
499 	mPtr->flags.acceptsDrop = flag;
500 }
501 
WSetEditMenuSubmenu(WEditMenu * mPtr,WEditMenuItem * item,WEditMenu * submenu)502 void WSetEditMenuSubmenu(WEditMenu * mPtr, WEditMenuItem * item, WEditMenu * submenu)
503 {
504 	item->submenu = submenu;
505 	submenu->parent = mPtr;
506 
507 	paintEditMenuItem(item);
508 }
509 
WGetEditMenuSubmenu(WEditMenuItem * item)510 WEditMenu *WGetEditMenuSubmenu(WEditMenuItem *item)
511 {
512 	return item->submenu;
513 }
514 
WRemoveEditMenuItem(WEditMenu * mPtr,WEditMenuItem * item)515 void WRemoveEditMenuItem(WEditMenu * mPtr, WEditMenuItem * item)
516 {
517 	if (WMRemoveFromArray(mPtr->items, item) != 0) {
518 		updateMenuContents(mPtr);
519 	}
520 }
521 
WSetEditMenuSelectable(WEditMenu * mPtr,Bool flag)522 void WSetEditMenuSelectable(WEditMenu * mPtr, Bool flag)
523 {
524 	mPtr->flags.isSelectable = flag;
525 }
526 
WSetEditMenuEditable(WEditMenu * mPtr,Bool flag)527 void WSetEditMenuEditable(WEditMenu * mPtr, Bool flag)
528 {
529 	mPtr->flags.isEditable = flag;
530 }
531 
WSetEditMenuIsFactory(WEditMenu * mPtr,Bool flag)532 void WSetEditMenuIsFactory(WEditMenu * mPtr, Bool flag)
533 {
534 	mPtr->flags.isFactory = flag;
535 }
536 
WSetEditMenuMinSize(WEditMenu * mPtr,WMSize size)537 void WSetEditMenuMinSize(WEditMenu * mPtr, WMSize size)
538 {
539 	mPtr->minSize.width = size.width;
540 	mPtr->minSize.height = size.height;
541 }
542 
WSetEditMenuMaxSize(WEditMenu * mPtr,WMSize size)543 void WSetEditMenuMaxSize(WEditMenu * mPtr, WMSize size)
544 {
545 	mPtr->maxSize.width = size.width;
546 	mPtr->maxSize.height = size.height;
547 }
548 
WGetEditMenuLocationForSubmenu(WEditMenu * mPtr,WEditMenu * submenu)549 WMPoint WGetEditMenuLocationForSubmenu(WEditMenu * mPtr, WEditMenu * submenu)
550 {
551 	WMArrayIterator iter;
552 	WEditMenuItem *item;
553 	int y;
554 
555 	if (mPtr->flags.isTitled)
556 		y = -mPtr->titleHeight;
557 	else
558 		y = 0;
559 	WM_ITERATE_ARRAY(mPtr->items, item, iter) {
560 		if (item->submenu == submenu) {
561 			WMPoint pt = WMGetViewScreenPosition(mPtr->view);
562 
563 			return wmkpoint(pt.x + mPtr->view->size.width, pt.y + y);
564 		}
565 		y += W_VIEW_HEIGHT(item->view);
566 	}
567 
568 	puts("invalid submenu passed to WGetEditMenuLocationForSubmenu()");
569 
570 	return wmkpoint(0, 0);
571 }
572 
closeMenuAction(WMWidget * w,void * data)573 static void closeMenuAction(WMWidget * w, void *data)
574 {
575 	WEditMenu *menu = (WEditMenu *) data;
576 
577 	/* Parameter not used, but tell the compiler that it is ok */
578 	(void) w;
579 
580 	WMAddIdleHandler(WMDestroyWidget, menu->closeB);
581 	menu->closeB = NULL;
582 
583 	WEditMenuHide(menu);
584 }
585 
WTearOffEditMenu(WEditMenu * menu,WEditMenu * submenu)586 void WTearOffEditMenu(WEditMenu * menu, WEditMenu * submenu)
587 {
588 	WEditMenuItem *item;
589 
590 	submenu->flags.isTornOff = 1;
591 
592 	item = (WEditMenuItem *) WMGetFromArray(submenu->items, 0);
593 
594 	submenu->closeB = WMCreateCommandButton(item);
595 	WMResizeWidget(submenu->closeB, 15, 15);
596 	WMMoveWidget(submenu->closeB, W_VIEW(submenu)->size.width - 20, 3);
597 	WMRealizeWidget(submenu->closeB);
598 	WMSetButtonText(submenu->closeB, "X");
599 	WMSetButtonAction(submenu->closeB, closeMenuAction, submenu);
600 	WMMapWidget(submenu->closeB);
601 
602 	if (menu->selectedItem && menu->selectedItem->submenu == submenu)
603 		deselectItem(menu);
604 }
605 
WEditMenuIsTornOff(WEditMenu * mPtr)606 Bool WEditMenuIsTornOff(WEditMenu * mPtr)
607 {
608 	return mPtr->flags.isTornOff;
609 }
610 
WEditMenuHide(WEditMenu * mPtr)611 void WEditMenuHide(WEditMenu * mPtr)
612 {
613 	WEditMenuItem *item;
614 	int i = 0;
615 
616 	if (WMWidgetIsMapped(mPtr)) {
617 		WMUnmapWidget(mPtr);
618 		mPtr->flags.wasMapped = 1;
619 	} else {
620 		mPtr->flags.wasMapped = 0;
621 	}
622 	while ((item = WGetEditMenuItem(mPtr, i++))) {
623 		WEditMenu *submenu;
624 
625 		submenu = WGetEditMenuSubmenu(item);
626 		if (submenu) {
627 			WEditMenuHide(submenu);
628 		}
629 	}
630 }
631 
WEditMenuUnhide(WEditMenu * mPtr)632 void WEditMenuUnhide(WEditMenu * mPtr)
633 {
634 	WEditMenuItem *item;
635 	int i = 0;
636 
637 	if (mPtr->flags.wasMapped) {
638 		WMMapWidget(mPtr);
639 	}
640 	while ((item = WGetEditMenuItem(mPtr, i++))) {
641 		WEditMenu *submenu;
642 
643 		submenu = WGetEditMenuSubmenu(item);
644 		if (submenu) {
645 			WEditMenuUnhide(submenu);
646 		}
647 	}
648 }
649 
WEditMenuShowAt(WEditMenu * menu,int x,int y)650 void WEditMenuShowAt(WEditMenu * menu, int x, int y)
651 {
652 	XSizeHints *hints;
653 
654 	hints = XAllocSizeHints();
655 
656 	hints->flags = USPosition;
657 	hints->x = x;
658 	hints->y = y;
659 
660 	WMMoveWidget(menu, x, y);
661 	XSetWMNormalHints(W_VIEW_DISPLAY(menu->view), W_VIEW_DRAWABLE(menu->view), hints);
662 	XFree(hints);
663 
664 	WMMapWidget(menu);
665 }
666 
updateMenuContents(WEditMenu * mPtr)667 static void updateMenuContents(WEditMenu * mPtr)
668 {
669 	int newW, newH;
670 	int w;
671 	int i;
672 	int iheight = mPtr->itemHeight;
673 	int offs = 1;
674 	WMArrayIterator iter;
675 	WEditMenuItem *item;
676 
677 	newW = 0;
678 	newH = offs;
679 
680 	i = 0;
681 	WM_ITERATE_ARRAY(mPtr->items, item, iter) {
682 		w = getItemTextWidth(item);
683 
684 		newW = WMAX(w, newW);
685 
686 		WMMoveWidget(item, offs, newH);
687 		if (i == 0 && mPtr->flags.isTitled) {
688 			newH += mPtr->titleHeight;
689 		} else {
690 			newH += iheight;
691 		}
692 		i = 1;
693 	}
694 
695 	newW += iheight + 10;
696 	newH--;
697 
698 	if (mPtr->minSize.width)
699 		newW = WMAX(newW, mPtr->minSize.width);
700 	if (mPtr->maxSize.width)
701 		newW = WMIN(newW, mPtr->maxSize.width);
702 
703 	if (mPtr->minSize.height)
704 		newH = WMAX(newH, mPtr->minSize.height);
705 	if (mPtr->maxSize.height)
706 		newH = WMIN(newH, mPtr->maxSize.height);
707 
708 	if (W_VIEW(mPtr)->size.width == newW && mPtr->view->size.height == newH + 1)
709 		return;
710 
711 	W_ResizeView(mPtr->view, newW, newH + 1);
712 
713 	if (mPtr->closeB)
714 		WMMoveWidget(mPtr->closeB, newW - 20, 3);
715 
716 	newW -= 2 * offs;
717 
718 	i = 0;
719 	WM_ITERATE_ARRAY(mPtr->items, item, iter) {
720 		if (i == 0 && mPtr->flags.isTitled) {
721 			WMResizeWidget(item, newW, mPtr->titleHeight);
722 		} else {
723 			WMResizeWidget(item, newW, iheight);
724 		}
725 		i = 1;
726 	}
727 }
728 
deselectItem(WEditMenu * menu)729 static void deselectItem(WEditMenu * menu)
730 {
731 	WEditMenu *submenu;
732 	WEditMenuItem *item = menu->selectedItem;
733 
734 	highlightItem(item, False);
735 
736 	if (menu->delegate && menu->delegate->itemDeselected) {
737 		(*menu->delegate->itemDeselected) (menu->delegate, menu, item);
738 	}
739 	submenu = item->submenu;
740 
741 	if (submenu && !WEditMenuIsTornOff(submenu)) {
742 		WEditMenuHide(submenu);
743 	}
744 
745 	menu->selectedItem = NULL;
746 }
747 
selectItem(WEditMenu * menu,WEditMenuItem * item)748 static void selectItem(WEditMenu * menu, WEditMenuItem * item)
749 {
750 	if (!menu->flags.isSelectable || menu->selectedItem == item) {
751 		return;
752 	}
753 	if (menu->selectedItem) {
754 		deselectItem(menu);
755 	}
756 
757 	if (menu->flags.isEditing) {
758 		stopEditItem(menu, False);
759 	}
760 
761 	if (item && !item->flags.isTitle) {
762 		highlightItem(item, True);
763 
764 		if (item->submenu && !W_VIEW_MAPPED(item->submenu->view)) {
765 			WMPoint pt;
766 
767 			pt = WGetEditMenuLocationForSubmenu(menu, item->submenu);
768 
769 			WEditMenuShowAt(item->submenu, pt.x, pt.y);
770 
771 			item->submenu->flags.isTornOff = 0;
772 		}
773 
774 		WMPostNotificationName("EditMenuItemSelected", menu, NULL);
775 
776 		if (menu->delegate && menu->delegate->itemSelected) {
777 			(*menu->delegate->itemSelected) (menu->delegate, menu, item);
778 		}
779 	}
780 
781 	menu->selectedItem = item;
782 }
783 
paintMenu(WEditMenu * mPtr)784 static void paintMenu(WEditMenu * mPtr)
785 {
786 	W_View *view = mPtr->view;
787 
788 	W_DrawRelief(W_VIEW_SCREEN(view), W_VIEW_DRAWABLE(view), 0, 0,
789 		     W_VIEW_WIDTH(view), W_VIEW_HEIGHT(view), WRSimple);
790 }
791 
handleEvents(XEvent * event,void * data)792 static void handleEvents(XEvent * event, void *data)
793 {
794 	WEditMenu *mPtr = (WEditMenu *) data;
795 
796 	switch (event->type) {
797 	case DestroyNotify:
798 		destroyEditMenu(mPtr);
799 		break;
800 
801 	case Expose:
802 		if (event->xexpose.count == 0)
803 			paintMenu(mPtr);
804 		break;
805 	}
806 }
807 
808 /* -------------------------- Menu Label Editing ------------------------ */
809 
stopEditItem(WEditMenu * menu,Bool apply)810 static void stopEditItem(WEditMenu * menu, Bool apply)
811 {
812 	if (apply) {
813 		wfree(menu->selectedItem->label);
814 		menu->selectedItem->label = WMGetTextFieldText(menu->tfield);
815 
816 		updateMenuContents(menu);
817 
818 		if (menu->delegate && menu->delegate->itemEdited) {
819 			(*menu->delegate->itemEdited) (menu->delegate, menu, menu->selectedItem);
820 		}
821 
822 	}
823 	WMUnmapWidget(menu->tfield);
824 	menu->flags.isEditing = 0;
825 }
826 
textEndedEditing(struct WMTextFieldDelegate * self,WMNotification * notif)827 static void textEndedEditing(struct WMTextFieldDelegate *self, WMNotification * notif)
828 {
829 	WEditMenu *menu = (WEditMenu *) self->data;
830 	uintptr_t reason;
831 	int i;
832 	WEditMenuItem *item;
833 
834 	if (!menu->flags.isEditing)
835 		return;
836 
837 	reason = (uintptr_t)WMGetNotificationClientData(notif);
838 
839 	switch (reason) {
840 	case WMEscapeTextMovement:
841 		stopEditItem(menu, False);
842 		break;
843 
844 	case WMReturnTextMovement:
845 		stopEditItem(menu, True);
846 		break;
847 
848 	case WMTabTextMovement:
849 		stopEditItem(menu, True);
850 
851 		i = WMGetFirstInArray(menu->items, menu->selectedItem);
852 		item = WMGetFromArray(menu->items, i + 1);
853 		if (item != NULL) {
854 			selectItem(menu, item);
855 			editItemLabel(item);
856 		}
857 		break;
858 
859 	case WMBacktabTextMovement:
860 		stopEditItem(menu, True);
861 
862 		i = WMGetFirstInArray(menu->items, menu->selectedItem);
863 		item = WMGetFromArray(menu->items, i - 1);
864 		if (item != NULL) {
865 			selectItem(menu, item);
866 			editItemLabel(item);
867 		}
868 		break;
869 	}
870 }
871 
872 static WMTextFieldDelegate textFieldDelegate = {
873 	NULL,
874 	NULL,
875 	NULL,
876 	textEndedEditing,
877 	NULL,
878 	NULL
879 };
880 
editItemLabel(WEditMenuItem * item)881 static void editItemLabel(WEditMenuItem * item)
882 {
883 	WEditMenu *mPtr = item->parent;
884 	WMTextField *tf;
885 
886 	if (!mPtr->flags.isEditable) {
887 		return;
888 	}
889 
890 	if (!mPtr->tfield) {
891 		mPtr->tfield = WMCreateTextField(mPtr);
892 		WMSetTextFieldBeveled(mPtr->tfield, False);
893 		WMRealizeWidget(mPtr->tfield);
894 
895 		mPtr->tdelegate = wmalloc(sizeof(WMTextFieldDelegate));
896 		memcpy(mPtr->tdelegate, &textFieldDelegate, sizeof(WMTextFieldDelegate));
897 
898 		mPtr->tdelegate->data = mPtr;
899 
900 		WMSetTextFieldDelegate(mPtr->tfield, mPtr->tdelegate);
901 	}
902 	tf = mPtr->tfield;
903 
904 	WMSetTextFieldText(tf, item->label);
905 	WMSelectTextFieldRange(tf, wmkrange(0, strlen(item->label)));
906 	WMResizeWidget(tf, mPtr->view->size.width, mPtr->itemHeight);
907 	WMMoveWidget(tf, 0, item->view->pos.y);
908 	WMMapWidget(tf);
909 	WMSetFocusToWidget(tf);
910 
911 	mPtr->flags.isEditing = 1;
912 }
913 
914 /* -------------------------------------------------- */
915 
slideWindow(Display * dpy,Window win,int srcX,int srcY,int dstX,int dstY)916 static void slideWindow(Display * dpy, Window win, int srcX, int srcY, int dstX, int dstY)
917 {
918 	double x, y, dx, dy;
919 	int i;
920 	int iterations;
921 
922 	iterations = WMIN(25, WMAX(abs(dstX - srcX), abs(dstY - srcY)));
923 
924 	x = srcX;
925 	y = srcY;
926 
927 	dx = (double)(dstX - srcX) / iterations;
928 	dy = (double)(dstY - srcY) / iterations;
929 
930 	for (i = 0; i <= iterations; i++) {
931 		XMoveWindow(dpy, win, x, y);
932 		XFlush(dpy);
933 
934 		wusleep(800);
935 
936 		x += dx;
937 		y += dy;
938 	}
939 }
940 
errorHandler(Display * d,XErrorEvent * ev)941 static int errorHandler(Display * d, XErrorEvent * ev)
942 {
943 	/* Parameter not used, but tell the compiler that it is ok */
944 	(void) d;
945 	(void) ev;
946 
947 	/* just ignore */
948 	return 0;
949 }
950 
findMenuInWindow(Display * dpy,Window toplevel,int x,int y)951 static WEditMenu *findMenuInWindow(Display * dpy, Window toplevel, int x, int y)
952 {
953 	Window foo, bar;
954 	Window *children;
955 	unsigned nchildren;
956 	int i;
957 	WEditMenu *menu;
958 	WMView *view;
959 	int (*oldHandler) (Display *, XErrorEvent *);
960 
961 	view = W_GetViewForXWindow(dpy, toplevel);
962 	if (view && view->self && WMWidgetClass(view->self) == EditMenuClass) {
963 		menu = (WEditMenu *) view->self;
964 		if (menu->flags.acceptsDrop) {
965 			return menu;
966 		}
967 	}
968 
969 	if (!XQueryTree(dpy, toplevel, &foo, &bar, &children, &nchildren) || children == NULL) {
970 		return NULL;
971 	}
972 
973 	oldHandler = XSetErrorHandler(errorHandler);
974 
975 	/* first window that contains the point is the one */
976 	for (i = nchildren - 1; i >= 0; i--) {
977 		XWindowAttributes attr;
978 
979 		if (XGetWindowAttributes(dpy, children[i], &attr)
980 		    && attr.map_state == IsViewable
981 		    && x >= attr.x && y >= attr.y && x < attr.x + attr.width && y < attr.y + attr.height) {
982 			Window tmp;
983 
984 			tmp = children[i];
985 
986 			menu = findMenuInWindow(dpy, tmp, x - attr.x, y - attr.y);
987 			if (menu) {
988 				XFree(children);
989 				return menu;
990 			}
991 		}
992 	}
993 
994 	XSetErrorHandler(oldHandler);
995 
996 	XFree(children);
997 	return NULL;
998 }
999 
handleDragOver(WEditMenu * menu,WMView * view,WEditMenuItem * item,int y)1000 static void handleDragOver(WEditMenu *menu, WMView *view, WEditMenuItem *item, int y)
1001 {
1002 	WMScreen *scr = W_VIEW_SCREEN(menu->view);
1003 	Window blaw;
1004 	int mx, my;
1005 	int offs;
1006 
1007 	XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view), scr->rootWin, 0, 0, &mx, &my, &blaw);
1008 
1009 	offs = menu->flags.standalone ? 0 : 1;
1010 
1011 	W_MoveView(view, mx + offs, y);
1012 	if (view->size.width != menu->view->size.width) {
1013 		W_ResizeView(view, menu->view->size.width - 2 * offs, menu->itemHeight);
1014 		W_ResizeView(item->view, menu->view->size.width - 2 * offs, menu->itemHeight);
1015 	}
1016 }
1017 
handleItemDrop(WEditMenu * menu,WEditMenuItem * item,int y)1018 static void handleItemDrop(WEditMenu *menu, WEditMenuItem *item, int y)
1019 {
1020 	WMScreen *scr = W_VIEW_SCREEN(menu->view);
1021 	Window blaw;
1022 	int mx, my;
1023 	int index;
1024 
1025 	XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view), scr->rootWin, 0, 0, &mx, &my, &blaw);
1026 
1027 	index = y - my;
1028 	if (menu->flags.isTitled) {
1029 		index -= menu->titleHeight;
1030 	}
1031 	index = (index + menu->itemHeight / 2) / menu->itemHeight;
1032 	if (index < 0)
1033 		index = 0;
1034 
1035 	if (menu->flags.isTitled) {
1036 		index++;
1037 	}
1038 
1039 	if (index > WMGetArrayItemCount(menu->items)) {
1040 		WMAddToArray(menu->items, item);
1041 	} else {
1042 		WMInsertInArray(menu->items, index, item);
1043 	}
1044 
1045 	W_ReparentView(item->view, menu->view, 0, index * menu->itemHeight);
1046 
1047 	item->parent = menu;
1048 	if (item->submenu) {
1049 		item->submenu->parent = menu;
1050 	}
1051 
1052 	updateMenuContents(menu);
1053 }
1054 
dragMenu(WEditMenu * menu)1055 static void dragMenu(WEditMenu * menu)
1056 {
1057 	WMScreen *scr = W_VIEW_SCREEN(menu->view);
1058 	XEvent ev;
1059 	Bool done = False;
1060 	int dx, dy;
1061 	unsigned blau;
1062 	Window blaw;
1063 
1064 	XGetGeometry(scr->display, W_VIEW_DRAWABLE(menu->view), &blaw, &dx, &dy, &blau, &blau, &blau, &blau);
1065 
1066 	XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view), scr->rootWin, dx, dy, &dx, &dy, &blaw);
1067 
1068 	dx = menu->dragX - dx;
1069 	dy = menu->dragY - dy;
1070 
1071 	XGrabPointer(scr->display, scr->rootWin, False,
1072 		     ButtonReleaseMask | ButtonMotionMask,
1073 		     GrabModeAsync, GrabModeAsync, None, scr->defaultCursor, CurrentTime);
1074 
1075 	if (menu->parent)
1076 		WTearOffEditMenu(menu->parent, menu);
1077 
1078 	while (!done) {
1079 		WMNextEvent(scr->display, &ev);
1080 
1081 		switch (ev.type) {
1082 		case ButtonRelease:
1083 			done = True;
1084 			break;
1085 
1086 		case MotionNotify:
1087 			while (XCheckMaskEvent(scr->display, ButtonMotionMask, &ev)) ;
1088 
1089 			WMMoveWidget(menu, ev.xmotion.x_root - dx, ev.xmotion.y_root - dy);
1090 			break;
1091 
1092 		default:
1093 			WMHandleEvent(&ev);
1094 			break;
1095 		}
1096 	}
1097 
1098 	XUngrabPointer(scr->display, CurrentTime);
1099 }
1100 
duplicateItem(WEditMenuItem * item)1101 static WEditMenuItem *duplicateItem(WEditMenuItem * item)
1102 {
1103 	WEditMenuItem *nitem;
1104 
1105 	nitem = WCreateEditMenuItem(item->parent, item->label, False);
1106 	if (item->pixmap)
1107 		nitem->pixmap = WMRetainPixmap(item->pixmap);
1108 
1109 	return nitem;
1110 }
1111 
duplicateMenu(WEditMenu * menu)1112 static WEditMenu *duplicateMenu(WEditMenu * menu)
1113 {
1114 	WEditMenu *nmenu;
1115 	WEditMenuItem *item;
1116 	WMArrayIterator iter;
1117 	Bool first = menu->flags.isTitled;
1118 
1119 	nmenu = WCreateEditMenu(WMWidgetScreen(menu), WGetEditMenuTitle(menu));
1120 
1121 	memcpy(&nmenu->flags, &menu->flags, sizeof(menu->flags));
1122 	nmenu->delegate = menu->delegate;
1123 
1124 	WM_ITERATE_ARRAY(menu->items, item, iter) {
1125 		WEditMenuItem *nitem;
1126 
1127 		if (first) {
1128 			first = False;
1129 			continue;
1130 		}
1131 
1132 		nitem = WAddMenuItemWithTitle(nmenu, item->label);
1133 		if (item->pixmap)
1134 			WSetEditMenuItemImage(nitem, item->pixmap);
1135 
1136 		if (menu->delegate && menu->delegate->itemCloned) {
1137 			(*menu->delegate->itemCloned) (menu->delegate, menu, item, nitem);
1138 		}
1139 	}
1140 
1141 	WMRealizeWidget(nmenu);
1142 
1143 	return nmenu;
1144 }
1145 
dragItem(WEditMenu * menu,WEditMenuItem * item,Bool copy)1146 static void dragItem(WEditMenu * menu, WEditMenuItem * item, Bool copy)
1147 {
1148 	static XColor black = { 0, 0, 0, 0, DoRed | DoGreen | DoBlue, 0 };
1149 	static XColor green = { 0x0045b045, 0x4500, 0xb000, 0x4500, DoRed | DoGreen | DoBlue, 0 };
1150 	static XColor back = { 0, 0xffff, 0xffff, 0xffff, DoRed | DoGreen | DoBlue, 0 };
1151 	Display *dpy = W_VIEW_DISPLAY(menu->view);
1152 	WMScreen *scr = W_VIEW_SCREEN(menu->view);
1153 	int x, y;
1154 	int dx, dy;
1155 	Bool done = False;
1156 	Window blaw;
1157 	int blai;
1158 	unsigned blau;
1159 	Window win;
1160 	WMView *dview;
1161 	int orix, oriy;
1162 	Bool enteredMenu = False;
1163 	WMSize oldSize = item->view->size;
1164 	WEditMenuItem *dritem = item;
1165 	WEditMenu *dmenu = NULL;
1166 
1167 	if (item->flags.isTitle) {
1168 		WMRaiseWidget(menu);
1169 
1170 		dragMenu(menu);
1171 
1172 		return;
1173 	}
1174 
1175 	selectItem(menu, NULL);
1176 
1177 	win = scr->rootWin;
1178 
1179 	XTranslateCoordinates(dpy, W_VIEW_DRAWABLE(item->view), win, 0, 0, &orix, &oriy, &blaw);
1180 
1181 	dview = W_CreateUnmanagedTopView(scr);
1182 	W_SetViewBackgroundColor(dview, scr->black);
1183 	W_ResizeView(dview, W_VIEW_WIDTH(item->view), W_VIEW_HEIGHT(item->view));
1184 	W_MoveView(dview, orix, oriy);
1185 	W_RealizeView(dview);
1186 
1187 	if (menu->flags.isFactory || copy) {
1188 		dritem = duplicateItem(item);
1189 
1190 		W_ReparentView(dritem->view, dview, 0, 0);
1191 		WMResizeWidget(dritem, oldSize.width, oldSize.height);
1192 		WMRealizeWidget(dritem);
1193 		WMMapWidget(dritem);
1194 	} else {
1195 		W_ReparentView(item->view, dview, 0, 0);
1196 	}
1197 
1198 	W_MapView(dview);
1199 
1200 	dx = menu->dragX - orix;
1201 	dy = menu->dragY - oriy;
1202 
1203 	XGrabPointer(dpy, scr->rootWin, False,
1204 		     ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
1205 		     GrabModeAsync, GrabModeAsync, None, scr->defaultCursor, CurrentTime);
1206 
1207 	if (menu->flags.acceptsDrop)
1208 		XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
1209 
1210 	while (!done) {
1211 		XEvent ev;
1212 
1213 		WMNextEvent(dpy, &ev);
1214 
1215 		switch (ev.type) {
1216 		case MotionNotify:
1217 			while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev)) ;
1218 
1219 			XQueryPointer(dpy, win, &blaw, &blaw, &blai, &blai, &x, &y, &blau);
1220 
1221 			dmenu = findMenuInWindow(dpy, win, x, y);
1222 
1223 			if (dmenu) {
1224 				handleDragOver(dmenu, dview, dritem, y - dy);
1225 				if (!enteredMenu) {
1226 					enteredMenu = True;
1227 					XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
1228 				}
1229 			} else {
1230 				if (enteredMenu) {
1231 					W_ResizeView(dview, oldSize.width, oldSize.height);
1232 					W_ResizeView(dritem->view, oldSize.width, oldSize.height);
1233 					enteredMenu = False;
1234 					XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
1235 				}
1236 				W_MoveView(dview, x - dx, y - dy);
1237 			}
1238 
1239 			break;
1240 
1241 		case ButtonRelease:
1242 			done = True;
1243 			break;
1244 
1245 		default:
1246 			WMHandleEvent(&ev);
1247 			break;
1248 		}
1249 	}
1250 	XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
1251 
1252 	XUngrabPointer(dpy, CurrentTime);
1253 
1254 	if (!enteredMenu) {
1255 		Bool rem = True;
1256 
1257 		if (!menu->flags.isFactory && !copy) {
1258 			W_UnmapView(dview);
1259 			if (menu->delegate && menu->delegate->shouldRemoveItem) {
1260 				rem = (*menu->delegate->shouldRemoveItem) (menu->delegate, menu, item);
1261 			}
1262 			W_MapView(dview);
1263 		}
1264 
1265 		if (!rem || menu->flags.isFactory || copy) {
1266 			slideWindow(dpy, W_VIEW_DRAWABLE(dview), x - dx, y - dy, orix, oriy);
1267 
1268 			if (!menu->flags.isFactory && !copy) {
1269 				WRemoveEditMenuItem(menu, dritem);
1270 				handleItemDrop(dmenu ? dmenu : menu, dritem, oriy);
1271 			}
1272 		} else {
1273 			WRemoveEditMenuItem(menu, dritem);
1274 		}
1275 	} else {
1276 		WRemoveEditMenuItem(menu, dritem);
1277 
1278 		if (menu->delegate && menu->delegate->itemCloned && (menu->flags.isFactory || copy)) {
1279 			(*menu->delegate->itemCloned) (menu->delegate, menu, item, dritem);
1280 		}
1281 
1282 		handleItemDrop(dmenu, dritem, y - dy);
1283 
1284 		if (item->submenu && (menu->flags.isFactory || copy)) {
1285 			WEditMenu *submenu;
1286 
1287 			submenu = duplicateMenu(item->submenu);
1288 			WSetEditMenuSubmenu(dmenu, dritem, submenu);
1289 		}
1290 	}
1291 
1292 	/* can't destroy now because we're being called from
1293 	 * the event handler of the item. and destroying now,
1294 	 * would mean destroying the item too in some cases.
1295 	 */
1296 	WMAddIdleHandler((WMCallback *) W_DestroyView, dview);
1297 }
1298 
handleItemClick(XEvent * event,void * data)1299 static void handleItemClick(XEvent * event, void *data)
1300 {
1301 	WEditMenuItem *item = (WEditMenuItem *) data;
1302 	WEditMenu *menu = item->parent;
1303 
1304 	switch (event->type) {
1305 	case ButtonPress:
1306 		selectItem(menu, item);
1307 
1308 		if (WMIsDoubleClick(event)) {
1309 			editItemLabel(item);
1310 		}
1311 
1312 		menu->flags.isDragging = 1;
1313 		menu->dragX = event->xbutton.x_root;
1314 		menu->dragY = event->xbutton.y_root;
1315 		break;
1316 
1317 	case ButtonRelease:
1318 		menu->flags.isDragging = 0;
1319 		break;
1320 
1321 	case MotionNotify:
1322 		if (menu->flags.isDragging) {
1323 			if (abs(event->xbutton.x_root - menu->dragX) > 5
1324 			    || abs(event->xbutton.y_root - menu->dragY) > 5) {
1325 				menu->flags.isDragging = 0;
1326 				dragItem(menu, item, event->xbutton.state & ControlMask);
1327 			}
1328 		}
1329 		break;
1330 	}
1331 }
1332 
destroyEditMenu(WEditMenu * mPtr)1333 static void destroyEditMenu(WEditMenu * mPtr)
1334 {
1335 	WMRemoveNotificationObserver(mPtr);
1336 
1337 	WMFreeArray(mPtr->items);
1338 
1339 	wfree(mPtr->tdelegate);
1340 
1341 	wfree(mPtr);
1342 }
1343