- Made all changes for moving WINGs headers in the WINGs subdirectory.
[wmaker-crm.git] / WPrefs.app / editmenu.c
blob79944429cf1c5de686421d776be029b8a6bf0b76
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 <WINGs/WINGsP.h>
25 #include <WINGs/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;
39 WMPixmap *pixmap; /* pixmap to show at left */
41 void *data;
42 WMCallback *destroyData;
44 struct W_EditMenu *submenu; /* if it's a cascade, NULL otherwise */
46 struct {
47 unsigned isTitle:1;
48 unsigned isHighlighted:1;
49 } flags;
50 } EditMenuItem;
53 typedef struct W_EditMenu {
54 W_Class widgetClass;
55 WMView *view;
57 struct W_EditMenu *parent;
59 WMBag *items; /* EditMenuItem */
61 EditMenuItem *selectedItem;
63 WMTextField *tfield;
65 WMButton *closeB;
67 int titleHeight;
68 int itemHeight;
70 WEditMenuDelegate *delegate;
72 WMTextFieldDelegate *tdelegate;
74 /* item dragging */
75 int draggedItem;
76 int dragX, dragY;
78 /* only for non-standalone menu */
79 WMSize maxSize;
80 WMSize minSize;
82 struct {
83 unsigned standalone:1;
84 unsigned isTitled:1;
86 unsigned acceptsDrop:1;
87 unsigned isFactory:1;
88 unsigned isSelectable:1;
89 unsigned isEditable:1;
91 unsigned isTornOff:1;
93 unsigned isDragging:1;
94 unsigned isEditing:1;
96 unsigned wasMapped:1;
97 } flags;
98 } EditMenu;
102 /******************** WEditMenuItem ********************/
104 static void destroyEditMenuItem(WEditMenuItem *iPtr);
105 static void paintEditMenuItem(WEditMenuItem *iPtr);
106 static void handleItemEvents(XEvent *event, void *data);
108 static void handleItemClick(XEvent *event, void *data);
111 static W_Class EditMenuItemClass = 0;
114 W_Class
115 InitEditMenuItem(WMScreen *scr)
117 /* register our widget with WINGs and get our widget class ID */
118 if (!EditMenuItemClass) {
119 EditMenuItemClass = W_RegisterUserWidget();
122 return EditMenuItemClass;
126 WEditMenuItem*
127 WCreateEditMenuItem(WMWidget *parent, char *title, Bool isTitle)
129 WEditMenuItem *iPtr;
130 WMScreen *scr = WMWidgetScreen(parent);
132 if (!EditMenuItemClass)
133 InitEditMenuItem(scr);
136 iPtr = wmalloc(sizeof(WEditMenuItem));
138 memset(iPtr, 0, sizeof(WEditMenuItem));
140 iPtr->widgetClass = EditMenuItemClass;
142 iPtr->view = W_CreateView(W_VIEW(parent));
143 if (!iPtr->view) {
144 wfree(iPtr);
145 return NULL;
147 iPtr->view->self = iPtr;
149 iPtr->parent = parent;
151 iPtr->label = wstrdup(title);
153 iPtr->flags.isTitle = isTitle;
155 if (isTitle) {
156 WMSetWidgetBackgroundColor(iPtr, WMBlackColor(scr));
159 WMCreateEventHandler(iPtr->view, ExposureMask|StructureNotifyMask,
160 handleItemEvents, iPtr);
162 WMCreateEventHandler(iPtr->view, ButtonPressMask|ButtonReleaseMask
163 |ButtonMotionMask, handleItemClick, iPtr);
166 return iPtr;
169 char*
170 WGetEditMenuItemTitle(WEditMenuItem *item)
172 return item->label;
175 void*
176 WGetEditMenuItemData(WEditMenuItem *item)
178 return item->data;
182 void
183 WSetEditMenuItemData(WEditMenuItem *item, void *data, WMCallback *destroyer)
185 item->data = data;
186 item->destroyData = destroyer;
190 void
191 WSetEditMenuItemImage(WEditMenuItem *item, WMPixmap *pixmap)
193 if (item->pixmap)
194 WMReleasePixmap(item->pixmap);
195 item->pixmap = WMRetainPixmap(pixmap);
199 static void
200 paintEditMenuItem(WEditMenuItem *iPtr)
202 WMScreen *scr = WMWidgetScreen(iPtr);
203 WMColor *color;
204 Window win = W_VIEW(iPtr)->window;
205 int w = W_VIEW(iPtr)->size.width;
206 int h = W_VIEW(iPtr)->size.height;
207 WMFont *font = (iPtr->flags.isTitle ? scr->boldFont : scr->normalFont);
209 if (!iPtr->view->flags.realized)
210 return;
212 color = scr->black;
213 if (iPtr->flags.isTitle && !iPtr->flags.isHighlighted) {
214 color = scr->white;
218 XClearWindow(scr->display, win);
220 W_DrawRelief(scr, win, 0, 0, w+1, h, WRRaised);
222 WMDrawString(scr, win, WMColorGC(color), font, 5, 3, iPtr->label,
223 strlen(iPtr->label));
225 if (iPtr->pixmap) {
226 WMSize size = WMGetPixmapSize(iPtr->pixmap);
228 WMDrawPixmap(iPtr->pixmap, win, w - size.width - 5,
229 (h - size.height)/2);
230 } else if (iPtr->submenu) {
231 /* draw the cascade indicator */
232 XDrawLine(scr->display,win,WMColorGC(scr->darkGray),
233 w-11, 6, w-6, h/2-1);
234 XDrawLine(scr->display,win,WMColorGC(scr->white),
235 w-11, h-8, w-6, h/2-1);
236 XDrawLine(scr->display,win,WMColorGC(scr->black),
237 w-12, 6, w-12, h-8);
242 static void
243 highlightItem(WEditMenuItem *iPtr, Bool high)
245 if (iPtr->flags.isTitle)
246 return;
248 iPtr->flags.isHighlighted = high;
250 if (high) {
251 WMSetWidgetBackgroundColor(iPtr, WMWhiteColor(WMWidgetScreen(iPtr)));
252 } else {
253 if (!iPtr->flags.isTitle) {
254 WMSetWidgetBackgroundColor(iPtr,
255 WMGrayColor(WMWidgetScreen(iPtr)));
256 } else {
257 WMSetWidgetBackgroundColor(iPtr,
258 WMBlackColor(WMWidgetScreen(iPtr)));
264 static int
265 getItemTextWidth(WEditMenuItem *iPtr)
267 WMScreen *scr = WMWidgetScreen(iPtr);
269 if (iPtr->flags.isTitle) {
270 return WMWidthOfString(scr->boldFont, iPtr->label,
271 strlen(iPtr->label));
272 } else {
273 return WMWidthOfString(scr->normalFont, iPtr->label,
274 strlen(iPtr->label));
280 static void
281 handleItemEvents(XEvent *event, void *data)
283 WEditMenuItem *iPtr = (WEditMenuItem*)data;
285 switch (event->type) {
286 case Expose:
287 if (event->xexpose.count!=0)
288 break;
289 paintEditMenuItem(iPtr);
290 break;
292 case DestroyNotify:
293 destroyEditMenuItem(iPtr);
294 break;
299 static void
300 destroyEditMenuItem(WEditMenuItem *iPtr)
302 if (iPtr->label)
303 wfree(iPtr->label);
304 if (iPtr->data && iPtr->destroyData)
305 (*iPtr->destroyData)(iPtr->data);
306 if (iPtr->submenu)
307 WMDestroyWidget(iPtr->submenu);
309 wfree(iPtr);
314 /******************** WEditMenu *******************/
316 static void destroyEditMenu(WEditMenu *mPtr);
318 static void updateMenuContents(WEditMenu *mPtr);
320 static void handleEvents(XEvent *event, void *data);
322 static void editItemLabel(WEditMenuItem *item);
324 static void stopEditItem(WEditMenu *menu, Bool apply);
326 static void deselectItem(WEditMenu *menu);
329 static W_Class EditMenuClass = 0;
332 W_Class
333 InitEditMenu(WMScreen *scr)
335 /* register our widget with WINGs and get our widget class ID */
336 if (!EditMenuClass) {
338 EditMenuClass = W_RegisterUserWidget();
341 return EditMenuClass;
346 typedef struct {
347 int flags;
348 int window_style;
349 int window_level;
350 int reserved;
351 Pixmap miniaturize_pixmap; /* pixmap for miniaturize button */
352 Pixmap close_pixmap; /* pixmap for close button */
353 Pixmap miniaturize_mask; /* miniaturize pixmap mask */
354 Pixmap close_mask; /* close pixmap mask */
355 int extra_flags;
356 } GNUstepWMAttributes;
359 #define GSWindowStyleAttr (1<<0)
360 #define GSWindowLevelAttr (1<<1)
363 static void
364 writeGNUstepWMAttr(WMScreen *scr, Window window, GNUstepWMAttributes *attr)
366 unsigned long data[9];
368 /* handle idiot compilers where array of CARD32 != struct of CARD32 */
369 data[0] = attr->flags;
370 data[1] = attr->window_style;
371 data[2] = attr->window_level;
372 data[3] = 0; /* reserved */
373 /* The X protocol says XIDs are 32bit */
374 data[4] = attr->miniaturize_pixmap;
375 data[5] = attr->close_pixmap;
376 data[6] = attr->miniaturize_mask;
377 data[7] = attr->close_mask;
378 data[8] = attr->extra_flags;
379 XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
380 32, PropModeReplace, (unsigned char *)data, 9);
384 static void
385 realizeObserver(void *self, WMNotification *not)
387 WEditMenu *menu = (WEditMenu*)self;
388 GNUstepWMAttributes attribs;
390 memset(&attribs, 0, sizeof(GNUstepWMAttributes));
391 attribs.flags = GSWindowStyleAttr|GSWindowLevelAttr;
392 attribs.window_style = WMBorderlessWindowMask;
393 attribs.window_level = WMSubmenuWindowLevel;
395 writeGNUstepWMAttr(WMWidgetScreen(menu), menu->view->window, &attribs);
399 static void
400 itemSelectObserver(void *self, WMNotification *notif)
402 WEditMenu *menu = (WEditMenu*)self;
403 WEditMenu *rmenu;
405 rmenu = (WEditMenu*)WMGetNotificationObject(notif);
406 /* check whether rmenu is from the same hierarchy of menu? */
408 if (rmenu == menu) {
409 return;
412 if (menu->selectedItem) {
413 if (!menu->selectedItem->submenu)
414 deselectItem(menu);
415 if (menu->flags.isEditing)
416 stopEditItem(menu, False);
421 static WEditMenu*
422 makeEditMenu(WMScreen *scr, WMWidget *parent, char *title)
424 WEditMenu *mPtr;
425 WEditMenuItem *item;
427 if (!EditMenuClass)
428 InitEditMenu(scr);
431 mPtr = wmalloc(sizeof(WEditMenu));
432 memset(mPtr, 0, sizeof(WEditMenu));
434 mPtr->widgetClass = EditMenuClass;
436 if (parent) {
437 mPtr->view = W_CreateView(W_VIEW(parent));
438 mPtr->flags.standalone = 0;
439 } else {
440 mPtr->view = W_CreateTopView(scr);
441 mPtr->flags.standalone = 1;
443 if (!mPtr->view) {
444 wfree(mPtr);
445 return NULL;
447 mPtr->view->self = mPtr;
449 mPtr->flags.isSelectable = 1;
450 mPtr->flags.isEditable = 1;
452 W_SetViewBackgroundColor(mPtr->view, scr->darkGray);
454 WMAddNotificationObserver(realizeObserver, mPtr,
455 WMViewRealizedNotification, mPtr->view);
457 WMAddNotificationObserver(itemSelectObserver, mPtr,
458 "EditMenuItemSelected", NULL);
460 mPtr->items = WMCreateBag(4);
462 WMCreateEventHandler(mPtr->view, ExposureMask|StructureNotifyMask,
463 handleEvents, mPtr);
466 if (title != NULL) {
467 item = WCreateEditMenuItem(mPtr, title, True);
469 WMMapWidget(item);
470 WMPutInBag(mPtr->items, item);
472 mPtr->flags.isTitled = 1;
475 mPtr->itemHeight = WMFontHeight(scr->normalFont) + 6;
476 mPtr->titleHeight = WMFontHeight(scr->boldFont) + 8;
478 updateMenuContents(mPtr);
480 return mPtr;
484 WEditMenu*
485 WCreateEditMenu(WMScreen *scr, char *title)
487 return makeEditMenu(scr, NULL, title);
491 WEditMenu*
492 WCreateEditMenuPad(WMWidget *parent)
494 return makeEditMenu(WMWidgetScreen(parent), parent, NULL);
498 void
499 WSetEditMenuDelegate(WEditMenu *mPtr, WEditMenuDelegate *delegate)
501 mPtr->delegate = delegate;
505 WEditMenuItem*
506 WInsertMenuItemWithTitle(WEditMenu *mPtr, int index, char *title)
508 WEditMenuItem *item;
510 item = WCreateEditMenuItem(mPtr, title, False);
512 WMMapWidget(item);
514 if (index >= WMGetBagItemCount(mPtr->items)) {
515 WMPutInBag(mPtr->items, item);
516 } else {
517 if (index < 0)
518 index = 0;
519 if (mPtr->flags.isTitled)
520 index++;
521 WMInsertInBag(mPtr->items, index, item);
524 updateMenuContents(mPtr);
526 return item;
530 WEditMenuItem*
531 WGetEditMenuItem(WEditMenu *mPtr, int index)
533 if (index >= WMGetBagItemCount(mPtr->items))
534 return NULL;
535 else
536 return WMGetFromBag(mPtr->items, index + (mPtr->flags.isTitled ? 1 : 0));
540 WEditMenuItem*
541 WAddMenuItemWithTitle(WEditMenu *mPtr, char *title)
543 return WInsertMenuItemWithTitle(mPtr, WMGetBagItemCount(mPtr->items),
544 title);
549 void
550 WSetEditMenuTitle(WEditMenu *mPtr, char *title)
552 WEditMenuItem *item;
554 item = WMGetFromBag(mPtr->items, 0);
556 wfree(item->label);
557 item->label = wstrdup(title);
559 updateMenuContents(mPtr);
561 WMRedisplayWidget(item);
566 char*
567 WGetEditMenuTitle(WEditMenu *mPtr)
569 WEditMenuItem *item;
571 item = WMGetFromBag(mPtr->items, 0);
573 return item->label;
577 void
578 WSetEditMenuAcceptsDrop(WEditMenu *mPtr, Bool flag)
580 mPtr->flags.acceptsDrop = flag;
584 void
585 WSetEditMenuSubmenu(WEditMenu *mPtr, WEditMenuItem *item, WEditMenu *submenu)
587 item->submenu = submenu;
588 submenu->parent = mPtr;
590 paintEditMenuItem(item);
594 WEditMenu*
595 WGetEditMenuSubmenu(WEditMenu *mPtr, WEditMenuItem *item)
597 return item->submenu;
601 static int
602 simpleMatch(void *a, void *b)
604 return ((a == b) ? 1 : 0);
608 void
609 WRemoveEditMenuItem(WEditMenu *mPtr, WEditMenuItem *item)
611 int index;
613 index = WMFindInBag(mPtr->items, simpleMatch, item);
615 if (index == WBNotFound) {
616 return;
619 WMDeleteFromBag(mPtr->items, index);
621 updateMenuContents(mPtr);
625 void
626 WSetEditMenuSelectable(WEditMenu *mPtr, Bool flag)
628 mPtr->flags.isSelectable = flag;
632 void
633 WSetEditMenuEditable(WEditMenu *mPtr, Bool flag)
635 mPtr->flags.isEditable = flag;
639 void
640 WSetEditMenuIsFactory(WEditMenu *mPtr, Bool flag)
642 mPtr->flags.isFactory = flag;
646 void
647 WSetEditMenuMinSize(WEditMenu *mPtr, WMSize size)
649 mPtr->minSize.width = size.width;
650 mPtr->minSize.height = size.height;
654 void
655 WSetEditMenuMaxSize(WEditMenu *mPtr, WMSize size)
657 mPtr->maxSize.width = size.width;
658 mPtr->maxSize.height = size.height;
662 WMPoint
663 WGetEditMenuLocationForSubmenu(WEditMenu *mPtr, WEditMenu *submenu)
665 WMBagIterator iter;
666 WEditMenuItem *item;
667 int y;
669 if (mPtr->flags.isTitled)
670 y = -mPtr->titleHeight;
671 else
672 y = 0;
673 WM_ITERATE_BAG(mPtr->items, item, iter) {
674 if (item->submenu == submenu) {
675 WMPoint pt = WMGetViewScreenPosition(mPtr->view);
677 return wmkpoint(pt.x + mPtr->view->size.width, pt.y + y);
679 y += W_VIEW_HEIGHT(item->view);
682 puts("invalid submenu passed to WGetEditMenuLocationForSubmenu()");
684 return wmkpoint(0,0);
689 static void
690 closeMenuAction(WMWidget *w, void *data)
692 WEditMenu *menu = (WEditMenu*)data;
694 WMAddIdleHandler(WMDestroyWidget, menu->closeB);
695 menu->closeB = NULL;
697 WEditMenuHide(menu);
701 void
702 WTearOffEditMenu(WEditMenu *menu, WEditMenu *submenu)
704 WEditMenuItem *item;
706 submenu->flags.isTornOff = 1;
708 item = (WEditMenuItem*)WMGetFromBag(submenu->items, 0);
710 submenu->closeB = WMCreateCommandButton(item);
711 WMResizeWidget(submenu->closeB, 15, 15);
712 WMMoveWidget(submenu->closeB, W_VIEW(submenu)->size.width - 20, 3);
713 WMRealizeWidget(submenu->closeB);
714 WMSetButtonText(submenu->closeB, "X");
715 WMSetButtonAction(submenu->closeB, closeMenuAction, submenu);
716 WMMapWidget(submenu->closeB);
718 if (menu->selectedItem && menu->selectedItem->submenu == submenu)
719 deselectItem(menu);
724 Bool
725 WEditMenuIsTornOff(WEditMenu *mPtr)
727 return mPtr->flags.isTornOff;
732 void
733 WEditMenuHide(WEditMenu *mPtr)
735 WEditMenuItem *item;
736 int i = 0;
738 if (WMWidgetIsMapped(mPtr)) {
739 WMUnmapWidget(mPtr);
740 mPtr->flags.wasMapped = 1;
741 } else {
742 mPtr->flags.wasMapped = 0;
744 while ((item = WGetEditMenuItem(mPtr, i++))) {
745 WEditMenu *submenu;
747 submenu = WGetEditMenuSubmenu(mPtr, item);
748 if (submenu) {
749 WEditMenuHide(submenu);
756 void
757 WEditMenuUnhide(WEditMenu *mPtr)
759 WEditMenuItem *item;
760 int i = 0;
762 if (mPtr->flags.wasMapped) {
763 WMMapWidget(mPtr);
765 while ((item = WGetEditMenuItem(mPtr, i++))) {
766 WEditMenu *submenu;
768 submenu = WGetEditMenuSubmenu(mPtr, item);
769 if (submenu) {
770 WEditMenuUnhide(submenu);
776 void
777 WEditMenuShowAt(WEditMenu *menu, int x, int y)
779 XSizeHints *hints;
781 hints = XAllocSizeHints();
783 hints->flags = USPosition;
784 hints->x = x;
785 hints->y = y;
787 WMMoveWidget(menu, x, y);
788 XSetWMNormalHints(W_VIEW_DISPLAY(menu->view),
789 W_VIEW_DRAWABLE(menu->view),
790 hints);
791 XFree(hints);
793 WMMapWidget(menu);
797 static void
798 updateMenuContents(WEditMenu *mPtr)
800 int newW, newH;
801 int w;
802 int i;
803 int iheight = mPtr->itemHeight;
804 int offs = 1;
805 WMBagIterator iter;
806 WEditMenuItem *item;
808 newW = 0;
809 newH = offs;
811 i = 0;
812 WM_ITERATE_BAG(mPtr->items, item, iter) {
813 w = getItemTextWidth(item);
815 newW = WMAX(w, newW);
817 WMMoveWidget(item, offs, newH);
818 if (i == 0 && mPtr->flags.isTitled) {
819 newH += mPtr->titleHeight;
820 } else {
821 newH += iheight;
823 i = 1;
826 newW += iheight + 10;
827 newH--;
829 if (mPtr->minSize.width)
830 newW = WMAX(newW, mPtr->minSize.width);
831 if (mPtr->maxSize.width)
832 newW = WMIN(newW, mPtr->maxSize.width);
834 if (mPtr->minSize.height)
835 newH = WMAX(newH, mPtr->minSize.height);
836 if (mPtr->maxSize.height)
837 newH = WMIN(newH, mPtr->maxSize.height);
839 if (W_VIEW(mPtr)->size.width == newW && mPtr->view->size.height == newH+1)
840 return;
842 W_ResizeView(mPtr->view, newW, newH+1);
844 if (mPtr->closeB)
845 WMMoveWidget(mPtr->closeB, newW - 20, 3);
847 newW -= 2*offs;
849 i = 0;
850 WM_ITERATE_BAG(mPtr->items, item, iter) {
851 if (i == 0 && mPtr->flags.isTitled) {
852 WMResizeWidget(item, newW, mPtr->titleHeight);
853 } else {
854 WMResizeWidget(item, newW, iheight);
856 i = 1;
861 static void
862 deselectItem(WEditMenu *menu)
864 WEditMenu *submenu;
865 WEditMenuItem *item = menu->selectedItem;
867 highlightItem(item, False);
869 if (menu->delegate && menu->delegate->itemDeselected) {
870 (*menu->delegate->itemDeselected)(menu->delegate, menu, item);
872 submenu = item->submenu;
874 if (submenu && !WEditMenuIsTornOff(submenu)) {
875 WEditMenuHide(submenu);
878 menu->selectedItem = NULL;
882 static void
883 selectItem(WEditMenu *menu, WEditMenuItem *item)
885 if (!menu->flags.isSelectable || menu->selectedItem == item) {
886 return;
888 if (menu->selectedItem) {
889 deselectItem(menu);
892 if (menu->flags.isEditing) {
893 stopEditItem(menu, False);
896 if (item && !item->flags.isTitle) {
897 highlightItem(item, True);
899 if (item->submenu && !W_VIEW_MAPPED(item->submenu->view)) {
900 WMPoint pt;
902 pt = WGetEditMenuLocationForSubmenu(menu, item->submenu);
904 WEditMenuShowAt(item->submenu, pt.x, pt.y);
906 item->submenu->flags.isTornOff = 0;
909 WMPostNotificationName("EditMenuItemSelected", menu, NULL);
911 if (menu->delegate && menu->delegate->itemSelected) {
912 (*menu->delegate->itemSelected)(menu->delegate, menu, item);
916 menu->selectedItem = item;
920 static void
921 paintMenu(WEditMenu *mPtr)
923 W_View *view = mPtr->view;
925 W_DrawRelief(W_VIEW_SCREEN(view), W_VIEW_DRAWABLE(view), 0, 0,
926 W_VIEW_WIDTH(view), W_VIEW_HEIGHT(view), WRSimple);
930 static void
931 handleEvents(XEvent *event, void *data)
933 WEditMenu *mPtr = (WEditMenu*)data;
935 switch (event->type) {
936 case DestroyNotify:
937 destroyEditMenu(mPtr);
938 break;
940 case Expose:
941 if (event->xexpose.count == 0)
942 paintMenu(mPtr);
943 break;
950 /* -------------------------- Menu Label Editing ------------------------ */
953 static void
954 stopEditItem(WEditMenu *menu, Bool apply)
956 char *text;
958 if (apply) {
959 text = WMGetTextFieldText(menu->tfield);
961 wfree(menu->selectedItem->label);
962 menu->selectedItem->label = wstrdup(text);
964 updateMenuContents(menu);
966 if (menu->delegate && menu->delegate->itemEdited) {
967 (*menu->delegate->itemEdited)(menu->delegate, menu,
968 menu->selectedItem);
972 WMUnmapWidget(menu->tfield);
973 menu->flags.isEditing = 0;
977 static void
978 textEndedEditing(struct WMTextFieldDelegate *self, WMNotification *notif)
980 WEditMenu *menu = (WEditMenu*)self->data;
981 int reason;
982 int i;
983 WEditMenuItem *item;
985 if (!menu->flags.isEditing)
986 return;
988 reason = (int)WMGetNotificationClientData(notif);
990 switch (reason) {
991 case WMEscapeTextMovement:
992 stopEditItem(menu, False);
993 break;
995 case WMReturnTextMovement:
996 stopEditItem(menu, True);
997 break;
999 case WMTabTextMovement:
1000 stopEditItem(menu, True);
1002 i = WMFindInBag(menu->items, simpleMatch, menu->selectedItem);
1003 item = WMGetFromBag(menu->items, i+1);
1004 if (item != NULL) {
1005 selectItem(menu, item);
1006 editItemLabel(item);
1008 break;
1010 case WMBacktabTextMovement:
1011 stopEditItem(menu, True);
1013 i = WMFindInBag(menu->items, simpleMatch, menu->selectedItem);
1014 item = WMGetFromBag(menu->items, i-1);
1015 if (item != NULL) {
1016 selectItem(menu, item);
1017 editItemLabel(item);
1019 break;
1025 static WMTextFieldDelegate textFieldDelegate = {
1026 NULL,
1027 NULL,
1028 NULL,
1029 textEndedEditing,
1030 NULL,
1031 NULL
1035 static void
1036 editItemLabel(WEditMenuItem *item)
1038 WEditMenu *mPtr = item->parent;
1039 WMTextField *tf;
1040 int i;
1042 if (!mPtr->flags.isEditable) {
1043 return;
1046 if (!mPtr->tfield) {
1047 mPtr->tfield = WMCreateTextField(mPtr);
1048 WMSetTextFieldBeveled(mPtr->tfield, False);
1049 WMRealizeWidget(mPtr->tfield);
1051 mPtr->tdelegate = wmalloc(sizeof(WMTextFieldDelegate));
1052 memcpy(mPtr->tdelegate, &textFieldDelegate,
1053 sizeof(WMTextFieldDelegate));
1055 mPtr->tdelegate->data = mPtr;
1057 WMSetTextFieldDelegate(mPtr->tfield, mPtr->tdelegate);
1059 tf = mPtr->tfield;
1061 i = WMFindInBag(mPtr->items, simpleMatch, item);
1063 WMSetTextFieldText(tf, item->label);
1064 WMSelectTextFieldRange(tf, wmkrange(0, strlen(item->label)));
1065 WMResizeWidget(tf, mPtr->view->size.width, mPtr->itemHeight);
1066 WMMoveWidget(tf, 0, item->view->pos.y);
1067 WMMapWidget(tf);
1068 WMSetFocusToWidget(tf);
1070 mPtr->flags.isEditing = 1;
1075 /* -------------------------------------------------- */
1078 static void
1079 slideWindow(Display *dpy, Window win, int srcX, int srcY, int dstX, int dstY)
1081 double x, y, dx, dy;
1082 int i;
1083 int iterations;
1085 iterations = WMIN(25, WMAX(abs(dstX-srcX), abs(dstY-srcY)));
1087 x = srcX;
1088 y = srcY;
1090 dx = (double)(dstX-srcX)/iterations;
1091 dy = (double)(dstY-srcY)/iterations;
1093 for (i = 0; i <= iterations; i++) {
1094 XMoveWindow(dpy, win, x, y);
1095 XFlush(dpy);
1097 wusleep(800);
1099 x += dx;
1100 y += dy;
1105 static int
1106 errorHandler(Display *d, XErrorEvent *ev)
1108 /* just ignore */
1109 return 0;
1113 static WEditMenu*
1114 findMenuInWindow(Display *dpy, Window toplevel, int x, int y)
1116 Window foo, bar;
1117 Window *children;
1118 unsigned nchildren;
1119 int i;
1120 WEditMenu *menu;
1121 WMView *view;
1122 int (*oldHandler)(Display *, XErrorEvent *);
1124 view = W_GetViewForXWindow(dpy, toplevel);
1125 if (view && view->self && WMWidgetClass(view->self) == EditMenuClass) {
1126 menu = (WEditMenu*)view->self;
1127 if (menu->flags.acceptsDrop) {
1128 return menu;
1132 if (!XQueryTree(dpy, toplevel, &foo, &bar,
1133 &children, &nchildren) || children == NULL) {
1134 return NULL;
1137 oldHandler = XSetErrorHandler(errorHandler);
1139 /* first window that contains the point is the one */
1140 for (i = nchildren-1; i >= 0; i--) {
1141 XWindowAttributes attr;
1143 if (XGetWindowAttributes(dpy, children[i], &attr)
1144 && attr.map_state == IsViewable
1145 && x >= attr.x && y >= attr.y
1146 && x < attr.x + attr.width && y < attr.y + attr.height) {
1147 Window tmp;
1149 tmp = children[i];
1151 menu = findMenuInWindow(dpy, tmp, x - attr.x, y - attr.y);
1152 if (menu) {
1153 XFree(children);
1154 return menu;
1159 XSetErrorHandler(oldHandler);
1161 XFree(children);
1162 return NULL;
1166 static void
1167 handleDragOver(WEditMenu *menu, WMView *view, WEditMenuItem *item,
1168 int x, int y)
1170 WMScreen *scr = W_VIEW_SCREEN(menu->view);
1171 Window blaw;
1172 int mx, my;
1173 int offs;
1175 XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
1176 scr->rootWin, 0, 0, &mx, &my, &blaw);
1178 offs = menu->flags.standalone ? 0 : 1;
1180 W_MoveView(view, mx + offs, y);
1181 if (view->size.width != menu->view->size.width) {
1182 W_ResizeView(view, menu->view->size.width - 2*offs,
1183 menu->itemHeight);
1184 W_ResizeView(item->view, menu->view->size.width - 2*offs,
1185 menu->itemHeight);
1190 static void
1191 handleItemDrop(WEditMenu *menu, WEditMenuItem *item, int x, int y)
1193 WMScreen *scr = W_VIEW_SCREEN(menu->view);
1194 Window blaw;
1195 int mx, my;
1196 int index;
1198 XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
1199 scr->rootWin, 0, 0, &mx, &my, &blaw);
1201 index = y - my;
1202 if (menu->flags.isTitled) {
1203 index -= menu->titleHeight;
1205 index = (index + menu->itemHeight/2) / menu->itemHeight;
1206 if (index < 0)
1207 index = 0;
1209 if (menu->flags.isTitled) {
1210 index++;
1213 if (index > WMGetBagItemCount(menu->items)) {
1214 WMPutInBag(menu->items, item);
1215 } else {
1216 WMInsertInBag(menu->items, index, item);
1219 W_ReparentView(item->view, menu->view, 0, index*menu->itemHeight);
1221 item->parent = menu;
1222 if (item->submenu) {
1223 item->submenu->parent = menu;
1226 updateMenuContents(menu);
1230 static void
1231 dragMenu(WEditMenu *menu)
1233 WMScreen *scr = W_VIEW_SCREEN(menu->view);
1234 XEvent ev;
1235 Bool done = False;
1236 int dx, dy;
1237 unsigned blau;
1238 Window blaw;
1240 XGetGeometry(scr->display, W_VIEW_DRAWABLE(menu->view), &blaw, &dx, &dy,
1241 &blau, &blau, &blau, &blau);
1243 XTranslateCoordinates(scr->display, W_VIEW_DRAWABLE(menu->view),
1244 scr->rootWin, dx, dy, &dx, &dy, &blaw);
1246 dx = menu->dragX - dx;
1247 dy = menu->dragY - dy;
1249 XGrabPointer(scr->display, scr->rootWin, False,
1250 ButtonReleaseMask|ButtonMotionMask,
1251 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
1252 CurrentTime);
1254 if (menu->parent)
1255 WTearOffEditMenu(menu->parent, menu);
1257 while (!done) {
1258 WMNextEvent(scr->display, &ev);
1260 switch (ev.type) {
1261 case ButtonRelease:
1262 done = True;
1263 break;
1265 case MotionNotify:
1266 while (XCheckMaskEvent(scr->display, ButtonMotionMask, &ev)) ;
1268 WMMoveWidget(menu, ev.xmotion.x_root - dx,
1269 ev.xmotion.y_root - dy);
1270 break;
1272 default:
1273 WMHandleEvent(&ev);
1274 break;
1278 XUngrabPointer(scr->display, CurrentTime);
1283 static WEditMenuItem*
1284 duplicateItem(WEditMenuItem *item)
1286 WEditMenuItem *nitem;
1288 nitem = WCreateEditMenuItem(item->parent, item->label, False);
1289 if (item->pixmap)
1290 nitem->pixmap = WMRetainPixmap(item->pixmap);
1292 return nitem;
1298 static WEditMenu*
1299 duplicateMenu(WEditMenu *menu)
1301 WEditMenu *nmenu;
1302 WEditMenuItem *item;
1303 WMBagIterator iter;
1304 Bool first = menu->flags.isTitled;
1306 nmenu = WCreateEditMenu(WMWidgetScreen(menu), WGetEditMenuTitle(menu));
1308 memcpy(&nmenu->flags, &menu->flags, sizeof(menu->flags));
1309 nmenu->delegate = menu->delegate;
1311 WM_ITERATE_BAG(menu->items, item, iter) {
1312 WEditMenuItem *nitem;
1314 if (first) {
1315 first = False;
1316 continue;
1319 nitem = WAddMenuItemWithTitle(nmenu, item->label);
1320 if (item->pixmap)
1321 WSetEditMenuItemImage(nitem, item->pixmap);
1323 if (menu->delegate && menu->delegate->itemCloned) {
1324 (*menu->delegate->itemCloned)(menu->delegate, menu,
1325 item, nitem);
1329 WMRealizeWidget(nmenu);
1331 return nmenu;
1336 static void
1337 dragItem(WEditMenu *menu, WEditMenuItem *item, Bool copy)
1339 static XColor black = {0, 0,0,0, DoRed|DoGreen|DoBlue};
1340 static XColor green = {0x0045b045, 0x4500,0xb000,0x4500, DoRed|DoGreen|DoBlue};
1341 static XColor back = {0, 0xffff,0xffff,0xffff, DoRed|DoGreen|DoBlue};
1342 Display *dpy = W_VIEW_DISPLAY(menu->view);
1343 WMScreen *scr = W_VIEW_SCREEN(menu->view);
1344 int x, y;
1345 int dx, dy;
1346 Bool done = False;
1347 Window blaw;
1348 int blai;
1349 unsigned blau;
1350 Window win;
1351 WMView *dview;
1352 int orix, oriy;
1353 Bool enteredMenu = False;
1354 WMSize oldSize = item->view->size;
1355 WEditMenuItem *dritem = item;
1356 WEditMenu *dmenu = NULL;
1359 if (item->flags.isTitle) {
1360 WMRaiseWidget(menu);
1362 dragMenu(menu);
1364 return;
1367 selectItem(menu, NULL);
1369 win = scr->rootWin;
1371 XTranslateCoordinates(dpy, W_VIEW_DRAWABLE(item->view), win,
1372 0, 0, &orix, &oriy, &blaw);
1374 dview = W_CreateUnmanagedTopView(scr);
1375 W_SetViewBackgroundColor(dview, scr->black);
1376 W_ResizeView(dview, W_VIEW_WIDTH(item->view), W_VIEW_HEIGHT(item->view));
1377 W_MoveView(dview, orix, oriy);
1378 W_RealizeView(dview);
1380 if (menu->flags.isFactory || copy) {
1381 dritem = duplicateItem(item);
1383 W_ReparentView(dritem->view, dview, 0, 0);
1384 WMResizeWidget(dritem, oldSize.width, oldSize.height);
1385 WMRealizeWidget(dritem);
1386 WMMapWidget(dritem);
1387 } else {
1388 W_ReparentView(item->view, dview, 0, 0);
1391 W_MapView(dview);
1393 dx = menu->dragX - orix;
1394 dy = menu->dragY - oriy;
1396 XGrabPointer(dpy, scr->rootWin, False,
1397 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
1398 GrabModeAsync, GrabModeAsync, None, scr->defaultCursor,
1399 CurrentTime);
1401 if (menu->flags.acceptsDrop)
1402 XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
1404 while (!done) {
1405 XEvent ev;
1407 WMNextEvent(dpy, &ev);
1409 switch (ev.type) {
1410 case MotionNotify:
1411 while (XCheckMaskEvent(dpy, ButtonMotionMask, &ev)) ;
1413 XQueryPointer(dpy, win, &blaw, &blaw, &blai, &blai, &x, &y, &blau);
1415 dmenu = findMenuInWindow(dpy, win, x, y);
1417 if (dmenu) {
1418 handleDragOver(dmenu, dview, dritem, x - dx, y - dy);
1419 if (!enteredMenu) {
1420 enteredMenu = True;
1421 XRecolorCursor(dpy, scr->defaultCursor, &green, &back);
1423 } else {
1424 if (enteredMenu) {
1425 W_ResizeView(dview, oldSize.width, oldSize.height);
1426 W_ResizeView(dritem->view, oldSize.width, oldSize.height);
1427 enteredMenu = False;
1428 XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
1430 W_MoveView(dview, x - dx, y - dy);
1433 break;
1435 case ButtonRelease:
1436 done = True;
1437 break;
1439 default:
1440 WMHandleEvent(&ev);
1441 break;
1444 XRecolorCursor(dpy, scr->defaultCursor, &black, &back);
1446 XUngrabPointer(dpy, CurrentTime);
1449 if (!enteredMenu) {
1450 Bool rem = True;
1452 if (!menu->flags.isFactory && !copy) {
1453 W_UnmapView(dview);
1454 if (menu->delegate && menu->delegate->shouldRemoveItem) {
1455 rem = (*menu->delegate->shouldRemoveItem)(menu->delegate,
1456 menu, item);
1458 W_MapView(dview);
1461 if (!rem || menu->flags.isFactory || copy) {
1462 slideWindow(dpy, W_VIEW_DRAWABLE(dview), x-dx, y-dy, orix, oriy);
1464 if (!menu->flags.isFactory && !copy) {
1465 WRemoveEditMenuItem(menu, dritem);
1466 handleItemDrop(dmenu ? dmenu : menu, dritem, orix, oriy);
1468 } else {
1469 WRemoveEditMenuItem(menu, dritem);
1471 } else {
1472 WRemoveEditMenuItem(menu, dritem);
1474 if (menu->delegate && menu->delegate->itemCloned
1475 && (menu->flags.isFactory || copy)) {
1476 (*menu->delegate->itemCloned)(menu->delegate, menu,
1477 item, dritem);
1480 handleItemDrop(dmenu, dritem, x-dy, y-dy);
1482 if (item->submenu && (menu->flags.isFactory || copy)) {
1483 WEditMenu *submenu;
1485 submenu = duplicateMenu(item->submenu);
1486 WSetEditMenuSubmenu(dmenu, dritem, submenu);
1490 /* can't destroy now because we're being called from
1491 * the event handler of the item. and destroying now,
1492 * would mean destroying the item too in some cases.
1494 WMAddIdleHandler((WMCallback*)W_DestroyView, dview);
1499 static void
1500 handleItemClick(XEvent *event, void *data)
1502 WEditMenuItem *item = (WEditMenuItem*)data;
1503 WEditMenu *menu = item->parent;
1505 switch (event->type) {
1506 case ButtonPress:
1507 selectItem(menu, item);
1509 if (WMIsDoubleClick(event)) {
1510 editItemLabel(item);
1513 menu->flags.isDragging = 1;
1514 menu->dragX = event->xbutton.x_root;
1515 menu->dragY = event->xbutton.y_root;
1516 break;
1518 case ButtonRelease:
1519 menu->flags.isDragging = 0;
1520 break;
1522 case MotionNotify:
1523 if (menu->flags.isDragging) {
1524 if (abs(event->xbutton.x_root - menu->dragX) > 5
1525 || abs(event->xbutton.y_root - menu->dragY) > 5) {
1526 menu->flags.isDragging = 0;
1527 dragItem(menu, item, event->xbutton.state & ControlMask);
1530 break;
1535 static void
1536 destroyEditMenu(WEditMenu *mPtr)
1538 WEditMenuItem *item;
1539 WMBagIterator iter;
1541 WMRemoveNotificationObserver(mPtr);
1543 WM_ITERATE_BAG(mPtr->items, item, iter) {
1544 if (item->submenu) {
1545 WMDestroyWidget(item->submenu);
1549 WMFreeBag(mPtr->items);
1551 wfree(mPtr->tdelegate);
1553 wfree(mPtr);