4 typedef struct W_Button
{
14 WMColorSpec textColor
;
15 WMColorSpec altTextColor
;
16 WMColorSpec disTextColor
;
29 float periodicInterval
;
31 WMHandlerID
*timer
; /* for continuous mode */
35 WMImagePosition imagePosition
:4;
36 WMAlignment alignment
:2;
38 unsigned int selected
:1;
40 unsigned int enabled
:1;
42 unsigned int dimsWhenDisabled
:1;
44 unsigned int bordered
:1;
46 unsigned int springLoaded
:1;
48 unsigned int pushIn
:1; /* change relief while pushed */
50 unsigned int pushLight
:1; /* highlight while pushed */
52 unsigned int pushChange
:1; /* change caption while pushed */
54 unsigned int stateLight
:1; /* state indicated by highlight */
56 unsigned int stateChange
:1; /* state indicated by caption change */
58 unsigned int statePush
:1; /* state indicated by relief */
60 unsigned int continuous
:1; /* continually perform action */
62 unsigned int prevSelected
:1;
64 unsigned int pushed
:1;
66 unsigned int wasPushed
:1;
68 unsigned int redrawPending
:1;
70 unsigned int addedObserver
:1;
74 #define DEFAULT_BUTTON_WIDTH 60
75 #define DEFAULT_BUTTON_HEIGHT 24
76 #define DEFAULT_BUTTON_ALIGNMENT WACenter
77 #define DEFAULT_BUTTON_IS_BORDERED True
79 #define DEFAULT_RADIO_WIDTH 100
80 #define DEFAULT_RADIO_HEIGHT 20
81 #define DEFAULT_RADIO_ALIGNMENT WALeft
82 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
83 #define DEFAULT_RADIO_TEXT "Radio"
85 #define DEFAULT_SWITCH_WIDTH 100
86 #define DEFAULT_SWITCH_HEIGHT 20
87 #define DEFAULT_SWITCH_ALIGNMENT WALeft
88 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
89 #define DEFAULT_SWITCH_TEXT "Switch"
91 static void destroyButton(Button
* bPtr
);
92 static void paintButton(Button
* bPtr
);
94 static void handleEvents(XEvent
* event
, void *data
);
95 static void handleActionEvents(XEvent
* event
, void *data
);
97 static char *WMPushedRadioNotification
= "WMPushedRadioNotification";
99 #define NFONT(b) (b)->view->screen->normalFont
101 WMButton
*WMCreateCustomButton(WMWidget
* parent
, int behaviourMask
)
105 bPtr
= wmalloc(sizeof(Button
));
106 memset(bPtr
, 0, sizeof(Button
));
108 bPtr
->widgetClass
= WC_Button
;
110 bPtr
->view
= W_CreateView(W_VIEW(parent
));
115 bPtr
->view
->self
= bPtr
;
117 bPtr
->flags
.type
= 0;
119 bPtr
->flags
.springLoaded
= (behaviourMask
& WBBSpringLoadedMask
) != 0;
120 bPtr
->flags
.pushIn
= (behaviourMask
& WBBPushInMask
) != 0;
121 bPtr
->flags
.pushChange
= (behaviourMask
& WBBPushChangeMask
) != 0;
122 bPtr
->flags
.pushLight
= (behaviourMask
& WBBPushLightMask
) != 0;
123 bPtr
->flags
.stateLight
= (behaviourMask
& WBBStateLightMask
) != 0;
124 bPtr
->flags
.stateChange
= (behaviourMask
& WBBStateChangeMask
) != 0;
125 bPtr
->flags
.statePush
= (behaviourMask
& WBBStatePushMask
) != 0;
127 W_ResizeView(bPtr
->view
, DEFAULT_BUTTON_WIDTH
, DEFAULT_BUTTON_HEIGHT
);
128 bPtr
->flags
.alignment
= DEFAULT_BUTTON_ALIGNMENT
;
129 bPtr
->flags
.bordered
= DEFAULT_BUTTON_IS_BORDERED
;
131 bPtr
->flags
.enabled
= 1;
132 bPtr
->flags
.dimsWhenDisabled
= 1;
134 bPtr
->textColor
= WMBlackColorSpec();
135 bPtr
->altTextColor
= WMBlackColorSpec();
136 bPtr
->disTextColor
= WMDarkGrayColorSpec();
138 WMCreateEventHandler(bPtr
->view
, ExposureMask
| StructureNotifyMask
, handleEvents
, bPtr
);
140 WMCreateEventHandler(bPtr
->view
, ButtonPressMask
| ButtonReleaseMask
141 | EnterWindowMask
| LeaveWindowMask
, handleActionEvents
, bPtr
);
143 W_ResizeView(bPtr
->view
, DEFAULT_BUTTON_WIDTH
, DEFAULT_BUTTON_HEIGHT
);
144 bPtr
->flags
.alignment
= DEFAULT_BUTTON_ALIGNMENT
;
145 bPtr
->flags
.bordered
= DEFAULT_BUTTON_IS_BORDERED
;
150 WMButton
*WMCreateButton(WMWidget
* parent
, WMButtonType type
)
152 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
156 case WBTMomentaryPush
:
157 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushInMask
| WBBPushLightMask
);
160 case WBTMomentaryChange
:
161 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushChangeMask
);
164 case WBTPushOnPushOff
:
165 bPtr
= WMCreateCustomButton(parent
, WBBPushInMask
| WBBStatePushMask
| WBBStateLightMask
);
169 bPtr
= WMCreateCustomButton(parent
, WBBPushInMask
| WBBStateChangeMask
| WBBStatePushMask
);
173 bPtr
= WMCreateCustomButton(parent
, WBBStateLightMask
);
177 bPtr
= WMCreateCustomButton(parent
, WBBStateChangeMask
);
178 bPtr
->flags
.bordered
= 0;
179 bPtr
->image
= WMRetainImage(scrPtr
->checkButtonImageOff
);
180 bPtr
->altImage
= WMRetainImage(scrPtr
->checkButtonImageOn
);
184 bPtr
= WMCreateCustomButton(parent
, WBBStateChangeMask
);
185 bPtr
->flags
.bordered
= 0;
186 bPtr
->image
= WMRetainImage(scrPtr
->radioButtonImageOff
);
187 bPtr
->altImage
= WMRetainImage(scrPtr
->radioButtonImageOn
);
191 case WBTMomentaryLight
:
192 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushLightMask
);
193 bPtr
->flags
.bordered
= 1;
197 bPtr
->flags
.type
= type
;
199 if (type
== WBTRadio
) {
200 W_ResizeView(bPtr
->view
, DEFAULT_RADIO_WIDTH
, DEFAULT_RADIO_HEIGHT
);
201 WMSetButtonText(bPtr
, DEFAULT_RADIO_TEXT
);
202 bPtr
->flags
.alignment
= DEFAULT_RADIO_ALIGNMENT
;
203 bPtr
->flags
.imagePosition
= DEFAULT_RADIO_IMAGE_POSITION
;
204 } else if (type
== WBTSwitch
) {
205 W_ResizeView(bPtr
->view
, DEFAULT_SWITCH_WIDTH
, DEFAULT_SWITCH_HEIGHT
);
206 WMSetButtonText(bPtr
, DEFAULT_SWITCH_TEXT
);
207 bPtr
->flags
.alignment
= DEFAULT_SWITCH_ALIGNMENT
;
208 bPtr
->flags
.imagePosition
= DEFAULT_SWITCH_IMAGE_POSITION
;
214 void WMSetButtonImageDefault(WMButton
* bPtr
)
216 WMSetButtonImage(bPtr
, WMWidgetScreen(bPtr
)->buttonArrow
);
217 WMSetButtonAltImage(bPtr
, WMWidgetScreen(bPtr
)->pushedButtonArrow
);
220 void WMSetButtonImage(WMButton
*bPtr
, WMImage
*image
)
222 if (bPtr
->image
!= NULL
)
223 WMDestroyImage(bPtr
->image
);
224 bPtr
->image
= WMRetainImage(image
);
226 if (bPtr
->view
->flags
.realized
) {
231 void WMSetButtonAltImage(WMButton
* bPtr
, WMImage
* image
)
233 if (bPtr
->altImage
!= NULL
)
234 WMDestroyImage(bPtr
->altImage
);
235 bPtr
->altImage
= WMRetainImage(image
);
237 if (bPtr
->view
->flags
.realized
) {
242 void WMSetButtonImagePosition(WMButton
* bPtr
, WMImagePosition position
)
244 bPtr
->flags
.imagePosition
= position
;
246 if (bPtr
->view
->flags
.realized
) {
251 void WMSetButtonTextAlignment(WMButton
* bPtr
, WMAlignment alignment
)
253 bPtr
->flags
.alignment
= alignment
;
255 if (bPtr
->view
->flags
.realized
) {
260 void WMSetButtonText(WMButton
* bPtr
, char *text
)
263 wfree(bPtr
->caption
);
266 bPtr
->caption
= wstrdup(text
);
268 bPtr
->caption
= NULL
;
271 if (bPtr
->view
->flags
.realized
) {
276 void WMSetButtonAltText(WMButton
* bPtr
, char *text
)
278 if (bPtr
->altCaption
)
279 wfree(bPtr
->altCaption
);
282 bPtr
->altCaption
= wstrdup(text
);
284 bPtr
->altCaption
= NULL
;
287 if (bPtr
->view
->flags
.realized
) {
292 void WMSetButtonTextColor(WMButton
*bPtr
, WMColorSpec
*color
)
294 bPtr
->textColor
= *color
;
297 void WMSetButtonAltTextColor(WMButton
*bPtr
, WMColorSpec
*color
)
299 bPtr
->altTextColor
= *color
;
302 void WMSetButtonDisabledTextColor(WMButton
*bPtr
, WMColorSpec
*color
)
304 bPtr
->disTextColor
= *color
;
307 void WMSetButtonSelected(WMButton
* bPtr
, int isSelected
)
309 bPtr
->flags
.selected
= isSelected
? 1 : 0;
311 if (bPtr
->view
->flags
.realized
) {
314 if (bPtr
->groupIndex
> 0)
315 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
318 int WMGetButtonSelected(WMButton
* bPtr
)
320 CHECK_CLASS(bPtr
, WC_Button
);
322 return bPtr
->flags
.selected
;
325 void WMSetButtonBordered(WMButton
* bPtr
, int isBordered
)
327 bPtr
->flags
.bordered
= isBordered
;
329 if (bPtr
->view
->flags
.realized
) {
334 void WMSetButtonFont(WMButton
* bPtr
, WMFont
* font
)
337 WMReleaseFont(bPtr
->font
);
339 bPtr
->font
= WMRetainFont(font
);
342 void WMSetButtonEnabled(WMButton
* bPtr
, Bool flag
)
344 bPtr
->flags
.enabled
= ((flag
== 0) ? 0 : 1);
346 if (bPtr
->view
->flags
.mapped
) {
351 int WMGetButtonEnabled(WMButton
* bPtr
)
353 CHECK_CLASS(bPtr
, WC_Button
);
355 return bPtr
->flags
.enabled
;
358 void WMSetButtonImageDimsWhenDisabled(WMButton
* bPtr
, Bool flag
)
360 bPtr
->flags
.dimsWhenDisabled
= ((flag
== 0) ? 0 : 1);
363 void WMSetButtonTag(WMButton
* bPtr
, int tag
)
368 void WMPerformButtonClick(WMButton
* bPtr
)
370 CHECK_CLASS(bPtr
, WC_Button
);
372 if (!bPtr
->flags
.enabled
)
375 bPtr
->flags
.pushed
= 1;
376 bPtr
->flags
.selected
= 1;
378 if (bPtr
->view
->flags
.mapped
) {
380 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr
)));
384 bPtr
->flags
.pushed
= 0;
386 if (bPtr
->groupIndex
> 0) {
387 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
391 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
393 if (bPtr
->view
->flags
.mapped
)
397 void WMSetButtonAction(WMButton
* bPtr
, WMAction
* action
, void *clientData
)
399 CHECK_CLASS(bPtr
, WC_Button
);
401 bPtr
->action
= action
;
403 bPtr
->clientData
= clientData
;
406 static void radioPushObserver(void *observerData
, WMNotification
* notification
)
408 WMButton
*bPtr
= (WMButton
*) observerData
;
409 WMButton
*pushedButton
= (WMButton
*) WMGetNotificationObject(notification
);
411 if (bPtr
!= pushedButton
&& pushedButton
->groupIndex
== bPtr
->groupIndex
&& bPtr
->groupIndex
!= 0) {
412 if (bPtr
->flags
.selected
) {
413 bPtr
->flags
.selected
= 0;
419 void WMGroupButtons(WMButton
* bPtr
, WMButton
* newMember
)
421 static int tagIndex
= 0;
423 CHECK_CLASS(bPtr
, WC_Button
);
424 CHECK_CLASS(newMember
, WC_Button
);
426 if (!bPtr
->flags
.addedObserver
) {
427 WMAddNotificationObserver(radioPushObserver
, bPtr
, WMPushedRadioNotification
, NULL
);
428 bPtr
->flags
.addedObserver
= 1;
430 if (!newMember
->flags
.addedObserver
) {
431 WMAddNotificationObserver(radioPushObserver
, newMember
, WMPushedRadioNotification
, NULL
);
432 newMember
->flags
.addedObserver
= 1;
435 if (bPtr
->groupIndex
== 0) {
436 bPtr
->groupIndex
= ++tagIndex
;
438 newMember
->groupIndex
= bPtr
->groupIndex
;
441 void WMSetButtonContinuous(WMButton
* bPtr
, Bool flag
)
443 bPtr
->flags
.continuous
= ((flag
== 0) ? 0 : 1);
445 WMDeleteTimerHandler(bPtr
->timer
);
450 void WMSetButtonPeriodicDelay(WMButton
* bPtr
, float delay
, float interval
)
452 bPtr
->periodicInterval
= interval
;
453 bPtr
->periodicDelay
= delay
;
456 static void paintButton(Button
* bPtr
)
458 cairo_t
*cr
= W_CreateCairoForView(W_VIEW(bPtr
));
459 W_Screen
*scrPtr
= bPtr
->view
->screen
;
464 WMColorSpec textColor
;
465 WMColorSpec backColor
;
467 backColor
= bPtr
->view
->backColor
;
468 caption
= bPtr
->caption
;
470 if (bPtr
->flags
.enabled
) {
471 textColor
= bPtr
->textColor
;
473 textColor
= bPtr
->disTextColor
;
476 if (bPtr
->flags
.enabled
|| !bPtr
->flags
.dimsWhenDisabled
)
477 image
= WMRetainImage(bPtr
->image
);
480 //XXX make dimmited image
484 if (bPtr
->flags
.bordered
)
489 if (bPtr
->flags
.selected
) {
490 if (bPtr
->flags
.stateLight
) {
491 backColor
= WMWhiteColorSpec();
492 textColor
= WMBlackColorSpec();
495 if (bPtr
->flags
.stateChange
) {
496 if (bPtr
->altCaption
)
497 caption
= bPtr
->altCaption
;
499 image
= bPtr
->altImage
;
500 textColor
= bPtr
->altTextColor
;
503 if (bPtr
->flags
.statePush
&& bPtr
->flags
.bordered
) {
509 if (bPtr
->flags
.pushed
) {
510 if (bPtr
->flags
.pushIn
) {
514 if (bPtr
->flags
.pushLight
) {
515 backColor
= WMWhiteColorSpec();
516 textColor
= WMBlackColorSpec();
519 if (bPtr
->flags
.pushChange
) {
520 if (bPtr
->altCaption
)
521 caption
= bPtr
->altCaption
;
523 image
= bPtr
->altImage
;
524 textColor
= bPtr
->altTextColor
;
529 W_DrawButtonRelief(scrPtr
, cr
, 0, 0,
530 bPtr
->view
->size
.width
, bPtr
->view
->size
.height
,
534 W_PaintTextAndImage(scrPtr
, cr
, bPtr
->view
, True
, &textColor
,
535 (bPtr
->font
!=NULL
? bPtr
->font
: scrPtr
->normalFont
),
536 relief
, caption
, bPtr
->flags
.alignment
, image
,
537 bPtr
->flags
.imagePosition
, NULL
, offset
);
540 WMDestroyImage(image
);
546 static void handleEvents(XEvent
* event
, void *data
)
548 Button
*bPtr
= (Button
*) data
;
550 CHECK_CLASS(data
, WC_Button
);
552 switch (event
->type
) {
554 if (event
->xexpose
.count
!= 0)
565 static void autoRepeat(void *data
)
567 Button
*bPtr
= (Button
*) data
;
569 if (bPtr
->action
&& bPtr
->flags
.pushed
)
570 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
572 bPtr
->timer
= WMAddTimerHandler((int)(bPtr
->periodicInterval
* 1000), autoRepeat
, bPtr
);
575 static void handleActionEvents(XEvent
* event
, void *data
)
577 Button
*bPtr
= (Button
*) data
;
578 int doclick
= 0, dopaint
= 0;
580 CHECK_CLASS(data
, WC_Button
);
582 if (!bPtr
->flags
.enabled
)
585 switch (event
->type
) {
587 if (bPtr
->groupIndex
== 0) {
588 bPtr
->flags
.pushed
= bPtr
->flags
.wasPushed
;
589 if (bPtr
->flags
.pushed
) {
590 bPtr
->flags
.selected
= !bPtr
->flags
.prevSelected
;
597 if (bPtr
->groupIndex
== 0) {
598 bPtr
->flags
.wasPushed
= bPtr
->flags
.pushed
;
599 if (bPtr
->flags
.pushed
) {
600 bPtr
->flags
.selected
= bPtr
->flags
.prevSelected
;
603 bPtr
->flags
.pushed
= 0;
608 if (event
->xbutton
.button
== Button1
) {
609 bPtr
->flags
.prevSelected
= bPtr
->flags
.selected
;
610 bPtr
->flags
.wasPushed
= 0;
611 bPtr
->flags
.pushed
= 1;
612 if (bPtr
->groupIndex
> 0) {
613 bPtr
->flags
.selected
= 1;
617 bPtr
->flags
.selected
= !bPtr
->flags
.selected
;
620 if (bPtr
->flags
.continuous
&& !bPtr
->timer
) {
621 bPtr
->timer
= WMAddTimerHandler((int)(bPtr
->periodicDelay
* 1000),
628 if (event
->xbutton
.button
== Button1
) {
629 if (bPtr
->flags
.pushed
) {
630 if (bPtr
->groupIndex
== 0 || (bPtr
->flags
.selected
&& bPtr
->groupIndex
> 0))
633 if (bPtr
->flags
.springLoaded
) {
634 bPtr
->flags
.selected
= bPtr
->flags
.prevSelected
;
637 bPtr
->flags
.pushed
= 0;
640 WMDeleteTimerHandler(bPtr
->timer
);
650 if (bPtr
->flags
.selected
&& bPtr
->groupIndex
> 0) {
651 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
655 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
659 static void destroyButton(Button
* bPtr
)
661 if (bPtr
->flags
.addedObserver
) {
662 WMRemoveNotificationObserver(bPtr
);
666 WMDeleteTimerHandler(bPtr
->timer
);
669 WMReleaseFont(bPtr
->font
);
672 wfree(bPtr
->caption
);
674 if (bPtr
->altCaption
)
675 wfree(bPtr
->altCaption
);
678 WMDestroyImage(bPtr
->image
);
681 WMDestroyImage(bPtr
->altImage
);