1 /* editmenu.c - editable menus
3 * WPrefs - Window Maker Preferences Program
5 * Copyright (c) 2000-2003 Alfredo K. Kojima
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 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.
22 #include <WINGs/WINGsP.h>
23 #include <WINGs/WUtil.h>
31 typedef struct W_EditMenuItem
{
35 struct W_EditMenu
*parent
;
38 WMPixmap
*pixmap
; /* pixmap to show at left */
41 WMCallback
*destroyData
;
43 struct W_EditMenu
*submenu
; /* if it's a cascade, NULL otherwise */
47 unsigned isHighlighted
:1;
51 typedef struct W_EditMenu
{
55 struct W_EditMenu
*parent
;
57 WMArray
*items
; /* EditMenuItem */
59 EditMenuItem
*selectedItem
;
68 WEditMenuDelegate
*delegate
;
70 WMTextFieldDelegate
*tdelegate
;
76 /* only for non-standalone menu */
81 unsigned standalone
:1;
84 unsigned acceptsDrop
:1;
86 unsigned isSelectable
:1;
87 unsigned isEditable
:1;
91 unsigned isDragging
:1;
98 /******************** WEditMenuItem ********************/
100 static void destroyEditMenuItem(WEditMenuItem
* iPtr
);
101 static void paintEditMenuItem(WEditMenuItem
* iPtr
);
102 static void handleItemEvents(XEvent
* event
, void *data
);
104 static void handleItemClick(XEvent
* event
, void *data
);
106 static W_Class EditMenuItemClass
= 0;
108 W_Class
InitEditMenuItem(WMScreen
* scr
)
110 /* register our widget with WINGs and get our widget class ID */
111 if (!EditMenuItemClass
) {
112 EditMenuItemClass
= W_RegisterUserWidget();
115 return EditMenuItemClass
;
118 WEditMenuItem
*WCreateEditMenuItem(WMWidget
* parent
, const char *title
, Bool isTitle
)
121 WMScreen
*scr
= WMWidgetScreen(parent
);
123 if (!EditMenuItemClass
)
124 InitEditMenuItem(scr
);
126 iPtr
= wmalloc(sizeof(WEditMenuItem
));
128 iPtr
->widgetClass
= EditMenuItemClass
;
130 iPtr
->view
= W_CreateView(W_VIEW(parent
));
135 iPtr
->view
->self
= iPtr
;
137 iPtr
->parent
= parent
;
139 iPtr
->label
= wstrdup(title
);
141 iPtr
->flags
.isTitle
= isTitle
;
144 WMSetWidgetBackgroundColor(iPtr
, WMBlackColor(scr
));
147 WMCreateEventHandler(iPtr
->view
, ExposureMask
| StructureNotifyMask
, handleItemEvents
, iPtr
);
149 WMCreateEventHandler(iPtr
->view
, ButtonPressMask
| ButtonReleaseMask
150 | ButtonMotionMask
, handleItemClick
, iPtr
);
155 char *WGetEditMenuItemTitle(WEditMenuItem
* item
)
160 void *WGetEditMenuItemData(WEditMenuItem
* item
)
165 void WSetEditMenuItemData(WEditMenuItem
* item
, void *data
, WMCallback
* destroyer
)
168 item
->destroyData
= destroyer
;
171 void WSetEditMenuItemImage(WEditMenuItem
* item
, WMPixmap
* pixmap
)
174 WMReleasePixmap(item
->pixmap
);
175 item
->pixmap
= WMRetainPixmap(pixmap
);
178 static void paintEditMenuItem(WEditMenuItem
* iPtr
)
180 WMScreen
*scr
= WMWidgetScreen(iPtr
);
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
);
187 if (!iPtr
->view
->flags
.realized
)
191 if (iPtr
->flags
.isTitle
&& !iPtr
->flags
.isHighlighted
) {
195 XClearWindow(scr
->display
, win
);
197 W_DrawRelief(scr
, win
, 0, 0, w
+ 1, h
, WRRaised
);
199 WMDrawString(scr
, win
, color
, font
, 5, 3, iPtr
->label
, strlen(iPtr
->label
));
202 WMSize size
= WMGetPixmapSize(iPtr
->pixmap
);
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);
213 static void highlightItem(WEditMenuItem
* iPtr
, Bool high
)
215 if (iPtr
->flags
.isTitle
)
218 iPtr
->flags
.isHighlighted
= high
;
221 WMSetWidgetBackgroundColor(iPtr
, WMWhiteColor(WMWidgetScreen(iPtr
)));
223 if (!iPtr
->flags
.isTitle
) {
224 WMSetWidgetBackgroundColor(iPtr
, WMGrayColor(WMWidgetScreen(iPtr
)));
226 WMSetWidgetBackgroundColor(iPtr
, WMBlackColor(WMWidgetScreen(iPtr
)));
231 static int getItemTextWidth(WEditMenuItem
* iPtr
)
233 WMScreen
*scr
= WMWidgetScreen(iPtr
);
235 if (iPtr
->flags
.isTitle
) {
236 return WMWidthOfString(scr
->boldFont
, iPtr
->label
, strlen(iPtr
->label
));
238 return WMWidthOfString(scr
->normalFont
, iPtr
->label
, strlen(iPtr
->label
));
242 static void handleItemEvents(XEvent
* event
, void *data
)
244 WEditMenuItem
*iPtr
= (WEditMenuItem
*) data
;
246 switch (event
->type
) {
248 if (event
->xexpose
.count
!= 0)
250 paintEditMenuItem(iPtr
);
254 destroyEditMenuItem(iPtr
);
259 static void destroyEditMenuItem(WEditMenuItem
* iPtr
)
263 if (iPtr
->data
&& iPtr
->destroyData
)
264 (*iPtr
->destroyData
) (iPtr
->data
);
266 WMDestroyWidget(iPtr
->submenu
);
271 /******************** WEditMenu *******************/
273 static void destroyEditMenu(WEditMenu
* mPtr
);
275 static void updateMenuContents(WEditMenu
* mPtr
);
277 static void handleEvents(XEvent
* event
, void *data
);
279 static void editItemLabel(WEditMenuItem
* item
);
281 static void stopEditItem(WEditMenu
* menu
, Bool apply
);
283 static void deselectItem(WEditMenu
* menu
);
285 static W_Class EditMenuClass
= 0;
287 W_Class
InitEditMenu(WMScreen
* scr
)
289 /* register our widget with WINGs and get our widget class ID */
290 if (!EditMenuClass
) {
292 EditMenuClass
= W_RegisterUserWidget();
295 return EditMenuClass
;
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 */
308 } GNUstepWMAttributes
;
310 #define GSWindowStyleAttr (1<<0)
311 #define GSWindowLevelAttr (1<<1)
313 static void writeGNUstepWMAttr(WMScreen
* scr
, Window window
, GNUstepWMAttributes
* attr
)
315 unsigned long data
[9];
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);
332 static void realizeObserver(void *self
, WMNotification
* not)
334 WEditMenu
*menu
= (WEditMenu
*) self
;
335 GNUstepWMAttributes attribs
;
337 /* Parameter not used, but tell the compiler that it is ok */
340 memset(&attribs
, 0, sizeof(GNUstepWMAttributes
));
341 attribs
.flags
= GSWindowStyleAttr
| GSWindowLevelAttr
;
342 attribs
.window_style
= WMBorderlessWindowMask
;
343 attribs
.window_level
= WMSubmenuWindowLevel
;
345 writeGNUstepWMAttr(WMWidgetScreen(menu
), menu
->view
->window
, &attribs
);
348 static void itemSelectObserver(void *self
, WMNotification
* notif
)
350 WEditMenu
*menu
= (WEditMenu
*) self
;
353 rmenu
= (WEditMenu
*) WMGetNotificationObject(notif
);
354 /* check whether rmenu is from the same hierarchy of menu? */
360 if (menu
->selectedItem
) {
361 if (!menu
->selectedItem
->submenu
)
363 if (menu
->flags
.isEditing
)
364 stopEditItem(menu
, False
);
368 static WEditMenu
*makeEditMenu(WMScreen
* scr
, WMWidget
* parent
, const char *title
)
376 mPtr
= wmalloc(sizeof(WEditMenu
));
378 mPtr
->widgetClass
= EditMenuClass
;
381 mPtr
->view
= W_CreateView(W_VIEW(parent
));
382 mPtr
->flags
.standalone
= 0;
384 mPtr
->view
= W_CreateTopView(scr
);
385 mPtr
->flags
.standalone
= 1;
391 mPtr
->view
->self
= mPtr
;
393 mPtr
->flags
.isSelectable
= 1;
394 mPtr
->flags
.isEditable
= 1;
396 W_SetViewBackgroundColor(mPtr
->view
, scr
->darkGray
);
398 WMAddNotificationObserver(realizeObserver
, mPtr
, WMViewRealizedNotification
, mPtr
->view
);
400 WMAddNotificationObserver(itemSelectObserver
, mPtr
, "EditMenuItemSelected", NULL
);
402 mPtr
->items
= WMCreateArray(4);
404 WMCreateEventHandler(mPtr
->view
, ExposureMask
| StructureNotifyMask
, handleEvents
, mPtr
);
407 item
= WCreateEditMenuItem(mPtr
, title
, True
);
410 WMAddToArray(mPtr
->items
, item
);
412 mPtr
->flags
.isTitled
= 1;
415 mPtr
->itemHeight
= WMFontHeight(scr
->normalFont
) + 6;
416 mPtr
->titleHeight
= WMFontHeight(scr
->boldFont
) + 8;
418 updateMenuContents(mPtr
);
423 WEditMenu
*WCreateEditMenu(WMScreen
* scr
, const char *title
)
425 return makeEditMenu(scr
, NULL
, title
);
428 WEditMenu
*WCreateEditMenuPad(WMWidget
* parent
)
430 return makeEditMenu(WMWidgetScreen(parent
), parent
, NULL
);
433 void WSetEditMenuDelegate(WEditMenu
* mPtr
, WEditMenuDelegate
* delegate
)
435 mPtr
->delegate
= delegate
;
438 WEditMenuItem
*WInsertMenuItemWithTitle(WEditMenu
* mPtr
, int index
, const char *title
)
442 item
= WCreateEditMenuItem(mPtr
, title
, False
);
446 if (index
>= WMGetArrayItemCount(mPtr
->items
)) {
447 WMAddToArray(mPtr
->items
, item
);
451 if (mPtr
->flags
.isTitled
)
453 WMInsertInArray(mPtr
->items
, index
, item
);
456 updateMenuContents(mPtr
);
461 WEditMenuItem
*WGetEditMenuItem(WEditMenu
* mPtr
, int index
)
463 if (index
>= WMGetArrayItemCount(mPtr
->items
))
466 return WMGetFromArray(mPtr
->items
, index
+ (mPtr
->flags
.isTitled
? 1 : 0));
469 WEditMenuItem
*WAddMenuItemWithTitle(WEditMenu
* mPtr
, const char *title
)
471 return WInsertMenuItemWithTitle(mPtr
, WMGetArrayItemCount(mPtr
->items
), title
);
474 void WSetEditMenuTitle(WEditMenu
* mPtr
, const char *title
)
478 item
= WMGetFromArray(mPtr
->items
, 0);
481 item
->label
= wstrdup(title
);
483 updateMenuContents(mPtr
);
485 WMRedisplayWidget(item
);
488 char *WGetEditMenuTitle(WEditMenu
* mPtr
)
492 item
= WMGetFromArray(mPtr
->items
, 0);
497 void WSetEditMenuAcceptsDrop(WEditMenu
* mPtr
, Bool flag
)
499 mPtr
->flags
.acceptsDrop
= flag
;
502 void WSetEditMenuSubmenu(WEditMenu
* mPtr
, WEditMenuItem
* item
, WEditMenu
* submenu
)
504 item
->submenu
= submenu
;
505 submenu
->parent
= mPtr
;
507 paintEditMenuItem(item
);
510 WEditMenu
*WGetEditMenuSubmenu(WEditMenu
* mPtr
, WEditMenuItem
* item
)
512 return item
->submenu
;
515 void WRemoveEditMenuItem(WEditMenu
* mPtr
, WEditMenuItem
* item
)
517 if (WMRemoveFromArray(mPtr
->items
, item
) != 0) {
518 updateMenuContents(mPtr
);
522 void WSetEditMenuSelectable(WEditMenu
* mPtr
, Bool flag
)
524 mPtr
->flags
.isSelectable
= flag
;
527 void WSetEditMenuEditable(WEditMenu
* mPtr
, Bool flag
)
529 mPtr
->flags
.isEditable
= flag
;
532 void WSetEditMenuIsFactory(WEditMenu
* mPtr
, Bool flag
)
534 mPtr
->flags
.isFactory
= flag
;
537 void WSetEditMenuMinSize(WEditMenu
* mPtr
, WMSize size
)
539 mPtr
->minSize
.width
= size
.width
;
540 mPtr
->minSize
.height
= size
.height
;
543 void WSetEditMenuMaxSize(WEditMenu
* mPtr
, WMSize size
)
545 mPtr
->maxSize
.width
= size
.width
;
546 mPtr
->maxSize
.height
= size
.height
;
549 WMPoint
WGetEditMenuLocationForSubmenu(WEditMenu
* mPtr
, WEditMenu
* submenu
)
551 WMArrayIterator iter
;
555 if (mPtr
->flags
.isTitled
)
556 y
= -mPtr
->titleHeight
;
559 WM_ITERATE_ARRAY(mPtr
->items
, item
, iter
) {
560 if (item
->submenu
== submenu
) {
561 WMPoint pt
= WMGetViewScreenPosition(mPtr
->view
);
563 return wmkpoint(pt
.x
+ mPtr
->view
->size
.width
, pt
.y
+ y
);
565 y
+= W_VIEW_HEIGHT(item
->view
);
568 puts("invalid submenu passed to WGetEditMenuLocationForSubmenu()");
570 return wmkpoint(0, 0);
573 static void closeMenuAction(WMWidget
* w
, void *data
)
575 WEditMenu
*menu
= (WEditMenu
*) data
;
577 WMAddIdleHandler(WMDestroyWidget
, menu
->closeB
);
583 void WTearOffEditMenu(WEditMenu
* menu
, WEditMenu
* submenu
)
587 submenu
->flags
.isTornOff
= 1;
589 item
= (WEditMenuItem
*) WMGetFromArray(submenu
->items
, 0);
591 submenu
->closeB
= WMCreateCommandButton(item
);
592 WMResizeWidget(submenu
->closeB
, 15, 15);
593 WMMoveWidget(submenu
->closeB
, W_VIEW(submenu
)->size
.width
- 20, 3);
594 WMRealizeWidget(submenu
->closeB
);
595 WMSetButtonText(submenu
->closeB
, "X");
596 WMSetButtonAction(submenu
->closeB
, closeMenuAction
, submenu
);
597 WMMapWidget(submenu
->closeB
);
599 if (menu
->selectedItem
&& menu
->selectedItem
->submenu
== submenu
)
603 Bool
WEditMenuIsTornOff(WEditMenu
* mPtr
)
605 return mPtr
->flags
.isTornOff
;
608 void WEditMenuHide(WEditMenu
* mPtr
)
613 if (WMWidgetIsMapped(mPtr
)) {
615 mPtr
->flags
.wasMapped
= 1;
617 mPtr
->flags
.wasMapped
= 0;
619 while ((item
= WGetEditMenuItem(mPtr
, i
++))) {
622 submenu
= WGetEditMenuSubmenu(mPtr
, item
);
624 WEditMenuHide(submenu
);
629 void WEditMenuUnhide(WEditMenu
* mPtr
)
634 if (mPtr
->flags
.wasMapped
) {
637 while ((item
= WGetEditMenuItem(mPtr
, i
++))) {
640 submenu
= WGetEditMenuSubmenu(mPtr
, item
);
642 WEditMenuUnhide(submenu
);
647 void WEditMenuShowAt(WEditMenu
* menu
, int x
, int y
)
651 hints
= XAllocSizeHints();
653 hints
->flags
= USPosition
;
657 WMMoveWidget(menu
, x
, y
);
658 XSetWMNormalHints(W_VIEW_DISPLAY(menu
->view
), W_VIEW_DRAWABLE(menu
->view
), hints
);
664 static void updateMenuContents(WEditMenu
* mPtr
)
669 int iheight
= mPtr
->itemHeight
;
671 WMArrayIterator iter
;
678 WM_ITERATE_ARRAY(mPtr
->items
, item
, iter
) {
679 w
= getItemTextWidth(item
);
681 newW
= WMAX(w
, newW
);
683 WMMoveWidget(item
, offs
, newH
);
684 if (i
== 0 && mPtr
->flags
.isTitled
) {
685 newH
+= mPtr
->titleHeight
;
692 newW
+= iheight
+ 10;
695 if (mPtr
->minSize
.width
)
696 newW
= WMAX(newW
, mPtr
->minSize
.width
);
697 if (mPtr
->maxSize
.width
)
698 newW
= WMIN(newW
, mPtr
->maxSize
.width
);
700 if (mPtr
->minSize
.height
)
701 newH
= WMAX(newH
, mPtr
->minSize
.height
);
702 if (mPtr
->maxSize
.height
)
703 newH
= WMIN(newH
, mPtr
->maxSize
.height
);
705 if (W_VIEW(mPtr
)->size
.width
== newW
&& mPtr
->view
->size
.height
== newH
+ 1)
708 W_ResizeView(mPtr
->view
, newW
, newH
+ 1);
711 WMMoveWidget(mPtr
->closeB
, newW
- 20, 3);
716 WM_ITERATE_ARRAY(mPtr
->items
, item
, iter
) {
717 if (i
== 0 && mPtr
->flags
.isTitled
) {
718 WMResizeWidget(item
, newW
, mPtr
->titleHeight
);
720 WMResizeWidget(item
, newW
, iheight
);
726 static void deselectItem(WEditMenu
* menu
)
729 WEditMenuItem
*item
= menu
->selectedItem
;
731 highlightItem(item
, False
);
733 if (menu
->delegate
&& menu
->delegate
->itemDeselected
) {
734 (*menu
->delegate
->itemDeselected
) (menu
->delegate
, menu
, item
);
736 submenu
= item
->submenu
;
738 if (submenu
&& !WEditMenuIsTornOff(submenu
)) {
739 WEditMenuHide(submenu
);
742 menu
->selectedItem
= NULL
;
745 static void selectItem(WEditMenu
* menu
, WEditMenuItem
* item
)
747 if (!menu
->flags
.isSelectable
|| menu
->selectedItem
== item
) {
750 if (menu
->selectedItem
) {
754 if (menu
->flags
.isEditing
) {
755 stopEditItem(menu
, False
);
758 if (item
&& !item
->flags
.isTitle
) {
759 highlightItem(item
, True
);
761 if (item
->submenu
&& !W_VIEW_MAPPED(item
->submenu
->view
)) {
764 pt
= WGetEditMenuLocationForSubmenu(menu
, item
->submenu
);
766 WEditMenuShowAt(item
->submenu
, pt
.x
, pt
.y
);
768 item
->submenu
->flags
.isTornOff
= 0;
771 WMPostNotificationName("EditMenuItemSelected", menu
, NULL
);
773 if (menu
->delegate
&& menu
->delegate
->itemSelected
) {
774 (*menu
->delegate
->itemSelected
) (menu
->delegate
, menu
, item
);
778 menu
->selectedItem
= item
;
781 static void paintMenu(WEditMenu
* mPtr
)
783 W_View
*view
= mPtr
->view
;
785 W_DrawRelief(W_VIEW_SCREEN(view
), W_VIEW_DRAWABLE(view
), 0, 0,
786 W_VIEW_WIDTH(view
), W_VIEW_HEIGHT(view
), WRSimple
);
789 static void handleEvents(XEvent
* event
, void *data
)
791 WEditMenu
*mPtr
= (WEditMenu
*) data
;
793 switch (event
->type
) {
795 destroyEditMenu(mPtr
);
799 if (event
->xexpose
.count
== 0)
805 /* -------------------------- Menu Label Editing ------------------------ */
807 static void stopEditItem(WEditMenu
* menu
, Bool apply
)
812 text
= WMGetTextFieldText(menu
->tfield
);
814 wfree(menu
->selectedItem
->label
);
815 menu
->selectedItem
->label
= wstrdup(text
);
817 updateMenuContents(menu
);
819 if (menu
->delegate
&& menu
->delegate
->itemEdited
) {
820 (*menu
->delegate
->itemEdited
) (menu
->delegate
, menu
, menu
->selectedItem
);
824 WMUnmapWidget(menu
->tfield
);
825 menu
->flags
.isEditing
= 0;
828 static void textEndedEditing(struct WMTextFieldDelegate
*self
, WMNotification
* notif
)
830 WEditMenu
*menu
= (WEditMenu
*) self
->data
;
835 if (!menu
->flags
.isEditing
)
838 reason
= (uintptr_t)WMGetNotificationClientData(notif
);
841 case WMEscapeTextMovement
:
842 stopEditItem(menu
, False
);
845 case WMReturnTextMovement
:
846 stopEditItem(menu
, True
);
849 case WMTabTextMovement
:
850 stopEditItem(menu
, True
);
852 i
= WMGetFirstInArray(menu
->items
, menu
->selectedItem
);
853 item
= WMGetFromArray(menu
->items
, i
+ 1);
855 selectItem(menu
, item
);
860 case WMBacktabTextMovement
:
861 stopEditItem(menu
, True
);
863 i
= WMGetFirstInArray(menu
->items
, menu
->selectedItem
);
864 item
= WMGetFromArray(menu
->items
, i
- 1);
866 selectItem(menu
, item
);
873 static WMTextFieldDelegate textFieldDelegate
= {
882 static void editItemLabel(WEditMenuItem
* item
)
884 WEditMenu
*mPtr
= item
->parent
;
887 if (!mPtr
->flags
.isEditable
) {
892 mPtr
->tfield
= WMCreateTextField(mPtr
);
893 WMSetTextFieldBeveled(mPtr
->tfield
, False
);
894 WMRealizeWidget(mPtr
->tfield
);
896 mPtr
->tdelegate
= wmalloc(sizeof(WMTextFieldDelegate
));
897 memcpy(mPtr
->tdelegate
, &textFieldDelegate
, sizeof(WMTextFieldDelegate
));
899 mPtr
->tdelegate
->data
= mPtr
;
901 WMSetTextFieldDelegate(mPtr
->tfield
, mPtr
->tdelegate
);
905 WMSetTextFieldText(tf
, item
->label
);
906 WMSelectTextFieldRange(tf
, wmkrange(0, strlen(item
->label
)));
907 WMResizeWidget(tf
, mPtr
->view
->size
.width
, mPtr
->itemHeight
);
908 WMMoveWidget(tf
, 0, item
->view
->pos
.y
);
910 WMSetFocusToWidget(tf
);
912 mPtr
->flags
.isEditing
= 1;
915 /* -------------------------------------------------- */
917 static void slideWindow(Display
* dpy
, Window win
, int srcX
, int srcY
, int dstX
, int dstY
)
923 iterations
= WMIN(25, WMAX(abs(dstX
- srcX
), abs(dstY
- srcY
)));
928 dx
= (double)(dstX
- srcX
) / iterations
;
929 dy
= (double)(dstY
- srcY
) / iterations
;
931 for (i
= 0; i
<= iterations
; i
++) {
932 XMoveWindow(dpy
, win
, x
, y
);
942 static int errorHandler(Display
* d
, XErrorEvent
* ev
)
944 /* Parameter not used, but tell the compiler that it is ok */
952 static WEditMenu
*findMenuInWindow(Display
* dpy
, Window toplevel
, int x
, int y
)
960 int (*oldHandler
) (Display
*, XErrorEvent
*);
962 view
= W_GetViewForXWindow(dpy
, toplevel
);
963 if (view
&& view
->self
&& WMWidgetClass(view
->self
) == EditMenuClass
) {
964 menu
= (WEditMenu
*) view
->self
;
965 if (menu
->flags
.acceptsDrop
) {
970 if (!XQueryTree(dpy
, toplevel
, &foo
, &bar
, &children
, &nchildren
) || children
== NULL
) {
974 oldHandler
= XSetErrorHandler(errorHandler
);
976 /* first window that contains the point is the one */
977 for (i
= nchildren
- 1; i
>= 0; i
--) {
978 XWindowAttributes attr
;
980 if (XGetWindowAttributes(dpy
, children
[i
], &attr
)
981 && attr
.map_state
== IsViewable
982 && x
>= attr
.x
&& y
>= attr
.y
&& x
< attr
.x
+ attr
.width
&& y
< attr
.y
+ attr
.height
) {
987 menu
= findMenuInWindow(dpy
, tmp
, x
- attr
.x
, y
- attr
.y
);
995 XSetErrorHandler(oldHandler
);
1001 static void handleDragOver(WEditMenu
* menu
, WMView
* view
, WEditMenuItem
* item
, int x
, int y
)
1003 WMScreen
*scr
= W_VIEW_SCREEN(menu
->view
);
1008 XTranslateCoordinates(scr
->display
, W_VIEW_DRAWABLE(menu
->view
), scr
->rootWin
, 0, 0, &mx
, &my
, &blaw
);
1010 offs
= menu
->flags
.standalone
? 0 : 1;
1012 W_MoveView(view
, mx
+ offs
, y
);
1013 if (view
->size
.width
!= menu
->view
->size
.width
) {
1014 W_ResizeView(view
, menu
->view
->size
.width
- 2 * offs
, menu
->itemHeight
);
1015 W_ResizeView(item
->view
, menu
->view
->size
.width
- 2 * offs
, menu
->itemHeight
);
1019 static void handleItemDrop(WEditMenu
* menu
, WEditMenuItem
* item
, int x
, int y
)
1021 WMScreen
*scr
= W_VIEW_SCREEN(menu
->view
);
1026 XTranslateCoordinates(scr
->display
, W_VIEW_DRAWABLE(menu
->view
), scr
->rootWin
, 0, 0, &mx
, &my
, &blaw
);
1029 if (menu
->flags
.isTitled
) {
1030 index
-= menu
->titleHeight
;
1032 index
= (index
+ menu
->itemHeight
/ 2) / menu
->itemHeight
;
1036 if (menu
->flags
.isTitled
) {
1040 if (index
> WMGetArrayItemCount(menu
->items
)) {
1041 WMAddToArray(menu
->items
, item
);
1043 WMInsertInArray(menu
->items
, index
, item
);
1046 W_ReparentView(item
->view
, menu
->view
, 0, index
* menu
->itemHeight
);
1048 item
->parent
= menu
;
1049 if (item
->submenu
) {
1050 item
->submenu
->parent
= menu
;
1053 updateMenuContents(menu
);
1056 static void dragMenu(WEditMenu
* menu
)
1058 WMScreen
*scr
= W_VIEW_SCREEN(menu
->view
);
1065 XGetGeometry(scr
->display
, W_VIEW_DRAWABLE(menu
->view
), &blaw
, &dx
, &dy
, &blau
, &blau
, &blau
, &blau
);
1067 XTranslateCoordinates(scr
->display
, W_VIEW_DRAWABLE(menu
->view
), scr
->rootWin
, dx
, dy
, &dx
, &dy
, &blaw
);
1069 dx
= menu
->dragX
- dx
;
1070 dy
= menu
->dragY
- dy
;
1072 XGrabPointer(scr
->display
, scr
->rootWin
, False
,
1073 ButtonReleaseMask
| ButtonMotionMask
,
1074 GrabModeAsync
, GrabModeAsync
, None
, scr
->defaultCursor
, CurrentTime
);
1077 WTearOffEditMenu(menu
->parent
, menu
);
1080 WMNextEvent(scr
->display
, &ev
);
1088 while (XCheckMaskEvent(scr
->display
, ButtonMotionMask
, &ev
)) ;
1090 WMMoveWidget(menu
, ev
.xmotion
.x_root
- dx
, ev
.xmotion
.y_root
- dy
);
1099 XUngrabPointer(scr
->display
, CurrentTime
);
1102 static WEditMenuItem
*duplicateItem(WEditMenuItem
* item
)
1104 WEditMenuItem
*nitem
;
1106 nitem
= WCreateEditMenuItem(item
->parent
, item
->label
, False
);
1108 nitem
->pixmap
= WMRetainPixmap(item
->pixmap
);
1113 static WEditMenu
*duplicateMenu(WEditMenu
* menu
)
1116 WEditMenuItem
*item
;
1117 WMArrayIterator iter
;
1118 Bool first
= menu
->flags
.isTitled
;
1120 nmenu
= WCreateEditMenu(WMWidgetScreen(menu
), WGetEditMenuTitle(menu
));
1122 memcpy(&nmenu
->flags
, &menu
->flags
, sizeof(menu
->flags
));
1123 nmenu
->delegate
= menu
->delegate
;
1125 WM_ITERATE_ARRAY(menu
->items
, item
, iter
) {
1126 WEditMenuItem
*nitem
;
1133 nitem
= WAddMenuItemWithTitle(nmenu
, item
->label
);
1135 WSetEditMenuItemImage(nitem
, item
->pixmap
);
1137 if (menu
->delegate
&& menu
->delegate
->itemCloned
) {
1138 (*menu
->delegate
->itemCloned
) (menu
->delegate
, menu
, item
, nitem
);
1142 WMRealizeWidget(nmenu
);
1147 static void dragItem(WEditMenu
* menu
, WEditMenuItem
* item
, Bool copy
)
1149 static XColor black
= { 0, 0, 0, 0, DoRed
| DoGreen
| DoBlue
, 0 };
1150 static XColor green
= { 0x0045b045, 0x4500, 0xb000, 0x4500, DoRed
| DoGreen
| DoBlue
, 0 };
1151 static XColor back
= { 0, 0xffff, 0xffff, 0xffff, DoRed
| DoGreen
| DoBlue
, 0 };
1152 Display
*dpy
= W_VIEW_DISPLAY(menu
->view
);
1153 WMScreen
*scr
= W_VIEW_SCREEN(menu
->view
);
1163 Bool enteredMenu
= False
;
1164 WMSize oldSize
= item
->view
->size
;
1165 WEditMenuItem
*dritem
= item
;
1166 WEditMenu
*dmenu
= NULL
;
1168 if (item
->flags
.isTitle
) {
1169 WMRaiseWidget(menu
);
1176 selectItem(menu
, NULL
);
1180 XTranslateCoordinates(dpy
, W_VIEW_DRAWABLE(item
->view
), win
, 0, 0, &orix
, &oriy
, &blaw
);
1182 dview
= W_CreateUnmanagedTopView(scr
);
1183 W_SetViewBackgroundColor(dview
, scr
->black
);
1184 W_ResizeView(dview
, W_VIEW_WIDTH(item
->view
), W_VIEW_HEIGHT(item
->view
));
1185 W_MoveView(dview
, orix
, oriy
);
1186 W_RealizeView(dview
);
1188 if (menu
->flags
.isFactory
|| copy
) {
1189 dritem
= duplicateItem(item
);
1191 W_ReparentView(dritem
->view
, dview
, 0, 0);
1192 WMResizeWidget(dritem
, oldSize
.width
, oldSize
.height
);
1193 WMRealizeWidget(dritem
);
1194 WMMapWidget(dritem
);
1196 W_ReparentView(item
->view
, dview
, 0, 0);
1201 dx
= menu
->dragX
- orix
;
1202 dy
= menu
->dragY
- oriy
;
1204 XGrabPointer(dpy
, scr
->rootWin
, False
,
1205 ButtonPressMask
| ButtonReleaseMask
| ButtonMotionMask
,
1206 GrabModeAsync
, GrabModeAsync
, None
, scr
->defaultCursor
, CurrentTime
);
1208 if (menu
->flags
.acceptsDrop
)
1209 XRecolorCursor(dpy
, scr
->defaultCursor
, &green
, &back
);
1214 WMNextEvent(dpy
, &ev
);
1218 while (XCheckMaskEvent(dpy
, ButtonMotionMask
, &ev
)) ;
1220 XQueryPointer(dpy
, win
, &blaw
, &blaw
, &blai
, &blai
, &x
, &y
, &blau
);
1222 dmenu
= findMenuInWindow(dpy
, win
, x
, y
);
1225 handleDragOver(dmenu
, dview
, dritem
, x
- dx
, y
- dy
);
1228 XRecolorCursor(dpy
, scr
->defaultCursor
, &green
, &back
);
1232 W_ResizeView(dview
, oldSize
.width
, oldSize
.height
);
1233 W_ResizeView(dritem
->view
, oldSize
.width
, oldSize
.height
);
1234 enteredMenu
= False
;
1235 XRecolorCursor(dpy
, scr
->defaultCursor
, &black
, &back
);
1237 W_MoveView(dview
, x
- dx
, y
- dy
);
1251 XRecolorCursor(dpy
, scr
->defaultCursor
, &black
, &back
);
1253 XUngrabPointer(dpy
, CurrentTime
);
1258 if (!menu
->flags
.isFactory
&& !copy
) {
1260 if (menu
->delegate
&& menu
->delegate
->shouldRemoveItem
) {
1261 rem
= (*menu
->delegate
->shouldRemoveItem
) (menu
->delegate
, menu
, item
);
1266 if (!rem
|| menu
->flags
.isFactory
|| copy
) {
1267 slideWindow(dpy
, W_VIEW_DRAWABLE(dview
), x
- dx
, y
- dy
, orix
, oriy
);
1269 if (!menu
->flags
.isFactory
&& !copy
) {
1270 WRemoveEditMenuItem(menu
, dritem
);
1271 handleItemDrop(dmenu
? dmenu
: menu
, dritem
, orix
, oriy
);
1274 WRemoveEditMenuItem(menu
, dritem
);
1277 WRemoveEditMenuItem(menu
, dritem
);
1279 if (menu
->delegate
&& menu
->delegate
->itemCloned
&& (menu
->flags
.isFactory
|| copy
)) {
1280 (*menu
->delegate
->itemCloned
) (menu
->delegate
, menu
, item
, dritem
);
1283 handleItemDrop(dmenu
, dritem
, x
- dy
, y
- dy
);
1285 if (item
->submenu
&& (menu
->flags
.isFactory
|| copy
)) {
1288 submenu
= duplicateMenu(item
->submenu
);
1289 WSetEditMenuSubmenu(dmenu
, dritem
, submenu
);
1293 /* can't destroy now because we're being called from
1294 * the event handler of the item. and destroying now,
1295 * would mean destroying the item too in some cases.
1297 WMAddIdleHandler((WMCallback
*) W_DestroyView
, dview
);
1300 static void handleItemClick(XEvent
* event
, void *data
)
1302 WEditMenuItem
*item
= (WEditMenuItem
*) data
;
1303 WEditMenu
*menu
= item
->parent
;
1305 switch (event
->type
) {
1307 selectItem(menu
, item
);
1309 if (WMIsDoubleClick(event
)) {
1310 editItemLabel(item
);
1313 menu
->flags
.isDragging
= 1;
1314 menu
->dragX
= event
->xbutton
.x_root
;
1315 menu
->dragY
= event
->xbutton
.y_root
;
1319 menu
->flags
.isDragging
= 0;
1323 if (menu
->flags
.isDragging
) {
1324 if (abs(event
->xbutton
.x_root
- menu
->dragX
) > 5
1325 || abs(event
->xbutton
.y_root
- menu
->dragY
) > 5) {
1326 menu
->flags
.isDragging
= 0;
1327 dragItem(menu
, item
, event
->xbutton
.state
& ControlMask
);
1334 static void destroyEditMenu(WEditMenu
* mPtr
)
1336 WMRemoveNotificationObserver(mPtr
);
1338 WMFreeArray(mPtr
->items
);
1340 wfree(mPtr
->tdelegate
);