7 typedef struct ItemList
{
9 struct ItemList
*nextPtr
;
10 unsigned int disabled
:1;
13 typedef struct W_PopUpButton
{
25 short selectedItemIndex
;
27 short highlightedItem
;
29 ItemList
*selectedItem
; /* selected item if it is a menu btn */
31 WMView
*menuView
; /* override redirect popup menu */
34 unsigned int pullsDown
:1;
36 unsigned int configured
:1;
38 unsigned int insideMenu
:1;
43 #define MENU_BLINK_DELAY 60000
44 #define MENU_BLINK_COUNT 2
47 W_ViewProcedureTable _PopUpButtonViewProcedures
= {
54 #define DEFAULT_WIDTH 60
55 #define DEFAULT_HEIGHT 20
56 #define DEFAULT_CAPTION ""
59 static void destroyPopUpButton(PopUpButton
*bPtr
);
60 static void paintPopUpButton(PopUpButton
*bPtr
);
62 static void handleEvents(XEvent
*event
, void *data
);
63 static void handleActionEvents(XEvent
*event
, void *data
);
65 static void resizeMenu(PopUpButton
*bPtr
);
69 WMCreatePopUpButton(WMWidget
*parent
)
72 W_Screen
*scr
= W_VIEW(parent
)->screen
;
75 bPtr
= wmalloc(sizeof(PopUpButton
));
76 memset(bPtr
, 0, sizeof(PopUpButton
));
78 bPtr
->widgetClass
= WC_PopUpButton
;
80 bPtr
->view
= W_CreateView(W_VIEW(parent
));
85 bPtr
->view
->self
= bPtr
;
87 WMCreateEventHandler(bPtr
->view
, ExposureMask
|StructureNotifyMask
88 |ClientMessageMask
, handleEvents
, bPtr
);
91 W_ResizeView(bPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
92 bPtr
->caption
= wstrdup(DEFAULT_CAPTION
);
94 WMCreateEventHandler(bPtr
->view
, ButtonPressMask
|ButtonReleaseMask
,
95 handleActionEvents
, bPtr
);
97 bPtr
->menuView
= W_CreateTopView(scr
);
98 bPtr
->menuView
->attribs
.override_redirect
= True
;
99 bPtr
->menuView
->attribFlags
|= CWOverrideRedirect
;
101 W_ResizeView(bPtr
->menuView
, bPtr
->view
->size
.width
, 1);
103 WMCreateEventHandler(bPtr
->menuView
, ButtonPressMask
|ButtonReleaseMask
104 |EnterWindowMask
|LeaveWindowMask
|ButtonMotionMask
,
105 handleActionEvents
, bPtr
);
112 WMSetPopUpButtonAction(WMPopUpButton
*bPtr
, WMAction
*action
, void *clientData
)
114 CHECK_CLASS(bPtr
, WC_PopUpButton
);
116 bPtr
->action
= action
;
118 bPtr
->clientData
= clientData
;
123 WMAddPopUpButtonItem(WMPopUpButton
*bPtr
, char *title
)
125 ItemList
*itemPtr
, *tmp
;
127 CHECK_CLASS(bPtr
, WC_PopUpButton
);
129 itemPtr
= wmalloc(sizeof(ItemList
));
130 memset(itemPtr
, 0, sizeof(ItemList
));
131 itemPtr
->text
= wstrdup(title
);
133 /* append item to list */
136 bPtr
->items
= itemPtr
;
138 while (tmp
->nextPtr
!=NULL
)
140 tmp
->nextPtr
= itemPtr
;
145 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
151 WMInsertPopUpButtonItem(WMPopUpButton
*bPtr
, int index
, char *title
)
155 CHECK_CLASS(bPtr
, WC_PopUpButton
);
159 if (index
>= bPtr
->itemCount
) {
160 WMAddPopUpButtonItem(bPtr
, title
);
164 itemPtr
= wmalloc(sizeof(ItemList
));
165 memset(itemPtr
, 0, sizeof(ItemList
));
166 itemPtr
->text
= wstrdup(title
);
169 itemPtr
->nextPtr
= bPtr
->items
;
170 bPtr
->items
= itemPtr
;
176 /* insert item in list */
180 bPtr
->items
->nextPtr
= tmp
->nextPtr
;
181 tmp
->nextPtr
= bPtr
->items
;
186 /* if there is an selected item, update it's index to match the new
188 if (index
< bPtr
->selectedItemIndex
)
189 bPtr
->selectedItemIndex
++;
191 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
197 WMRemovePopUpButtonItem(WMPopUpButton
*bPtr
, int index
)
201 CHECK_CLASS(bPtr
, WC_PopUpButton
);
203 if (index
< 0 || index
>= bPtr
->itemCount
)
208 free(bPtr
->items
->text
);
209 tmp
= bPtr
->items
->nextPtr
;
219 next
= tmp
->nextPtr
->nextPtr
;
221 free(tmp
->nextPtr
->text
);
229 if (bPtr
->selectedItem
!=NULL
&& !bPtr
->flags
.pullsDown
) {
230 if (index
< bPtr
->selectedItemIndex
)
231 bPtr
->selectedItemIndex
--;
232 else if (index
== bPtr
->selectedItemIndex
) {
233 /* reselect first item if the removed item is the
235 bPtr
->selectedItem
= bPtr
->items
;
236 bPtr
->selectedItemIndex
= 0;
237 if (bPtr
->view
->flags
.mapped
)
238 paintPopUpButton(bPtr
);
242 if (bPtr
->menuView
&& bPtr
->menuView
->flags
.realized
)
248 WMSetPopUpButtonSelectedItem(WMPopUpButton
*bPtr
, int index
)
250 ItemList
*itemPtr
= bPtr
->items
;
254 bPtr
->selectedItem
= NULL
;
255 if (bPtr
->view
->flags
.mapped
)
256 paintPopUpButton(bPtr
);
261 itemPtr
= itemPtr
->nextPtr
;
263 bPtr
->selectedItem
= itemPtr
;
264 bPtr
->selectedItemIndex
= index
;
266 if (bPtr
->view
->flags
.mapped
)
267 paintPopUpButton(bPtr
);
271 WMGetPopUpButtonSelectedItem(WMPopUpButton
*bPtr
)
273 if (!bPtr
->flags
.pullsDown
&& bPtr
->selectedItem
==NULL
)
276 return bPtr
->selectedItemIndex
;
281 WMSetPopUpButtonText(WMPopUpButton
*bPtr
, char *text
)
286 bPtr
->caption
= wstrdup(text
);
288 bPtr
->caption
= NULL
;
289 if (bPtr
->view
->flags
.realized
) {
290 if (bPtr
->flags
.pullsDown
|| bPtr
->selectedItemIndex
< 0) {
291 paintPopUpButton(bPtr
);
299 WMSetPopUpButtonItemEnabled(WMPopUpButton
*bPtr
, int index
, Bool flag
)
302 ItemList
*item
= bPtr
->items
;
304 if (index
< 0 || index
>= bPtr
->itemCount
)
307 for (i
= 0; i
<index
; i
++)
310 item
->disabled
= !flag
;
315 WMSetPopUpButtonPullsDown(WMPopUpButton
*bPtr
, Bool flag
)
317 bPtr
->flags
.pullsDown
= flag
;
319 bPtr
->selectedItem
= bPtr
->items
;
320 if (bPtr
->selectedItem
)
321 bPtr
->selectedItemIndex
= 0;
323 bPtr
->selectedItemIndex
= -1;
325 bPtr
->selectedItemIndex
= -1;
327 if (bPtr
->view
->flags
.mapped
)
328 paintPopUpButton(bPtr
);
333 WMGetPopUpButtonNumberOfItems(WMPopUpButton
*bPtr
)
335 return bPtr
->itemCount
;
340 WMGetPopUpButtonItem(WMPopUpButton
*bPtr
, int index
)
342 ItemList
*itemPtr
= bPtr
->items
;
344 if ((index
< 0) || (index
>= bPtr
->itemCount
))
348 itemPtr
= itemPtr
->nextPtr
;
350 return itemPtr
->text
;
355 paintPopUpButton(PopUpButton
*bPtr
)
357 W_Screen
*scr
= bPtr
->view
->screen
;
362 if (bPtr
->flags
.pullsDown
) {
363 caption
= bPtr
->caption
;
365 if (bPtr
->selectedItem
== NULL
) {
366 /* if no item selected, show the caption */
367 caption
= bPtr
->caption
;
369 caption
= bPtr
->selectedItem
->text
;
373 pixmap
= XCreatePixmap(scr
->display
, bPtr
->view
->window
,
374 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
,
376 XFillRectangle(scr
->display
, pixmap
, W_GC(scr
->gray
), 0, 0,
377 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
);
379 W_DrawRelief(scr
, pixmap
, 0, 0, bPtr
->view
->size
.width
,
380 bPtr
->view
->size
.height
, WRRaised
);
383 W_PaintText(bPtr
->view
, pixmap
, scr
->normalFont
,
384 6, (bPtr
->view
->size
.height
-scr
->normalFont
->height
)/2,
385 bPtr
->view
->size
.width
, WALeft
, W_GC(scr
->black
), False
,
386 caption
, strlen(caption
));
389 if (bPtr
->flags
.pullsDown
) {
390 XCopyArea(scr
->display
, scr
->pullDownIndicator
->pixmap
,
391 pixmap
, scr
->copyGC
, 0, 0, scr
->pullDownIndicator
->width
,
392 scr
->pullDownIndicator
->height
,
393 bPtr
->view
->size
.width
-scr
->pullDownIndicator
->width
-4,
394 (bPtr
->view
->size
.height
-scr
->pullDownIndicator
->height
)/2);
398 x
= bPtr
->view
->size
.width
- scr
->popUpIndicator
->width
- 4;
399 y
= (bPtr
->view
->size
.height
-scr
->popUpIndicator
->height
)/2;
401 XSetClipOrigin(scr
->display
, scr
->clipGC
, x
, y
);
402 XSetClipMask(scr
->display
, scr
->clipGC
, scr
->popUpIndicator
->mask
);
403 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
, pixmap
,
404 scr
->clipGC
, 0, 0, scr
->popUpIndicator
->width
,
405 scr
->popUpIndicator
->height
, x
, y
);
408 XCopyArea(scr
->display
, pixmap
, bPtr
->view
->window
, scr
->copyGC
, 0, 0,
409 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
, 0, 0);
411 XFreePixmap(scr
->display
, pixmap
);
417 handleEvents(XEvent
*event
, void *data
)
419 PopUpButton
*bPtr
= (PopUpButton
*)data
;
421 CHECK_CLASS(data
, WC_PopUpButton
);
424 switch (event
->type
) {
426 if (event
->xexpose
.count
!=0)
428 paintPopUpButton(bPtr
);
432 destroyPopUpButton(bPtr
);
440 paintMenuEntry(PopUpButton
*bPtr
, int index
, int highlight
)
442 W_Screen
*scr
= bPtr
->view
->screen
;
446 int width
, height
, itemHeight
;
448 itemHeight
= bPtr
->view
->size
.height
;
449 width
= bPtr
->view
->size
.width
;
450 height
= itemHeight
* bPtr
->itemCount
;
451 yo
= (itemHeight
- scr
->normalFont
->height
)/2;
454 XClearArea(scr
->display
, bPtr
->menuView
->window
, 0, index
*itemHeight
,
455 width
, itemHeight
, False
);
457 } else if (index
< 0 && bPtr
->flags
.pullsDown
) {
461 XFillRectangle(scr
->display
, bPtr
->menuView
->window
, W_GC(scr
->white
),
462 1, index
*itemHeight
+1, width
-3, itemHeight
-3);
464 itemPtr
= bPtr
->items
;
465 for (i
= 0; i
< index
; i
++)
466 itemPtr
= itemPtr
->nextPtr
;
468 W_DrawRelief(scr
, bPtr
->menuView
->window
, 0, index
*itemHeight
,
469 width
, itemHeight
, WRRaised
);
471 W_PaintText(bPtr
->menuView
, bPtr
->menuView
->window
, scr
->normalFont
, 6,
472 index
*itemHeight
+ yo
, width
, WALeft
, W_GC(scr
->black
), False
,
473 itemPtr
->text
, strlen(itemPtr
->text
));
475 if (!bPtr
->flags
.pullsDown
&& index
== bPtr
->selectedItemIndex
) {
476 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
,
477 bPtr
->menuView
->window
, scr
->copyGC
, 0, 0,
478 scr
->popUpIndicator
->width
, scr
->popUpIndicator
->height
,
479 width
-scr
->popUpIndicator
->width
-4,
480 i
*itemHeight
+(itemHeight
-scr
->popUpIndicator
->height
)/2);
486 makeMenuPixmap(PopUpButton
*bPtr
)
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
- scr
->normalFont
->height
)/2;
500 pixmap
= XCreatePixmap(scr
->display
, bPtr
->view
->window
, width
, height
,
503 XFillRectangle(scr
->display
, pixmap
, W_GC(scr
->gray
), 0, 0, width
, height
);
505 itemPtr
= bPtr
->items
;
506 for (i
= 0; i
< bPtr
->itemCount
; i
++) {
509 W_DrawRelief(scr
, pixmap
, 0, i
*itemHeight
, width
, itemHeight
,
512 if (itemPtr
->disabled
)
513 gc
= W_GC(scr
->darkGray
);
515 gc
= W_GC(scr
->black
);
517 W_PaintText(bPtr
->menuView
, pixmap
, scr
->normalFont
, 6,
518 i
*itemHeight
+ yo
, width
, WALeft
, gc
, False
,
519 itemPtr
->text
, strlen(itemPtr
->text
));
521 if (!bPtr
->flags
.pullsDown
&& i
== bPtr
->selectedItemIndex
) {
522 XCopyArea(scr
->display
, scr
->popUpIndicator
->pixmap
, pixmap
,
523 scr
->copyGC
, 0, 0, scr
->popUpIndicator
->width
,
524 scr
->popUpIndicator
->height
,
525 width
-scr
->popUpIndicator
->width
-4,
526 i
*itemHeight
+(itemHeight
-scr
->popUpIndicator
->height
)/2);
528 itemPtr
= itemPtr
->nextPtr
;
536 resizeMenu(PopUpButton
*bPtr
)
540 height
= bPtr
->itemCount
* bPtr
->view
->size
.height
;
541 W_ResizeView(bPtr
->menuView
, bPtr
->view
->size
.width
, height
);
546 popUpMenu(PopUpButton
*bPtr
)
548 W_Screen
*scr
= bPtr
->view
->screen
;
552 if (!bPtr
->menuView
->flags
.realized
) {
553 W_RealizeView(bPtr
->menuView
);
557 if (bPtr
->itemCount
< 1)
560 XTranslateCoordinates(scr
->display
, bPtr
->view
->window
, scr
->rootWin
,
561 0, 0, &x
, &y
, &dummyW
);
563 if (bPtr
->flags
.pullsDown
) {
564 y
+= bPtr
->view
->size
.height
;
566 y
-= bPtr
->view
->size
.height
*bPtr
->selectedItemIndex
;
568 W_MoveView(bPtr
->menuView
, x
, y
);
570 XSetWindowBackgroundPixmap(scr
->display
, bPtr
->menuView
->window
,
571 makeMenuPixmap(bPtr
));
572 XClearWindow(scr
->display
, bPtr
->menuView
->window
);
574 W_MapView(bPtr
->menuView
);
576 bPtr
->highlightedItem
= 0;
577 if (!bPtr
->flags
.pullsDown
&& bPtr
->selectedItem
!= NULL
)
578 paintMenuEntry(bPtr
, bPtr
->selectedItemIndex
, True
);
583 popDownMenu(PopUpButton
*bPtr
)
585 W_UnmapView(bPtr
->menuView
);
587 /* free the background pixmap used to draw the menu contents */
588 XSetWindowBackgroundPixmap(bPtr
->view
->screen
->display
,
589 bPtr
->menuView
->window
, None
);
594 itemIsEnabled(PopUpButton
*bPtr
, int index
)
596 ItemList
*item
= bPtr
->items
;
599 item
= item
->nextPtr
;
601 return !item
->disabled
;
605 handleActionEvents(XEvent
*event
, void *data
)
607 PopUpButton
*bPtr
= (PopUpButton
*)data
;
610 CHECK_CLASS(data
, WC_PopUpButton
);
612 if (bPtr
->itemCount
< 1)
615 switch (event
->type
) {
616 /* called for menuView */
618 bPtr
->flags
.insideMenu
= 0;
619 if (bPtr
->menuView
->flags
.mapped
)
620 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
621 bPtr
->highlightedItem
= -1;
625 bPtr
->flags
.insideMenu
= 1;
629 if (bPtr
->flags
.insideMenu
) {
630 oldItem
= bPtr
->highlightedItem
;
631 bPtr
->highlightedItem
= event
->xmotion
.y
/ bPtr
->view
->size
.height
;
632 if (oldItem
!=bPtr
->highlightedItem
) {
633 paintMenuEntry(bPtr
, oldItem
, False
);
634 paintMenuEntry(bPtr
, bPtr
->highlightedItem
,
635 itemIsEnabled(bPtr
, bPtr
->highlightedItem
));
640 /* called for bPtr->view */
643 if (!bPtr
->flags
.pullsDown
) {
644 bPtr
->highlightedItem
= bPtr
->selectedItemIndex
;
645 bPtr
->flags
.insideMenu
= 1;
647 bPtr
->highlightedItem
= -1;
648 bPtr
->flags
.insideMenu
= 0;
650 XGrabPointer(bPtr
->view
->screen
->display
, bPtr
->menuView
->window
,
651 False
, ButtonReleaseMask
|ButtonMotionMask
|EnterWindowMask
652 |LeaveWindowMask
, GrabModeAsync
, GrabModeAsync
,
653 None
, None
, CurrentTime
);
657 XUngrabPointer(bPtr
->view
->screen
->display
, event
->xbutton
.time
);
658 if (!bPtr
->flags
.pullsDown
)
660 if (bPtr
->flags
.insideMenu
&& bPtr
->highlightedItem
>=0) {
661 if (itemIsEnabled(bPtr
, bPtr
->highlightedItem
)) {
663 WMSetPopUpButtonSelectedItem(bPtr
, bPtr
->highlightedItem
);
665 if (bPtr
->flags
.pullsDown
) {
666 for (i
=0; i
<MENU_BLINK_COUNT
; i
++) {
667 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
668 XSync(bPtr
->view
->screen
->display
, 0);
669 wusleep(MENU_BLINK_DELAY
);
670 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, True
);
671 XSync(bPtr
->view
->screen
->display
, 0);
672 wusleep(MENU_BLINK_DELAY
);
675 paintMenuEntry(bPtr
, bPtr
->highlightedItem
, False
);
678 (*bPtr
->action
)(bPtr
, bPtr
->clientData
);
681 if (bPtr
->menuView
->flags
.mapped
)
690 destroyPopUpButton(PopUpButton
*bPtr
)
692 ItemList
*itemPtr
, *tmp
;
694 itemPtr
= bPtr
->items
;
695 while (itemPtr
!=NULL
) {
697 tmp
= itemPtr
->nextPtr
;
705 /* have to destroy explicitly because the popup is a toplevel */
706 W_DestroyView(bPtr
->menuView
);