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 W_ViewProcedureTable _PopUpButtonViewProcedures
= {
65 #define DEFAULT_WIDTH 60
66 #define DEFAULT_HEIGHT 20
67 #define DEFAULT_CAPTION ""
70 static void destroyPopUpButton(PopUpButton
*bPtr
);
71 static void paintPopUpButton(PopUpButton
*bPtr
);
73 static void handleEvents(XEvent
*event
, void *data
);
74 static void handleActionEvents(XEvent
*event
, void *data
);
76 static void resizeMenu(PopUpButton
*bPtr
);
80 WMCreatePopUpButton(WMWidget
*parent
)
83 W_Screen
*scr
= W_VIEW(parent
)->screen
;
86 bPtr
= wmalloc(sizeof(PopUpButton
));
87 memset(bPtr
, 0, sizeof(PopUpButton
));
89 bPtr
->widgetClass
= WC_PopUpButton
;
91 bPtr
->view
= W_CreateView(W_VIEW(parent
));
96 bPtr
->view
->self
= bPtr
;
98 WMCreateEventHandler(bPtr
->view
, ExposureMask
|StructureNotifyMask
99 |ClientMessageMask
, handleEvents
, bPtr
);
102 W_ResizeView(bPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
103 bPtr
->caption
= wstrdup(DEFAULT_CAPTION
);
105 WMCreateEventHandler(bPtr
->view
, ButtonPressMask
|ButtonReleaseMask
,
106 handleActionEvents
, bPtr
);
108 bPtr
->flags
.enabled
= 1;
110 bPtr
->menuView
= W_CreateTopView(scr
);
111 bPtr
->menuView
->attribs
.override_redirect
= True
;
112 bPtr
->menuView
->attribFlags
|= CWOverrideRedirect
;
114 W_ResizeView(bPtr
->menuView
, bPtr
->view
->size
.width
, 1);
116 WMCreateEventHandler(bPtr
->menuView
, ButtonPressMask
|ButtonReleaseMask
117 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
118 |ExposureMask
, handleActionEvents
, bPtr
);
125 WMSetPopUpButtonAction(WMPopUpButton
*bPtr
, WMAction
*action
, void *clientData
)
127 CHECK_CLASS(bPtr
, WC_PopUpButton
);
129 bPtr
->action
= action
;
131 bPtr
->clientData
= clientData
;
136 WMAddPopUpButtonItem(WMPopUpButton
*bPtr
, char *title
)
138 ItemList
*itemPtr
, *tmp
;
140 CHECK_CLASS(bPtr
, WC_PopUpButton
);
142 itemPtr
= wmalloc(sizeof(ItemList
));
143 memset(itemPtr
, 0, sizeof(ItemList
));
144 itemPtr
->text
= wstrdup(title
);
146 /* append item to list */
149 bPtr
->items
= itemPtr
;
151 while (tmp
->nextPtr
!=NULL
)
153 tmp
->nextPtr
= itemPtr
;
158 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
164 WMInsertPopUpButtonItem(WMPopUpButton
*bPtr
, int index
, char *title
)
168 CHECK_CLASS(bPtr
, WC_PopUpButton
);
172 if (index
>= bPtr
->itemCount
) {
173 WMAddPopUpButtonItem(bPtr
, title
);
177 itemPtr
= wmalloc(sizeof(ItemList
));
178 memset(itemPtr
, 0, sizeof(ItemList
));
179 itemPtr
->text
= wstrdup(title
);
182 itemPtr
->nextPtr
= bPtr
->items
;
183 bPtr
->items
= itemPtr
;
189 /* insert item in list */
193 itemPtr
->nextPtr
= tmp
->nextPtr
;
194 tmp
->nextPtr
= itemPtr
;
199 /* if there is an selected item, update it's index to match the new
201 if (index
< bPtr
->selectedItemIndex
)
202 bPtr
->selectedItemIndex
++;
204 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
210 WMRemovePopUpButtonItem(WMPopUpButton
*bPtr
, int index
)
214 CHECK_CLASS(bPtr
, WC_PopUpButton
);
216 if (index
< 0 || index
>= bPtr
->itemCount
)
221 free(bPtr
->items
->text
);
222 tmp
= bPtr
->items
->nextPtr
;
232 next
= tmp
->nextPtr
->nextPtr
;
234 free(tmp
->nextPtr
->text
);
242 if (bPtr
->selectedItem
!=NULL
&& !bPtr
->flags
.pullsDown
) {
243 if (index
< bPtr
->selectedItemIndex
)
244 bPtr
->selectedItemIndex
--;
245 else if (index
== bPtr
->selectedItemIndex
) {
246 /* reselect first item if the removed item is the
248 bPtr
->selectedItem
= bPtr
->items
;
249 bPtr
->selectedItemIndex
= 0;
250 if (bPtr
->view
->flags
.mapped
)
251 paintPopUpButton(bPtr
);
255 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
261 WMSetPopUpButtonEnabled(WMPopUpButton
*bPtr
, Bool flag
)
263 bPtr
->flags
.enabled
= flag
;
264 if (bPtr
->view
->flags
.mapped
)
265 paintPopUpButton(bPtr
);
270 WMGetPopUpButtonEnabled(WMPopUpButton
*bPtr
)
272 return bPtr
->flags
.enabled
;
277 WMSetPopUpButtonSelectedItem(WMPopUpButton
*bPtr
, int index
)
279 ItemList
*itemPtr
= bPtr
->items
;
283 bPtr
->selectedItem
= NULL
;
284 if (bPtr
->view
->flags
.mapped
)
285 paintPopUpButton(bPtr
);
290 itemPtr
= itemPtr
->nextPtr
;
292 bPtr
->selectedItem
= itemPtr
;
293 bPtr
->selectedItemIndex
= index
;
295 if (bPtr
->view
->flags
.mapped
)
296 paintPopUpButton(bPtr
);
300 WMGetPopUpButtonSelectedItem(WMPopUpButton
*bPtr
)
302 if (!bPtr
->flags
.pullsDown
&& bPtr
->selectedItem
==NULL
)
305 return bPtr
->selectedItemIndex
;
310 WMSetPopUpButtonText(WMPopUpButton
*bPtr
, char *text
)
315 bPtr
->caption
= wstrdup(text
);
317 bPtr
->caption
= NULL
;
318 if (bPtr
->view
->flags
.realized
) {
319 if (bPtr
->flags
.pullsDown
|| bPtr
->selectedItemIndex
< 0) {
320 paintPopUpButton(bPtr
);
328 WMSetPopUpButtonItemEnabled(WMPopUpButton
*bPtr
, int index
, Bool flag
)
331 ItemList
*item
= bPtr
->items
;
333 if (index
< 0 || index
>= bPtr
->itemCount
)
336 for (i
= 0; i
<index
; i
++)
339 item
->disabled
= !flag
;
344 WMGetPopUpButtonItemEnabled(WMPopUpButton
*bPtr
, int index
)
347 ItemList
*item
= bPtr
->items
;
349 if (index
< 0 || index
>= bPtr
->itemCount
)
352 for (i
= 0; i
<index
; i
++)
355 return !item
->disabled
;
360 WMSetPopUpButtonPullsDown(WMPopUpButton
*bPtr
, Bool flag
)
362 bPtr
->flags
.pullsDown
= flag
;
364 /* This code causes bugs. It should not select any item,
365 * since it was not asked to. -Dan
366 bPtr->selectedItem = bPtr->items;
367 if (bPtr->selectedItem)
368 bPtr->selectedItemIndex = 0;
370 bPtr->selectedItemIndex = -1;
372 bPtr
->selectedItem
= NULL
;
373 /* the drawing routine, still draws things wrong if we put
374 * the index to -1 (i.e. not selected).
375 * Find out why. -Dan */
376 bPtr
->selectedItemIndex
= 0;
378 bPtr
->selectedItemIndex
= -1;
380 if (bPtr
->view
->flags
.mapped
)
381 paintPopUpButton(bPtr
);
386 WMGetPopUpButtonNumberOfItems(WMPopUpButton
*bPtr
)
388 return bPtr
->itemCount
;
393 WMGetPopUpButtonItem(WMPopUpButton
*bPtr
, int index
)
395 ItemList
*itemPtr
= bPtr
->items
;
397 if ((index
< 0) || (index
>= bPtr
->itemCount
))
401 itemPtr
= itemPtr
->nextPtr
;
403 return itemPtr
->text
;
408 paintPopUpButton(PopUpButton
*bPtr
)
410 W_Screen
*scr
= bPtr
->view
->screen
;
415 if (bPtr
->flags
.pullsDown
) {
416 caption
= bPtr
->caption
;
418 if (bPtr
->selectedItem
== NULL
) {
419 /* if no item selected, show the caption */
420 caption
= bPtr
->caption
;
422 caption
= bPtr
->selectedItem
->text
;
426 pixmap
= XCreatePixmap(scr
->display
, bPtr
->view
->window
,
427 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
,
429 XFillRectangle(scr
->display
, pixmap
, WMColorGC(scr
->gray
), 0, 0,
430 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
);
432 W_DrawRelief(scr
, pixmap
, 0, 0, bPtr
->view
->size
.width
,
433 bPtr
->view
->size
.height
, WRRaised
);
436 W_PaintText(bPtr
->view
, pixmap
, scr
->normalFont
, 6,
437 (bPtr
->view
->size
.height
-WMFontHeight(scr
->normalFont
))/2,
438 bPtr
->view
->size
.width
, WALeft
,
439 bPtr
->flags
.enabled
? WMColorGC(scr
->black
) : WMColorGC(scr
->darkGray
),
440 False
, caption
, strlen(caption
));
443 if (bPtr
->flags
.pullsDown
) {
444 XCopyArea(scr
->display
, scr
->pullDownIndicator
->pixmap
,
445 pixmap
, scr
->copyGC
, 0, 0, scr
->pullDownIndicator
->width
,
446 scr
->pullDownIndicator
->height
,
447 bPtr
->view
->size
.width
-scr
->pullDownIndicator
->width
-4,
448 (bPtr
->view
->size
.height
-scr
->pullDownIndicator
->height
)/2);
452 x
= bPtr
->view
->size
.width
- scr
->popUpIndicator
->width
- 4;
453 y
= (bPtr
->view
->size
.height
-scr
->popUpIndicator
->height
)/2;
455 XSetClipOrigin(scr
->display
, scr
->clipGC
, x
, y
);
456 XSetClipMask(scr
->display
, scr
->clipGC
, scr
->popUpIndicator
->mask
);
457 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
, pixmap
,
458 scr
->clipGC
, 0, 0, scr
->popUpIndicator
->width
,
459 scr
->popUpIndicator
->height
, x
, y
);
462 XCopyArea(scr
->display
, pixmap
, bPtr
->view
->window
, scr
->copyGC
, 0, 0,
463 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
, 0, 0);
465 XFreePixmap(scr
->display
, pixmap
);
471 handleEvents(XEvent
*event
, void *data
)
473 PopUpButton
*bPtr
= (PopUpButton
*)data
;
475 CHECK_CLASS(data
, WC_PopUpButton
);
478 switch (event
->type
) {
480 if (event
->xexpose
.count
!=0)
482 paintPopUpButton(bPtr
);
486 destroyPopUpButton(bPtr
);
494 paintMenuEntry(PopUpButton
*bPtr
, int index
, int highlight
)
496 W_Screen
*scr
= bPtr
->view
->screen
;
500 int width
, height
, itemHeight
;
502 itemHeight
= bPtr
->view
->size
.height
;
503 width
= bPtr
->view
->size
.width
;
504 height
= itemHeight
* bPtr
->itemCount
;
505 yo
= (itemHeight
- WMFontHeight(scr
->normalFont
))/2;
508 XClearArea(scr
->display
, bPtr
->menuView
->window
, 0, index
*itemHeight
,
509 width
, itemHeight
, False
);
511 } else if (index
< 0 && bPtr
->flags
.pullsDown
) {
515 XFillRectangle(scr
->display
, bPtr
->menuView
->window
, WMColorGC(scr
->white
),
516 1, index
*itemHeight
+1, width
-3, itemHeight
-3);
518 itemPtr
= bPtr
->items
;
519 for (i
= 0; i
< index
; i
++)
520 itemPtr
= itemPtr
->nextPtr
;
522 W_DrawRelief(scr
, bPtr
->menuView
->window
, 0, index
*itemHeight
,
523 width
, itemHeight
, WRRaised
);
525 W_PaintText(bPtr
->menuView
, bPtr
->menuView
->window
, scr
->normalFont
, 6,
526 index
*itemHeight
+ yo
, width
, WALeft
, WMColorGC(scr
->black
), False
,
527 itemPtr
->text
, strlen(itemPtr
->text
));
529 if (!bPtr
->flags
.pullsDown
&& index
== bPtr
->selectedItemIndex
) {
530 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
,
531 bPtr
->menuView
->window
, scr
->copyGC
, 0, 0,
532 scr
->popUpIndicator
->width
, scr
->popUpIndicator
->height
,
533 width
-scr
->popUpIndicator
->width
-4,
534 i
*itemHeight
+(itemHeight
-scr
->popUpIndicator
->height
)/2);
540 makeMenuPixmap(PopUpButton
*bPtr
)
543 W_Screen
*scr
= bPtr
->view
->screen
;
547 int width
, height
, itemHeight
;
549 itemHeight
= bPtr
->view
->size
.height
;
550 width
= bPtr
->view
->size
.width
;
551 height
= itemHeight
* bPtr
->itemCount
;
552 yo
= (itemHeight
- WMFontHeight(scr
->normalFont
))/2;
554 pixmap
= XCreatePixmap(scr
->display
, bPtr
->view
->window
, width
, height
,
557 XFillRectangle(scr
->display
, pixmap
, WMColorGC(scr
->gray
), 0, 0, width
, height
);
559 itemPtr
= bPtr
->items
;
560 for (i
= 0; i
< bPtr
->itemCount
; i
++) {
563 W_DrawRelief(scr
, pixmap
, 0, i
*itemHeight
, width
, itemHeight
,
566 if (itemPtr
->disabled
)
567 gc
= WMColorGC(scr
->darkGray
);
569 gc
= WMColorGC(scr
->black
);
571 W_PaintText(bPtr
->menuView
, pixmap
, scr
->normalFont
, 6,
572 i
*itemHeight
+ yo
, width
, WALeft
, gc
, False
,
573 itemPtr
->text
, strlen(itemPtr
->text
));
575 if (!bPtr
->flags
.pullsDown
&& i
== bPtr
->selectedItemIndex
) {
576 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
, pixmap
,
577 scr
->copyGC
, 0, 0, scr
->popUpIndicator
->width
,
578 scr
->popUpIndicator
->height
,
579 width
-scr
->popUpIndicator
->width
-4,
580 i
*itemHeight
+(itemHeight
-scr
->popUpIndicator
->height
)/2);
582 itemPtr
= itemPtr
->nextPtr
;
590 resizeMenu(PopUpButton
*bPtr
)
594 height
= bPtr
->itemCount
* bPtr
->view
->size
.height
;
596 W_ResizeView(bPtr
->menuView
, bPtr
->view
->size
.width
, height
);
601 popUpMenu(PopUpButton
*bPtr
)
603 W_Screen
*scr
= bPtr
->view
->screen
;
607 if (!bPtr
->menuView
->flags
.realized
) {
608 W_RealizeView(bPtr
->menuView
);
612 if (bPtr
->itemCount
< 1)
615 XTranslateCoordinates(scr
->display
, bPtr
->view
->window
, scr
->rootWin
,
616 0, 0, &x
, &y
, &dummyW
);
618 if (bPtr
->flags
.pullsDown
) {
619 y
+= bPtr
->view
->size
.height
;
621 y
-= bPtr
->view
->size
.height
*bPtr
->selectedItemIndex
;
623 W_MoveView(bPtr
->menuView
, x
, y
);
625 XSetWindowBackgroundPixmap(scr
->display
, bPtr
->menuView
->window
,
626 makeMenuPixmap(bPtr
));
627 XClearWindow(scr
->display
, bPtr
->menuView
->window
);
629 W_MapView(bPtr
->menuView
);
631 bPtr
->highlightedItem
= 0;
632 if (!bPtr
->flags
.pullsDown
&& bPtr
->selectedItem
!= NULL
)
633 paintMenuEntry(bPtr
, bPtr
->selectedItemIndex
, True
);
638 popDownMenu(PopUpButton
*bPtr
)
640 W_UnmapView(bPtr
->menuView
);
642 /* free the background pixmap used to draw the menu contents */
643 XSetWindowBackgroundPixmap(bPtr
->view
->screen
->display
,
644 bPtr
->menuView
->window
, None
);
649 itemIsEnabled(PopUpButton
*bPtr
, int index
)
651 ItemList
*item
= bPtr
->items
;
653 while (index
-- > 0 && item
)
654 item
= item
->nextPtr
;
656 return item
? !item
->disabled
: False
;
661 autoScroll(void *data
)
663 PopUpButton
*bPtr
= (PopUpButton
*)data
;
664 int scrHeight
= WMWidgetScreen(bPtr
)->rootView
->size
.height
;
669 if (bPtr
->scrollStartY
>= scrHeight
-1
670 && bPtr
->menuView
->pos
.y
+bPtr
->menuView
->size
.height
>= scrHeight
-1) {
673 if (bPtr
->menuView
->pos
.y
+bPtr
->menuView
->size
.height
-5
676 - (bPtr
->menuView
->pos
.y
+bPtr
->menuView
->size
.height
);
680 } else if (bPtr
->scrollStartY
<= 1 && bPtr
->menuView
->pos
.y
< 1) {
683 if (bPtr
->menuView
->pos
.y
+5 > 1)
684 dy
= 1 - bPtr
->menuView
->pos
.y
;
692 W_MoveView(bPtr
->menuView
, bPtr
->menuView
->pos
.x
,
693 bPtr
->menuView
->pos
.y
+ dy
);
695 oldItem
= bPtr
->highlightedItem
;
696 bPtr
->highlightedItem
= (bPtr
->scrollStartY
- bPtr
->menuView
->pos
.y
)
697 / bPtr
->view
->size
.height
;
699 if (oldItem
!=bPtr
->highlightedItem
) {
700 paintMenuEntry(bPtr
, oldItem
, False
);
701 paintMenuEntry(bPtr
, bPtr
->highlightedItem
,
702 itemIsEnabled(bPtr
, bPtr
->highlightedItem
));
705 bPtr
->timer
= WMAddTimerHandler(SCROLL_DELAY
, autoScroll
, bPtr
);
713 handleActionEvents(XEvent
*event
, void *data
)
715 PopUpButton
*bPtr
= (PopUpButton
*)data
;
717 int scrHeight
= WMWidgetScreen(bPtr
)->rootView
->size
.height
;
719 CHECK_CLASS(data
, WC_PopUpButton
);
721 if (bPtr
->itemCount
< 1)
724 switch (event
->type
) {
725 /* called for menuView */
727 paintMenuEntry(bPtr
, bPtr
->highlightedItem
,
728 itemIsEnabled(bPtr
, bPtr
->highlightedItem
));
732 bPtr
->flags
.insideMenu
= 0;
733 if (bPtr
->menuView
->flags
.mapped
)
734 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
735 bPtr
->highlightedItem
= -1;
739 bPtr
->flags
.insideMenu
= 1;
743 if (bPtr
->flags
.insideMenu
) {
744 oldItem
= bPtr
->highlightedItem
;
745 bPtr
->highlightedItem
= event
->xmotion
.y
/ bPtr
->view
->size
.height
;
746 if (oldItem
!=bPtr
->highlightedItem
) {
747 paintMenuEntry(bPtr
, oldItem
, False
);
748 paintMenuEntry(bPtr
, bPtr
->highlightedItem
,
749 itemIsEnabled(bPtr
, bPtr
->highlightedItem
));
752 if (event
->xmotion
.y_root
>= scrHeight
-1
753 || event
->xmotion
.y_root
<= 1) {
754 bPtr
->scrollStartY
= event
->xmotion
.y_root
;
757 } else if (bPtr
->timer
) {
758 WMDeleteTimerHandler(bPtr
->timer
);
764 /* called for bPtr->view */
766 if (!bPtr
->flags
.enabled
)
770 if (!bPtr
->flags
.pullsDown
) {
771 bPtr
->highlightedItem
= bPtr
->selectedItemIndex
;
772 bPtr
->flags
.insideMenu
= 1;
774 bPtr
->highlightedItem
= -1;
775 bPtr
->flags
.insideMenu
= 0;
777 XGrabPointer(bPtr
->view
->screen
->display
, bPtr
->menuView
->window
,
778 False
, ButtonReleaseMask
|ButtonMotionMask
|EnterWindowMask
779 |LeaveWindowMask
, GrabModeAsync
, GrabModeAsync
,
780 None
, None
, CurrentTime
);
784 XUngrabPointer(bPtr
->view
->screen
->display
, event
->xbutton
.time
);
785 if (!bPtr
->flags
.pullsDown
)
789 WMDeleteTimerHandler(bPtr
->timer
);
793 if (bPtr
->flags
.insideMenu
&& bPtr
->highlightedItem
>=0) {
794 if (itemIsEnabled(bPtr
, bPtr
->highlightedItem
)) {
796 WMSetPopUpButtonSelectedItem(bPtr
, bPtr
->highlightedItem
);
798 if (bPtr
->flags
.pullsDown
) {
799 for (i
=0; i
<MENU_BLINK_COUNT
; i
++) {
800 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
801 XSync(bPtr
->view
->screen
->display
, 0);
802 wusleep(MENU_BLINK_DELAY
);
803 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, True
);
804 XSync(bPtr
->view
->screen
->display
, 0);
805 wusleep(MENU_BLINK_DELAY
);
808 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
811 (*bPtr
->action
)(bPtr
, bPtr
->clientData
);
814 if (bPtr
->menuView
->flags
.mapped
)
823 destroyPopUpButton(PopUpButton
*bPtr
)
825 ItemList
*itemPtr
, *tmp
;
828 WMDeleteTimerHandler(bPtr
->timer
);
831 itemPtr
= bPtr
->items
;
832 while (itemPtr
!=NULL
) {
834 tmp
= itemPtr
->nextPtr
;
842 /* have to destroy explicitly because the popup is a toplevel */
843 W_DestroyView(bPtr
->menuView
);