updated editable menu widget
[wmaker-crm.git] / WPrefs.app / editmenu.c
bloba73437b288bb9235f6c983678f7c182dfd8ce0c5
1 /* editmenu.c - editable menus
2 *
3 * WPrefs - Window Maker Preferences Program
4 *
5 * Copyright (c) 2000 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.
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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
24 #include <WINGsP.h>
25 #include <WUtil.h>
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <ctype.h>
30 #include "editmenu.h"
32 typedef struct W_EditMenuItem {
33 W_Class widgetClass;
34 WMView *view;
36 struct W_EditMenu *parent;
38 char *label;
40 void *data;
41 WMCallback *destroyData;
43 struct W_EditMenu *submenu; /* if it's a cascade, NULL otherwise */
45 struct {
46 unsigned isTitle:1;
47 unsigned isHighlighted:1;
48 } flags;
49 } EditMenuItem;
52 typedef struct W_EditMenu {
53 W_Class widgetClass;
54 WMView *view;
56 struct W_EditMenu *parent;
58 WMBag *items; /* EditMenuItem */
60 EditMenuItem *selectedItem;
62 WMTextField *tfield;
64 int titleHeight;
65 int itemHeight;
67 WEditMenuDelegate *delegate;
69 /* item dragging */
70 int draggedItem;
71 int dragX, dragY;
73 struct W_EditMenu **dropTargets;
75 /* only for non-standalone menu */
76 WMSize maxSize;
77 WMSize minSize;
79 struct {
80 unsigned standalone:1;
81 unsigned isTitled:1;
83 unsigned isFactory:1;
84 unsigned isSelectable:1;
85 unsigned isEditable:1;
87 unsigned isTornOff:1;
89 unsigned isDragging:1;
90 unsigned isEditing:1;
91 } flags;
92 } EditMenu;
96 /******************** WEditMenuItem ********************/
98 static void destroyEditMenuItem(WEditMenuItem *iPtr);
99 static void paintEditMenuItem(WEditMenuItem *iPtr);
100 static void handleItemEvents(XEvent *event, void *data);
102 static void handleItemClick(XEvent *event, void *data);
105 static W_Class EditMenuItemClass = 0;
108 W_Class
109 InitEditMenuItem(WMScreen *scr)
111 /* register our widget with WINGs and get our widget class ID */
112 if (!EditMenuItemClass) {
113 EditMenuItemClass = W_RegisterUserWidget();
116 return EditMenuItemClass;
120 WEditMenuItem*
121 WCreateEditMenuItem(WMWidget *parent, char *title, Bool isTitle)
123 WEditMenuItem *iPtr;
124 WMScreen *scr = WMWidgetScreen(parent);
126 if (!EditMenuItemClass)
127 InitEditMenuItem(scr);
130 iPtr = wmalloc(sizeof(WEditMenuItem));
132 memset(iPtr, 0, sizeof(WEditMenuItem));
134 iPtr->widgetClass = EditMenuItemClass;
136 iPtr->view = W_CreateView(W_VIEW(parent));
137 if (!iPtr->view) {
138 free(iPtr);
139 return NULL;
141 iPtr->view->self = iPtr;
143 iPtr->parent = parent;
145 iPtr->label = wstrdup(title);
147 iPtr->flags.isTitle = isTitle;
149 if (isTitle) {
150 WMSetWidgetBackgroundColor(iPtr, WMBlackColor(scr));
153 WMCreateEventHandler(iPtr->view, ExposureMask|StructureNotifyMask,
154 handleItemEvents, iPtr);
156 WMCreateEventHandler(iPtr->view, ButtonPressMask|ButtonReleaseMask
157 |ButtonMotionMask, handleItemClick, iPtr);
160 return iPtr;
164 void *WGetEditMenuItemData(WEditMenuItem *item)
166 return item->data;
170 void WSetEditMenuItemData(WEditMenuItem *item, void *data,
171 WMCallback *destroyer)
173 item->data = data;
174 item->destroyData = destroyer;
180 static void
181 paintEditMenuItem(WEditMenuItem *iPtr)
183 WMScreen *scr = WMWidgetScreen(iPtr);
184 WMColor *color;
185 Window win = W_VIEW(iPtr)->window;
186 int w = W_VIEW(iPtr)->size.width;
187 int h = W_VIEW(iPtr)->size.height;
188 WMFont *font = (iPtr->flags.isTitle ? scr->boldFont : scr->normalFont);
190 if (!iPtr->view->flags.realized)
191 return;
193 color = scr->black;
194 if (iPtr->flags.isTitle && !iPtr->flags.isHighlighted) {
195 color = scr->white;
199 XClearWindow(scr->display, win);
201 W_DrawRelief(scr, win, 0, 0, w+1, h, WRRaised);
203 WMDrawString(scr, win, WMColorGC(color), font, 5, 3, iPtr->label,
204 strlen(iPtr->label));
206 if (iPtr->submenu) {
207 /* draw the cascade indicator */
208 XDrawLine(scr->display,win,WMColorGC(scr->darkGray),
209 w-11, 6, w-6, h/2-1);
210 XDrawLine(scr->display,win,WMColorGC(scr->white),
211 w-11, h-8, w-6, h/2-1);
212 XDrawLine(scr->display,win,WMColorGC(scr->black),
213 w-12, 6, w-12, h-8);
218 static void
219 highlightItem(WEditMenuItem *iPtr, Bool high)
221 if (iPtr->flags.isTitle)
222 return;
224 iPtr->flags.isHighlighted = high;
226 if (high) {
227 WMSetWidgetBackgroundColor(iPtr, WMWhiteColor(WMWidgetScreen(iPtr)));
228 } else {
229 if (!iPtr->flags.isTitle) {
230 WMSetWidgetBackgroundColor(iPtr,
231 WMGrayColor(WMWidgetScreen(iPtr)));
232 } else {
233 WMSetWidgetBackgroundColor(iPtr,
234 WMBlackColor(WMWidgetScreen(iPtr)));
240 static int
241 getItemTextWidth(WEditMenuItem *iPtr)
243 WMScreen *scr = WMWidgetScreen(iPtr);
245 if (iPtr->flags.isTitle) {
246 return WMWidthOfString(scr->boldFont, iPtr->label,
247 strlen(iPtr->label));
248 } else {
249 return WMWidthOfString(scr->normalFont, iPtr->label,
250 strlen(iPtr->label));
256 static void
257 handleItemEvents(XEvent *event, void *data)
259 WEditMenuItem *iPtr = (WEditMenuItem*)data;
261 switch (event->type) {
262 case Expose:
263 if (event->xexpose.count!=0)
264 break;
265 paintEditMenuItem(iPtr);
266 break;
268 case DestroyNotify:
269 destroyEditMenuItem(iPtr);
270 break;
275 static void
276 destroyEditMenuItem(WEditMenuItem *iPtr)
278 if (iPtr->label)
279 free(iPtr->label);
280 if (iPtr->data && iPtr->destroyData)
281 (*iPtr->destroyData)(iPtr->data);
283 free(iPtr);
288 /******************** WEditMenu *******************/
290 static void destroyEditMenu(WEditMenu *mPtr);
292 static void updateMenuContents(WEditMenu *mPtr);
294 static void handleEvents(XEvent *event, void *data);
295 static void handleActionEvents(XEvent *event, void *data);
297 static void editItemLabel(WEditMenuItem *item);
298 static void stopEditItem(WEditMenu *menu, Bool apply);
301 static W_Class EditMenuClass = 0;
304 W_Class
305 InitEditMenu(WMScreen *scr)
307 /* register our widget with WINGs and get our widget class ID */
308 if (!EditMenuClass) {
310 EditMenuClass = W_RegisterUserWidget();
313 return EditMenuClass;
318 typedef struct {
319 int flags;
320 int window_style;
321 int window_level;
322 int reserved;
323 Pixmap miniaturize_pixmap; /* pixmap for miniaturize button */
324 Pixmap close_pixmap; /* pixmap for close button */
325 Pixmap miniaturize_mask; /* miniaturize pixmap mask */
326 Pixmap close_mask; /* close pixmap mask */
327 int extra_flags;
328 } GNUstepWMAttributes;
331 #define GSWindowStyleAttr (1<<0)
332 #define GSWindowLevelAttr (1<<1)
335 static void
336 writeGNUstepWMAttr(WMScreen *scr, Window window, GNUstepWMAttributes *attr)
338 unsigned long data[9];
340 /* handle idiot compilers where array of CARD32 != struct of CARD32 */
341 data[0] = attr->flags;
342 data[1] = attr->window_style;
343 data[2] = attr->window_level;
344 data[3] = 0; /* reserved */
345 /* The X protocol says XIDs are 32bit */
346 data[4] = attr->miniaturize_pixmap;
347 data[5] = attr->close_pixmap;
348 data[6] = attr->miniaturize_mask;
349 data[7] = attr->close_mask;
350 data[8] = attr->extra_flags;
351 XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
352 32, PropModeReplace, (unsigned char *)data, 9);
356 static void
357 realizeObserver(void *self, WMNotification *not)
359 WEditMenu *menu = (WEditMenu*)self;
360 GNUstepWMAttributes attribs;
362 memset(&attribs, 0, sizeof(GNUstepWMAttributes));
363 attribs.flags = GSWindowStyleAttr|GSWindowLevelAttr;
364 attribs.window_style = WMBorderlessWindowMask;
365 attribs.window_level = WMSubmenuWindowLevel;
367 writeGNUstepWMAttr(WMWidgetScreen(menu), menu->view->window, &attribs);
371 static WEditMenu*
372 makeEditMenu(WMScreen *scr, WMWidget *parent, char *title)
374 WEditMenu *mPtr;
375 WEditMenuItem *item;
377 if (!EditMenuClass)
378 InitEditMenu(scr);
381 mPtr = wmalloc(sizeof(WEditMenu));
382 memset(mPtr, 0, sizeof(WEditMenu));
384 mPtr->widgetClass = EditMenuClass;
386 if (parent) {
387 mPtr->view = W_CreateView(W_VIEW(parent));
388 mPtr->flags.standalone = 0;
389 } else {
390 mPtr->view = W_CreateTopView(scr);
391 mPtr->flags.standalone = 1;
393 if (!mPtr->view) {
394 free(mPtr);
395 return NULL;
397 mPtr->view->self = mPtr;
399 mPtr->flags.isSelectable = 1;
400 mPtr->flags.isEditable = 1;
402 W_SetViewBackgroundColor(mPtr->view, scr->darkGray);
404 WMAddNotificationObserver(realizeObserver, mPtr,
405 WMViewRealizedNotification, mPtr->view);
407 mPtr->items = WMCreateBag(4);
409 WMCreateEventHandler(mPtr->view, ExposureMask|StructureNotifyMask,
410 handleEvents, mPtr);
412 WMCreateEventHandler(mPtr->view, ButtonPressMask,handleActionEvents, mPtr);
415 if (title != NULL) {
416 item = WCreateEditMenuItem(mPtr, title, True);
418 WMMapWidget(item);
419 WMPutInBag(mPtr->items, item);
421 mPtr->flags.isTitled = 1;
424 mPtr->itemHeight = WMFontHeight(scr->normalFont) + 6;
425 mPtr->titleHeight = WMFontHeight(scr->boldFont) + 8;
427 updateMenuContents(mPtr);
429 return mPtr;
433 WEditMenu*
434 WCreateEditMenu(WMScreen *scr, char *title)
436 return makeEditMenu(scr, NULL, title);
440 WEditMenu*
441 WCreateEditMenuPad(WMWidget *parent)
443 return makeEditMenu(WMWidgetScreen(parent), parent, NULL);
447 void
448 WSetEditMenuDelegate(WEditMenu *mPtr, WEditMenuDelegate *delegate)
450 mPtr->delegate = delegate;
454 WEditMenuItem*
455 WInsertMenuItemWithTitle(WEditMenu *mPtr, int index, char *title)
457 WEditMenuItem *item;
459 item = WCreateEditMenuItem(mPtr, title, False);
461 WMMapWidget(item);
463 if (index >= WMGetBagItemCount(mPtr->items))
464 WMPutInBag(mPtr->items, item);
465 else {
466 if (index < 0)
467 index = 0;
468 if (mPtr->flags.isTitled)
469 index++;
470 WMInsertInBag(mPtr->items, index, item);
473 updateMenuContents(mPtr);
475 return item;
480 WEditMenuItem*
481 WAddMenuItemWithTitle(WEditMenu *mPtr, char *title)
483 return WInsertMenuItemWithTitle(mPtr, WMGetBagItemCount(mPtr->items),
484 title);
488 void
489 WSetEditMenuItemDropTargets(WEditMenu *mPtr, WEditMenu **dropTargets,
490 int count)
492 if (mPtr->dropTargets)
493 free(mPtr->dropTargets);
495 mPtr->dropTargets = wmalloc(sizeof(WEditMenu*)*(count+1));
496 memcpy(mPtr->dropTargets, dropTargets, sizeof(WEditMenu*)*count);
497 mPtr->dropTargets[count] = NULL;
501 void
502 WSetEditMenuSubmenu(WEditMenu *mPtr, WEditMenuItem *item, WEditMenu *submenu)
504 item->submenu = submenu;
505 submenu->parent = mPtr;
507 paintEditMenuItem(item);
510 static int simpleMatch(void *a, void *b)
512 if (a == b)
513 return 1;
514 else
515 return 0;
519 void
520 WRemoveEditMenuItem(WEditMenu *mPtr, WEditMenuItem *item)
522 int index;
524 index = WMFindInBag(mPtr->items, simpleMatch, item);
526 if (index == WBNotFound) {
527 return;
530 WMDeleteFromBag(mPtr->items, index);
532 updateMenuContents(mPtr);
536 void
537 WSetEditMenuSelectable(WEditMenu *mPtr, Bool flag)
539 mPtr->flags.isSelectable = flag;
543 void
544 WSetEditMenuEditable(WEditMenu *mPtr, Bool flag)
546 mPtr->flags.isEditable = flag;
550 void
551 WSetEditMenuIsFactory(WEditMenu *mPtr, Bool flag)
553 mPtr->flags.isFactory = flag;
557 void
558 WSetEditMenuMinSize(WEditMenu *mPtr, WMSize size)
560 mPtr->minSize.width = size.width;
561 mPtr->minSize.height = size.height;
565 void
566 WSetEditMenuMaxSize(WEditMenu *mPtr, WMSize size)
568 mPtr->maxSize.width = size.width;
569 mPtr->maxSize.height = size.height;
573 WMPoint
574 WGetEditMenuLocationForSubmenu(WEditMenu *mPtr, WEditMenu *submenu)
576 WMBagIterator iter;
577 WEditMenuItem *item;
578 int y;
580 if (mPtr->flags.isTitled) {
581 y = mPtr->titleHeight;
582 } else {
583 y = 0;
585 WM_ITERATE_BAG(mPtr->items, item, iter) {
586 if (item->submenu == submenu) {
587 return wmkpoint(mPtr->view->pos.x + mPtr->view->size.width, y);
589 y += mPtr->itemHeight;
592 puts("invalid submenu passed to WGetEditMenuLocationForSubmenu()");
594 return wmkpoint(0,0);
598 Bool
599 WEditMenuIsTornOff(WEditMenu *mPtr)
601 return !mPtr->flags.isTornOff;
605 static void
606 updateMenuContents(WEditMenu *mPtr)
608 int newW, newH;
609 int w;
610 int i;
611 int iheight = mPtr->itemHeight;
612 int offs = 1;
613 WMBagIterator iter;
614 WEditMenuItem *item;
616 newW = 0;
617 newH = offs;
619 i = 0;
620 WM_ITERATE_BAG(mPtr->items, item, iter) {
621 w = getItemTextWidth(item);
623 newW = WMAX(w, newW);
625 WMMoveWidget(item, offs, newH);
626 if (i == 0 && mPtr->flags.isTitled) {
627 newH += mPtr->titleHeight;
628 } else {
629 newH += iheight;
631 i = 1;
634 newW += iheight + 5;
635 newH--;
637 if (mPtr->minSize.width)
638 newW = WMAX(newW, mPtr->minSize.width);
639 if (mPtr->maxSize.width)
640 newW = WMIN(newW, mPtr->maxSize.width);
642 if (mPtr->minSize.height)
643 newH = WMAX(newH, mPtr->minSize.height);
644 if (mPtr->maxSize.height)
645 newH = WMIN(newH, mPtr->maxSize.height);
647 W_ResizeView(mPtr->view, newW, newH+1);
649 newW -= 2*offs;
651 i = 0;
652 WM_ITERATE_BAG(mPtr->items, item, iter) {
653 if (i == 0 && mPtr->flags.isTitled) {
654 WMResizeWidget(item, newW, mPtr->titleHeight);
655 } else {
656 WMResizeWidget(item, newW, iheight);
658 i = 1;
663 static void
664 selectItem(WEditMenu *menu, WEditMenuItem *item)
666 if (!menu->flags.isSelectable) {
667 return;
670 if (menu->selectedItem) {
671 highlightItem(menu->selectedItem, False);
673 if (menu->delegate) {
674 (*menu->delegate->itemDeselected)(menu->delegate, menu,
675 menu->selectedItem);
679 if (menu->flags.isEditing) {
680 stopEditItem(menu, False);
683 if (item) {
684 highlightItem(item, True);
686 if (item->submenu) {
687 WMPoint pt;
689 pt = WGetEditMenuLocationForSubmenu(menu, item->submenu);
690 WMMoveWidget(item->submenu, pt.x, pt.y);
691 WMMapWidget(item->submenu);
694 if (menu->delegate) {
695 (*menu->delegate->itemSelected)(menu->delegate, menu, item);
699 menu->selectedItem = item;
703 static void
704 paintMenu(WEditMenu *mPtr)
706 W_View *view = mPtr->view;
708 W_DrawRelief(W_VIEW_SCREEN(view), W_VIEW_DRAWABLE(view), 0, 0,
709 W_VIEW_WIDTH(view), W_VIEW_HEIGHT(view), WRSimple);
713 static void
714 handleEvents(XEvent *event, void *data)
716 WEditMenu *mPtr = (WEditMenu*)data;
718 switch (event->type) {
719 case DestroyNotify:
720 destroyEditMenu(mPtr);
721 break;
723 case Expose:
724 if (event->xexpose.count == 0)
725 paintMenu(mPtr);
726 break;
733 static void
734 handleActionEvents(XEvent *event, void *data)
736 // WEditMenu *mPtr = (WEditMenu*)data;
738 switch (event->type) {
739 case ButtonPress:
740 break;
745 /* -------------------------- Menu Label Editing ------------------------ */
748 static void
749 stopEditItem(WEditMenu *menu, Bool apply)
751 char *text;
753 if (apply) {
754 text = WMGetTextFieldText(menu->tfield);
756 free(menu->selectedItem->label);
757 menu->selectedItem->label = wstrdup(text);
759 updateMenuContents(menu);
761 WMUnmapWidget(menu->tfield);
762 menu->flags.isEditing = 0;
766 static void
767 textEndedEditing(struct WMTextFieldDelegate *self, WMNotification *notif)
769 WEditMenu *menu = (WEditMenu*)self->data;
770 int reason;
771 int i;
772 WEditMenuItem *item;
774 if (!menu->flags.isEditing)
775 return;
777 reason = (int)WMGetNotificationClientData(notif);
779 switch (reason) {
780 case WMEscapeTextMovement:
781 stopEditItem(menu, False);
782 break;
784 case WMReturnTextMovement:
785 stopEditItem(menu, True);
786 break;
788 case WMTabTextMovement:
789 stopEditItem(menu, True);
791 i = WMFindInBag(menu->items, simpleMatch, menu->selectedItem);
792 item = WMGetFromBag(menu->items, i+1);
793 if (item != NULL) {
794 selectItem(menu, item);
795 editItemLabel(item);
797 break;
799 case WMBacktabTextMovement:
800 stopEditItem(menu, True);
802 i = WMFindInBag(menu->items, simpleMatch, menu->selectedItem);
803 item = WMGetFromBag(menu->items, i-1);
804 if (item != NULL) {
805 selectItem(menu, item);
806 editItemLabel(item);
808 break;
814 static WMTextFieldDelegate textFieldDelegate = {
815 NULL,
816 NULL,
817 NULL,
818 textEndedEditing,
819 NULL,
820 NULL
824 static void
825 editItemLabel(WEditMenuItem *item)
827 WEditMenu *mPtr = item->parent;
828 WMTextField *tf;
829 int i;
831 if (!mPtr->flags.isEditable) {
832 return;
835 if (!mPtr->tfield) {
836 mPtr->tfield = WMCreateTextField(mPtr);
837 WMSetTextFieldBeveled(mPtr->tfield, False);
838 WMRealizeWidget(mPtr->tfield);
840 textFieldDelegate.data = mPtr;
842 WMSetTextFieldDelegate(mPtr->tfield, &textFieldDelegate);
844 tf = mPtr->tfield;
846 i = WMFindInBag(mPtr->items, simpleMatch, item);
848 WMSetTextFieldText(tf, item->label);
849 WMSelectTextFieldRange(tf, wmkrange(0, strlen(item->label)));
850 WMResizeWidget(tf, mPtr->view->size.width, mPtr->itemHeight);
851 WMMoveWidget(tf, 0, item->view->pos.y);
852 WMMapWidget(tf);
853 WMSetFocusToWidget(tf);
855 mPtr->flags.isEditing = 1;
860 /* -------------------------------------------------- */
863 static void
864 slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
866 double x, y, dx, dy;
867 int i;
868 int iterations;
870 iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY)));
872 x = srcX;
873 y = srcY;
875 dx = (double)(dstX-srcX)/iterations;
876 dy = (double)(dstY-srcY)/iterations;
878 for (i = 0; i <= iterations; i++) {
879 XMoveWindow(dpy, win, x, y);
880 XFlush(dpy);
882 wusleep(800);
884 x += dx;
885 y += dy;
890 static WEditMenu*
891 getEditMenuForWindow(WEditMenu **menus, Window window)
893 while (*menus) {
894 if (W_VIEW_DRAWABLE((*menus)->view) == window) {
895 return *menus;
897 menus++;
900 return NULL;
904 static WEditMenu*
905 findMenuInWindow(Display *dpy, Window toplevel, int x, int y,
906 WEditMenu **menus)
908 Window foo, bar;
909 Window *children;
910 unsigned nchildren;
911 int i;
912 WEditMenu *menu;
914 menu = getEditMenuForWindow(menus, toplevel);
915 if (menu)
916 return menu;
918 if (!XQueryTree(dpy, toplevel, &foo, &bar,
919 &children, &nchildren) || children == NULL) {
920 return NULL;
923 /* first window that contains the point is the one */
924 for (i = nchildren-1; i >= 0; i--) {
925 XWindowAttributes attr;
927 if (XGetWindowAttributes(dpy, children[i], &attr)
928 && attr.map_state == IsViewable
929 && x >= attr.x && y >= attr.y
930 && x < attr.x + attr.width && y < attr.y + attr.height) {
931 Window tmp;
933 tmp = children[i];
935 menu = findMenuInWindow(dpy, tmp, x - attr.x, y - attr.y, menus);
936 if (menu) {
937 XFree(children);
938 return menu;
943 XFree(children);
944 return NULL;
948 static void
949 handleDragOver(WEditMenu *menu, WMView *view, WEditMenuItem *item,
950 int x, int y)
952 WMScreen *scr = W_VIEW_SCREEN(menu->view);
953 Window blaw;
954 int mx, my;
955 int offs;
957 XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
958 scr->rootWin, 0, 0, &mx, &my, &blaw);
960 offs = menu->flags.standalone ? 0 : 1;
962 W_MoveView(view, mx + offs, y);
963 if (view->size.width != menu->view->size.width) {
964 W_ResizeView(view, menu->view->size.width - 2*offs,
965 menu->itemHeight);
966 W_ResizeView(item->view, menu->view->size.width - 2*offs,
967 menu->itemHeight);
972 static void
973 handleItemDrop(WEditMenu *menu, WEditMenuItem *item, int x, int y)
975 WMScreen *scr = W_VIEW_SCREEN(menu->view);
976 Window blaw;
977 int mx, my;
978 int index;
980 XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
981 scr->rootWin, 0, 0, &mx, &my, &blaw);
983 index = y - my;
984 if (menu->flags.isTitled) {
985 index -= menu->titleHeight;
987 index = (index + menu->itemHeight/2) / menu->itemHeight;
988 if (index < 0)
989 index = 0;
991 if (menu->flags.isTitled) {
992 index++;
995 if (index > WMGetBagItemCount(menu->items)) {
996 WMPutInBag(menu->items, item);
997 } else {
998 WMInsertInBag(menu->items, index, item);
1001 W_ReparentView(item->view, menu->view, 0, index*menu->itemHeight);
1003 item->parent = menu;
1005 updateMenuContents(menu);
1009 static void
1010 dragMenu(WEditMenu *menu)
1012 WMScreen *scr = W_VIEW_SCREEN(menu->view);
1013 XEvent ev;
1014 Bool done = False;
1015 int dx, dy;
1016 unsigned blau;
1017 Window blaw;
1019 XGetGeometry(scr->display, W_VIEW_DRAWABLE(menu->view), &blaw, &dx, &dy,
1020 &blau, &blau, &blau, &blau);
1021 XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
1022 scr->rootWin, dx, dy, &dx, &dy, &blaw);
1023 dx = menu->dragX - dx;
1024 dy = menu->dragY - dy;
1026 XGrabPointer(scr->display, scr->rootWin, False,
1027 ButtonReleaseMask|ButtonMotionMask,
1028 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
1029 CurrentTime);
1031 while (!done) {
1032 WMNextEvent(scr->display, &ev);
1034 switch (ev.type) {
1035 case ButtonRelease:
1036 done = True;
1037 break;
1039 case MotionNotify:
1040 WMMoveWidget(menu, ev.xmotion.x_root - dx,
1041 ev.xmotion.y_root - dy);
1042 break;
1044 default:
1045 WMHandleEvent(&ev);
1046 break;
1050 XUngrabPointer(scr->display, CurrentTime);
1055 static void
1056 dragItem(WEditMenu *menu, WEditMenuItem *item)
1058 Display *dpy = W_VIEW_DISPLAY(menu->view);
1059 WMScreen *scr = W_VIEW_SCREEN(menu->view);
1060 int x, y;
1061 int dx, dy;
1062 Bool done = False;
1063 Window blaw;
1064 int blai;
1065 unsigned blau;
1066 Window win;
1067 WMView *dview;
1068 int orix, oriy;
1069 Bool enteredMenu = False;
1070 WMSize oldSize = item->view->size;
1071 WEditMenu *dmenu = NULL;
1074 if (item->flags.isTitle) {
1075 WMRaiseWidget(menu);
1077 dragMenu(menu);
1079 return;
1083 selectItem(menu, NULL);
1085 assert(menu->dropTargets != NULL);
1088 win = scr->rootWin;
1090 XTranslateCoordinates(dpy, W_VIEW_DRAWABLE(item->view), win,
1091 0, 0, &orix, &oriy, &blaw);
1093 dview = W_CreateUnmanagedTopView(scr);
1094 W_SetViewBackgroundColor(dview, scr->black);
1095 W_ResizeView(dview, W_VIEW_WIDTH(item->view), W_VIEW_HEIGHT(item->view));
1096 W_MoveView(dview, orix, oriy);
1097 W_RealizeView(dview);
1099 if (menu->flags.isFactory) {
1100 WEditMenuItem *nitem;
1102 nitem = WCreateEditMenuItem(menu, item->label, False);
1104 if (menu->delegate) {
1105 (*menu->delegate->itemCloned)(menu->delegate, menu, item, nitem);
1107 item = nitem;
1109 W_ReparentView(item->view, dview, 0, 0);
1110 WMResizeWidget(item, oldSize.width, oldSize.height);
1111 WMRealizeWidget(item);
1112 WMMapWidget(item);
1113 } else {
1114 W_ReparentView(item->view, dview, 0, 0);
1117 W_MapView(dview);
1119 dx = menu->dragX - orix;
1120 dy = menu->dragY - oriy;
1122 XGrabPointer(dpy, scr->rootWin, False,
1123 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
1124 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
1125 CurrentTime);
1127 while (!done) {
1128 XEvent ev;
1130 WMNextEvent(dpy, &ev);
1132 switch (ev.type) {
1133 case MotionNotify:
1134 XQueryPointer(dpy, win, &blaw, &blaw, &blai, &blai, &x, &y, &blau);
1136 dmenu = findMenuInWindow(dpy, win, x, y, menu->dropTargets);
1138 if (dmenu) {
1139 handleDragOver(dmenu, dview, item, x - dx, y - dy);
1140 enteredMenu = True;
1141 } else {
1142 if (enteredMenu) {
1143 W_ResizeView(dview, oldSize.width, oldSize.height);
1144 W_ResizeView(item->view, oldSize.width, oldSize.height);
1145 enteredMenu = False;
1147 W_MoveView(dview, x - dx, y - dy);
1150 break;
1152 case ButtonRelease:
1153 done = True;
1154 break;
1156 default:
1157 WMHandleEvent(&ev);
1158 break;
1161 XUngrabPointer(dpy, CurrentTime);
1163 if (menu->flags.isFactory && !enteredMenu) {
1164 slideWindow(dpy, W_VIEW_DRAWABLE(dview), x-dx, y-dy, orix, oriy);
1165 } else {
1166 WRemoveEditMenuItem(menu, item);
1167 if (enteredMenu) {
1168 handleItemDrop(dmenu, item, x-dy, y-dy);
1172 /* can't destroy now because we're being called from
1173 * the event handler of the item. and destroying now,
1174 * would mean destroying the item too in some cases.
1176 WMAddIdleHandler((WMCallback*)W_DestroyView, dview);
1181 static void
1182 handleItemClick(XEvent *event, void *data)
1184 WEditMenuItem *item = (WEditMenuItem*)data;
1185 WEditMenu *menu = item->parent;
1187 switch (event->type) {
1188 case ButtonPress:
1189 selectItem(menu, item);
1191 if (WMIsDoubleClick(event)) {
1192 editItemLabel(item);
1195 menu->flags.isDragging = 1;
1196 menu->dragX = event->xbutton.x_root;
1197 menu->dragY = event->xbutton.y_root;
1198 break;
1200 case ButtonRelease:
1201 menu->flags.isDragging = 0;
1202 break;
1204 case MotionNotify:
1205 if (menu->flags.isDragging) {
1206 if (abs(event->xbutton.x_root - menu->dragX) > 5
1207 || abs(event->xbutton.y_root - menu->dragY) > 5) {
1208 menu->flags.isDragging = 0;
1209 dragItem(menu, item);
1212 break;
1217 static void
1218 destroyEditMenu(WEditMenu *mPtr)
1220 WMRemoveNotificationObserver(mPtr);
1222 WMFreeBag(mPtr->items);
1224 free(mPtr->dropTargets);
1226 free(mPtr);