7 typedef struct ItemList
{
9 struct ItemList
*nextPtr
;
10 unsigned int disabled
:1;
14 typedef struct W_PopUpButton
{
26 short selectedItemIndex
;
28 short highlightedItem
;
30 ItemList
*selectedItem
; /* selected item if it is a menu btn */
32 WMView
*menuView
; /* override redirect popup menu */
34 WMHandlerID timer
; /* for autoscroll */
37 int scrollStartY
; /* for autoscroll */
40 unsigned int pullsDown
:1;
42 unsigned int configured
:1;
44 unsigned int insideMenu
:1;
46 unsigned int enabled
:1;
52 #define MENU_BLINK_DELAY 60000
53 #define MENU_BLINK_COUNT 2
55 #define SCROLL_DELAY 10
58 #define DEFAULT_WIDTH 60
59 #define DEFAULT_HEIGHT 20
60 #define DEFAULT_CAPTION ""
63 static void destroyPopUpButton(PopUpButton
*bPtr
);
64 static void paintPopUpButton(PopUpButton
*bPtr
);
66 static void handleEvents(XEvent
*event
, void *data
);
67 static void handleActionEvents(XEvent
*event
, void *data
);
69 static void resizeMenu(PopUpButton
*bPtr
);
73 WMCreatePopUpButton(WMWidget
*parent
)
76 W_Screen
*scr
= W_VIEW(parent
)->screen
;
79 bPtr
= wmalloc(sizeof(PopUpButton
));
80 memset(bPtr
, 0, sizeof(PopUpButton
));
82 bPtr
->widgetClass
= WC_PopUpButton
;
84 bPtr
->view
= W_CreateView(W_VIEW(parent
));
89 bPtr
->view
->self
= bPtr
;
91 WMCreateEventHandler(bPtr
->view
, ExposureMask
|StructureNotifyMask
92 |ClientMessageMask
, handleEvents
, bPtr
);
95 W_ResizeView(bPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
96 bPtr
->caption
= wstrdup(DEFAULT_CAPTION
);
98 WMCreateEventHandler(bPtr
->view
, ButtonPressMask
|ButtonReleaseMask
,
99 handleActionEvents
, bPtr
);
101 bPtr
->flags
.enabled
= 1;
103 bPtr
->menuView
= W_CreateTopView(scr
);
104 bPtr
->menuView
->attribs
.override_redirect
= True
;
105 bPtr
->menuView
->attribFlags
|= CWOverrideRedirect
;
107 W_ResizeView(bPtr
->menuView
, bPtr
->view
->size
.width
, 1);
109 WMCreateEventHandler(bPtr
->menuView
, ButtonPressMask
|ButtonReleaseMask
110 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
111 |ExposureMask
, handleActionEvents
, bPtr
);
118 WMSetPopUpButtonAction(WMPopUpButton
*bPtr
, WMAction
*action
, void *clientData
)
120 CHECK_CLASS(bPtr
, WC_PopUpButton
);
122 bPtr
->action
= action
;
124 bPtr
->clientData
= clientData
;
129 WMAddPopUpButtonItem(WMPopUpButton
*bPtr
, char *title
)
131 ItemList
*itemPtr
, *tmp
;
133 CHECK_CLASS(bPtr
, WC_PopUpButton
);
135 itemPtr
= wmalloc(sizeof(ItemList
));
136 memset(itemPtr
, 0, sizeof(ItemList
));
137 itemPtr
->text
= wstrdup(title
);
139 /* append item to list */
142 bPtr
->items
= itemPtr
;
144 while (tmp
->nextPtr
!=NULL
)
146 tmp
->nextPtr
= itemPtr
;
151 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
157 WMInsertPopUpButtonItem(WMPopUpButton
*bPtr
, int index
, char *title
)
161 CHECK_CLASS(bPtr
, WC_PopUpButton
);
165 if (index
>= bPtr
->itemCount
) {
166 WMAddPopUpButtonItem(bPtr
, title
);
170 itemPtr
= wmalloc(sizeof(ItemList
));
171 memset(itemPtr
, 0, sizeof(ItemList
));
172 itemPtr
->text
= wstrdup(title
);
175 itemPtr
->nextPtr
= bPtr
->items
;
176 bPtr
->items
= itemPtr
;
182 /* insert item in list */
186 itemPtr
->nextPtr
= tmp
->nextPtr
;
187 tmp
->nextPtr
= itemPtr
;
192 /* if there is an selected item, update it's index to match the new
194 if (index
< bPtr
->selectedItemIndex
)
195 bPtr
->selectedItemIndex
++;
197 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
203 WMRemovePopUpButtonItem(WMPopUpButton
*bPtr
, int index
)
207 CHECK_CLASS(bPtr
, WC_PopUpButton
);
209 if (index
< 0 || index
>= bPtr
->itemCount
)
214 wfree(bPtr
->items
->text
);
215 tmp
= bPtr
->items
->nextPtr
;
225 next
= tmp
->nextPtr
->nextPtr
;
227 wfree(tmp
->nextPtr
->text
);
235 if (bPtr
->selectedItem
!=NULL
&& !bPtr
->flags
.pullsDown
) {
236 if (index
< bPtr
->selectedItemIndex
)
237 bPtr
->selectedItemIndex
--;
238 else if (index
== bPtr
->selectedItemIndex
) {
239 /* reselect first item if the removed item is the
241 bPtr
->selectedItem
= bPtr
->items
;
242 bPtr
->selectedItemIndex
= 0;
243 if (bPtr
->view
->flags
.mapped
)
244 paintPopUpButton(bPtr
);
248 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
254 WMSetPopUpButtonEnabled(WMPopUpButton
*bPtr
, Bool flag
)
256 bPtr
->flags
.enabled
= flag
;
257 if (bPtr
->view
->flags
.mapped
)
258 paintPopUpButton(bPtr
);
263 WMGetPopUpButtonEnabled(WMPopUpButton
*bPtr
)
265 return bPtr
->flags
.enabled
;
270 WMSetPopUpButtonSelectedItem(WMPopUpButton
*bPtr
, int index
)
272 ItemList
*itemPtr
= bPtr
->items
;
276 bPtr
->selectedItem
= NULL
;
277 if (bPtr
->view
->flags
.mapped
)
278 paintPopUpButton(bPtr
);
283 itemPtr
= itemPtr
->nextPtr
;
285 bPtr
->selectedItem
= itemPtr
;
286 bPtr
->selectedItemIndex
= index
;
288 if (bPtr
->view
->flags
.mapped
)
289 paintPopUpButton(bPtr
);
293 WMGetPopUpButtonSelectedItem(WMPopUpButton
*bPtr
)
295 if (!bPtr
->flags
.pullsDown
&& bPtr
->selectedItem
==NULL
)
298 return bPtr
->selectedItemIndex
;
303 WMSetPopUpButtonText(WMPopUpButton
*bPtr
, char *text
)
306 wfree(bPtr
->caption
);
308 bPtr
->caption
= wstrdup(text
);
310 bPtr
->caption
= NULL
;
311 if (bPtr
->view
->flags
.realized
) {
312 if (bPtr
->flags
.pullsDown
|| bPtr
->selectedItemIndex
< 0) {
313 paintPopUpButton(bPtr
);
321 WMSetPopUpButtonItemEnabled(WMPopUpButton
*bPtr
, int index
, Bool flag
)
324 ItemList
*item
= bPtr
->items
;
326 if (index
< 0 || index
>= bPtr
->itemCount
)
329 for (i
= 0; i
<index
; i
++)
332 item
->disabled
= !flag
;
337 WMGetPopUpButtonItemEnabled(WMPopUpButton
*bPtr
, int index
)
340 ItemList
*item
= bPtr
->items
;
342 if (index
< 0 || index
>= bPtr
->itemCount
)
345 for (i
= 0; i
<index
; i
++)
348 return !item
->disabled
;
353 WMSetPopUpButtonPullsDown(WMPopUpButton
*bPtr
, Bool flag
)
355 bPtr
->flags
.pullsDown
= flag
;
357 /* This code causes bugs. It should not select any item,
358 * since it was not asked to. -Dan
359 bPtr->selectedItem = bPtr->items;
360 if (bPtr->selectedItem)
361 bPtr->selectedItemIndex = 0;
363 bPtr->selectedItemIndex = -1;
365 bPtr
->selectedItem
= NULL
;
366 /* the drawing routine, still draws things wrong if we put
367 * the index to -1 (i.e. not selected).
368 * Find out why. -Dan */
369 bPtr
->selectedItemIndex
= 0;
371 bPtr
->selectedItemIndex
= -1;
373 if (bPtr
->view
->flags
.mapped
)
374 paintPopUpButton(bPtr
);
379 WMGetPopUpButtonNumberOfItems(WMPopUpButton
*bPtr
)
381 return bPtr
->itemCount
;
386 WMGetPopUpButtonItem(WMPopUpButton
*bPtr
, int index
)
388 ItemList
*itemPtr
= bPtr
->items
;
390 if ((index
< 0) || (index
>= bPtr
->itemCount
))
394 itemPtr
= itemPtr
->nextPtr
;
396 return itemPtr
->text
;
401 paintPopUpButton(PopUpButton
*bPtr
)
403 W_Screen
*scr
= bPtr
->view
->screen
;
408 if (bPtr
->flags
.pullsDown
) {
409 caption
= bPtr
->caption
;
411 if (bPtr
->selectedItem
== NULL
) {
412 /* if no item selected, show the caption */
413 caption
= bPtr
->caption
;
415 caption
= bPtr
->selectedItem
->text
;
419 pixmap
= XCreatePixmap(scr
->display
, bPtr
->view
->window
,
420 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
,
422 XFillRectangle(scr
->display
, pixmap
, WMColorGC(scr
->gray
), 0, 0,
423 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
);
425 W_DrawRelief(scr
, pixmap
, 0, 0, bPtr
->view
->size
.width
,
426 bPtr
->view
->size
.height
, WRRaised
);
429 W_PaintText(bPtr
->view
, pixmap
, scr
->normalFont
, 6,
430 (bPtr
->view
->size
.height
-WMFontHeight(scr
->normalFont
))/2,
431 bPtr
->view
->size
.width
, WALeft
,
432 bPtr
->flags
.enabled
? WMColorGC(scr
->black
) : WMColorGC(scr
->darkGray
),
433 False
, caption
, strlen(caption
));
436 if (bPtr
->flags
.pullsDown
) {
437 XCopyArea(scr
->display
, scr
->pullDownIndicator
->pixmap
,
438 pixmap
, scr
->copyGC
, 0, 0, scr
->pullDownIndicator
->width
,
439 scr
->pullDownIndicator
->height
,
440 bPtr
->view
->size
.width
-scr
->pullDownIndicator
->width
-4,
441 (bPtr
->view
->size
.height
-scr
->pullDownIndicator
->height
)/2);
445 x
= bPtr
->view
->size
.width
- scr
->popUpIndicator
->width
- 4;
446 y
= (bPtr
->view
->size
.height
-scr
->popUpIndicator
->height
)/2;
448 XSetClipOrigin(scr
->display
, scr
->clipGC
, x
, y
);
449 XSetClipMask(scr
->display
, scr
->clipGC
, scr
->popUpIndicator
->mask
);
450 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
, pixmap
,
451 scr
->clipGC
, 0, 0, scr
->popUpIndicator
->width
,
452 scr
->popUpIndicator
->height
, x
, y
);
455 XCopyArea(scr
->display
, pixmap
, bPtr
->view
->window
, scr
->copyGC
, 0, 0,
456 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
, 0, 0);
458 XFreePixmap(scr
->display
, pixmap
);
464 handleEvents(XEvent
*event
, void *data
)
466 PopUpButton
*bPtr
= (PopUpButton
*)data
;
468 CHECK_CLASS(data
, WC_PopUpButton
);
471 switch (event
->type
) {
473 if (event
->xexpose
.count
!=0)
475 paintPopUpButton(bPtr
);
479 destroyPopUpButton(bPtr
);
487 paintMenuEntry(PopUpButton
*bPtr
, int index
, int highlight
)
489 W_Screen
*scr
= bPtr
->view
->screen
;
493 int width
, height
, itemHeight
;
495 itemHeight
= bPtr
->view
->size
.height
;
496 width
= bPtr
->view
->size
.width
;
497 height
= itemHeight
* bPtr
->itemCount
;
498 yo
= (itemHeight
- WMFontHeight(scr
->normalFont
))/2;
501 XClearArea(scr
->display
, bPtr
->menuView
->window
, 0, index
*itemHeight
,
502 width
, itemHeight
, False
);
504 } else if (index
< 0 && bPtr
->flags
.pullsDown
) {
508 XFillRectangle(scr
->display
, bPtr
->menuView
->window
, WMColorGC(scr
->white
),
509 1, index
*itemHeight
+1, width
-3, itemHeight
-3);
511 itemPtr
= bPtr
->items
;
512 for (i
= 0; i
< index
; i
++)
513 itemPtr
= itemPtr
->nextPtr
;
515 W_DrawRelief(scr
, bPtr
->menuView
->window
, 0, index
*itemHeight
,
516 width
, itemHeight
, WRRaised
);
518 W_PaintText(bPtr
->menuView
, bPtr
->menuView
->window
, scr
->normalFont
, 6,
519 index
*itemHeight
+ yo
, width
, WALeft
, WMColorGC(scr
->black
), False
,
520 itemPtr
->text
, strlen(itemPtr
->text
));
522 if (!bPtr
->flags
.pullsDown
&& index
== bPtr
->selectedItemIndex
) {
523 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
,
524 bPtr
->menuView
->window
, scr
->copyGC
, 0, 0,
525 scr
->popUpIndicator
->width
, scr
->popUpIndicator
->height
,
526 width
-scr
->popUpIndicator
->width
-4,
527 i
*itemHeight
+(itemHeight
-scr
->popUpIndicator
->height
)/2);
533 makeMenuPixmap(PopUpButton
*bPtr
)
536 W_Screen
*scr
= bPtr
->view
->screen
;
540 int width
, height
, itemHeight
;
542 itemHeight
= bPtr
->view
->size
.height
;
543 width
= bPtr
->view
->size
.width
;
544 height
= itemHeight
* bPtr
->itemCount
;
545 yo
= (itemHeight
- WMFontHeight(scr
->normalFont
))/2;
547 pixmap
= XCreatePixmap(scr
->display
, bPtr
->view
->window
, width
, height
,
550 XFillRectangle(scr
->display
, pixmap
, WMColorGC(scr
->gray
), 0, 0, width
, height
);
552 itemPtr
= bPtr
->items
;
553 for (i
= 0; i
< bPtr
->itemCount
; i
++) {
556 W_DrawRelief(scr
, pixmap
, 0, i
*itemHeight
, width
, itemHeight
,
559 if (itemPtr
->disabled
)
560 gc
= WMColorGC(scr
->darkGray
);
562 gc
= WMColorGC(scr
->black
);
564 W_PaintText(bPtr
->menuView
, pixmap
, scr
->normalFont
, 6,
565 i
*itemHeight
+ yo
, width
, WALeft
, gc
, False
,
566 itemPtr
->text
, strlen(itemPtr
->text
));
568 if (!bPtr
->flags
.pullsDown
&& i
== bPtr
->selectedItemIndex
) {
569 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
, pixmap
,
570 scr
->copyGC
, 0, 0, scr
->popUpIndicator
->width
,
571 scr
->popUpIndicator
->height
,
572 width
-scr
->popUpIndicator
->width
-4,
573 i
*itemHeight
+(itemHeight
-scr
->popUpIndicator
->height
)/2);
575 itemPtr
= itemPtr
->nextPtr
;
583 resizeMenu(PopUpButton
*bPtr
)
587 height
= bPtr
->itemCount
* bPtr
->view
->size
.height
;
589 W_ResizeView(bPtr
->menuView
, bPtr
->view
->size
.width
, height
);
594 popUpMenu(PopUpButton
*bPtr
)
596 W_Screen
*scr
= bPtr
->view
->screen
;
600 if (!bPtr
->menuView
->flags
.realized
) {
601 W_RealizeView(bPtr
->menuView
);
605 if (bPtr
->itemCount
< 1)
608 XTranslateCoordinates(scr
->display
, bPtr
->view
->window
, scr
->rootWin
,
609 0, 0, &x
, &y
, &dummyW
);
611 if (bPtr
->flags
.pullsDown
) {
612 y
+= bPtr
->view
->size
.height
;
614 y
-= bPtr
->view
->size
.height
*bPtr
->selectedItemIndex
;
616 W_MoveView(bPtr
->menuView
, x
, y
);
618 XSetWindowBackgroundPixmap(scr
->display
, bPtr
->menuView
->window
,
619 makeMenuPixmap(bPtr
));
620 XClearWindow(scr
->display
, bPtr
->menuView
->window
);
622 W_MapView(bPtr
->menuView
);
624 bPtr
->highlightedItem
= 0;
625 if (!bPtr
->flags
.pullsDown
&& bPtr
->selectedItem
!= NULL
)
626 paintMenuEntry(bPtr
, bPtr
->selectedItemIndex
, True
);
631 popDownMenu(PopUpButton
*bPtr
)
633 W_UnmapView(bPtr
->menuView
);
635 /* free the background pixmap used to draw the menu contents */
636 XSetWindowBackgroundPixmap(bPtr
->view
->screen
->display
,
637 bPtr
->menuView
->window
, None
);
642 itemIsEnabled(PopUpButton
*bPtr
, int index
)
644 ItemList
*item
= bPtr
->items
;
646 while (index
-- > 0 && item
)
647 item
= item
->nextPtr
;
649 return item
? !item
->disabled
: False
;
654 autoScroll(void *data
)
656 PopUpButton
*bPtr
= (PopUpButton
*)data
;
657 int scrHeight
= WMWidgetScreen(bPtr
)->rootView
->size
.height
;
662 if (bPtr
->scrollStartY
>= scrHeight
-1
663 && bPtr
->menuView
->pos
.y
+bPtr
->menuView
->size
.height
>= scrHeight
-1) {
666 if (bPtr
->menuView
->pos
.y
+bPtr
->menuView
->size
.height
-5
669 - (bPtr
->menuView
->pos
.y
+bPtr
->menuView
->size
.height
);
673 } else if (bPtr
->scrollStartY
<= 1 && bPtr
->menuView
->pos
.y
< 1) {
676 if (bPtr
->menuView
->pos
.y
+5 > 1)
677 dy
= 1 - bPtr
->menuView
->pos
.y
;
685 W_MoveView(bPtr
->menuView
, bPtr
->menuView
->pos
.x
,
686 bPtr
->menuView
->pos
.y
+ dy
);
688 oldItem
= bPtr
->highlightedItem
;
689 bPtr
->highlightedItem
= (bPtr
->scrollStartY
- bPtr
->menuView
->pos
.y
)
690 / bPtr
->view
->size
.height
;
692 if (oldItem
!=bPtr
->highlightedItem
) {
693 paintMenuEntry(bPtr
, oldItem
, False
);
694 paintMenuEntry(bPtr
, bPtr
->highlightedItem
,
695 itemIsEnabled(bPtr
, bPtr
->highlightedItem
));
698 bPtr
->timer
= WMAddTimerHandler(SCROLL_DELAY
, autoScroll
, bPtr
);
706 handleActionEvents(XEvent
*event
, void *data
)
708 PopUpButton
*bPtr
= (PopUpButton
*)data
;
710 int scrHeight
= WMWidgetScreen(bPtr
)->rootView
->size
.height
;
712 CHECK_CLASS(data
, WC_PopUpButton
);
714 if (bPtr
->itemCount
< 1)
717 switch (event
->type
) {
718 /* called for menuView */
720 paintMenuEntry(bPtr
, bPtr
->highlightedItem
,
721 itemIsEnabled(bPtr
, bPtr
->highlightedItem
));
725 bPtr
->flags
.insideMenu
= 0;
726 if (bPtr
->menuView
->flags
.mapped
)
727 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
728 bPtr
->highlightedItem
= -1;
732 bPtr
->flags
.insideMenu
= 1;
736 if (bPtr
->flags
.insideMenu
) {
737 oldItem
= bPtr
->highlightedItem
;
738 bPtr
->highlightedItem
= event
->xmotion
.y
/ bPtr
->view
->size
.height
;
739 if (oldItem
!=bPtr
->highlightedItem
) {
740 paintMenuEntry(bPtr
, oldItem
, False
);
741 paintMenuEntry(bPtr
, bPtr
->highlightedItem
,
742 itemIsEnabled(bPtr
, bPtr
->highlightedItem
));
745 if (event
->xmotion
.y_root
>= scrHeight
-1
746 || event
->xmotion
.y_root
<= 1) {
747 bPtr
->scrollStartY
= event
->xmotion
.y_root
;
750 } else if (bPtr
->timer
) {
751 WMDeleteTimerHandler(bPtr
->timer
);
757 /* called for bPtr->view */
759 if (!bPtr
->flags
.enabled
)
763 if (!bPtr
->flags
.pullsDown
) {
764 bPtr
->highlightedItem
= bPtr
->selectedItemIndex
;
765 bPtr
->flags
.insideMenu
= 1;
767 bPtr
->highlightedItem
= -1;
768 bPtr
->flags
.insideMenu
= 0;
770 XGrabPointer(bPtr
->view
->screen
->display
, bPtr
->menuView
->window
,
771 False
, ButtonReleaseMask
|ButtonMotionMask
|EnterWindowMask
772 |LeaveWindowMask
, GrabModeAsync
, GrabModeAsync
,
773 None
, None
, CurrentTime
);
777 XUngrabPointer(bPtr
->view
->screen
->display
, event
->xbutton
.time
);
778 if (!bPtr
->flags
.pullsDown
)
782 WMDeleteTimerHandler(bPtr
->timer
);
786 if (bPtr
->flags
.insideMenu
&& bPtr
->highlightedItem
>=0) {
787 if (itemIsEnabled(bPtr
, bPtr
->highlightedItem
)) {
789 WMSetPopUpButtonSelectedItem(bPtr
, bPtr
->highlightedItem
);
791 if (bPtr
->flags
.pullsDown
) {
792 for (i
=0; i
<MENU_BLINK_COUNT
; i
++) {
793 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
794 XSync(bPtr
->view
->screen
->display
, 0);
795 wusleep(MENU_BLINK_DELAY
);
796 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, True
);
797 XSync(bPtr
->view
->screen
->display
, 0);
798 wusleep(MENU_BLINK_DELAY
);
801 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
804 (*bPtr
->action
)(bPtr
, bPtr
->clientData
);
807 if (bPtr
->menuView
->flags
.mapped
)
816 destroyPopUpButton(PopUpButton
*bPtr
)
818 ItemList
*itemPtr
, *tmp
;
821 WMDeleteTimerHandler(bPtr
->timer
);
824 itemPtr
= bPtr
->items
;
825 while (itemPtr
!=NULL
) {
826 wfree(itemPtr
->text
);
827 tmp
= itemPtr
->nextPtr
;
833 wfree(bPtr
->caption
);
835 /* have to destroy explicitly because the popup is a toplevel */
836 W_DestroyView(bPtr
->menuView
);