4 typedef struct W_Button
{
15 WMColor
*altTextColor
;
16 WMColor
*disTextColor
;
32 float periodicInterval
;
34 WMHandlerID
*timer
; /* for continuous mode */
38 WMImagePosition imagePosition
:4;
39 WMAlignment alignment
:2;
41 unsigned int selected
:2;
43 unsigned int enabled
:1;
45 unsigned int dimsWhenDisabled
:1;
47 unsigned int bordered
:1;
49 unsigned int springLoaded
:1;
51 unsigned int pushIn
:1; /* change relief while pushed */
53 unsigned int pushLight
:1; /* highlight while pushed */
55 unsigned int pushChange
:1; /* change caption while pushed */
57 unsigned int stateLight
:1; /* state indicated by highlight */
59 unsigned int stateChange
:1; /* state indicated by caption change */
61 unsigned int statePush
:1; /* state indicated by relief */
63 unsigned int continuous
:1; /* continually perform action */
65 unsigned int prevSelected
:1;
67 unsigned int pushed
:1;
69 unsigned int wasPushed
:1;
71 unsigned int redrawPending
:1;
73 unsigned int addedObserver
:1;
77 #define DEFAULT_BUTTON_WIDTH 60
78 #define DEFAULT_BUTTON_HEIGHT 24
79 #define DEFAULT_BUTTON_ALIGNMENT WACenter
80 #define DEFAULT_BUTTON_IS_BORDERED True
82 #define DEFAULT_RADIO_WIDTH 100
83 #define DEFAULT_RADIO_HEIGHT 20
84 #define DEFAULT_RADIO_ALIGNMENT WALeft
85 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
86 #define DEFAULT_RADIO_TEXT "Radio"
88 #define DEFAULT_SWITCH_WIDTH 100
89 #define DEFAULT_SWITCH_HEIGHT 20
90 #define DEFAULT_SWITCH_ALIGNMENT WALeft
91 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
92 #define DEFAULT_SWITCH_TEXT "Switch"
94 static void destroyButton(Button
* bPtr
);
95 static void paintButton(Button
* bPtr
);
97 static void handleEvents(XEvent
* event
, void *data
);
98 static void handleActionEvents(XEvent
* event
, void *data
);
100 static char *WMPushedRadioNotification
= "WMPushedRadioNotification";
103 WMButton
*WMCreateCustomButton(WMWidget
* parent
, int behaviourMask
)
107 bPtr
= wmalloc(sizeof(Button
));
109 bPtr
->widgetClass
= WC_Button
;
111 bPtr
->view
= W_CreateView(W_VIEW(parent
));
116 bPtr
->view
->self
= bPtr
;
118 bPtr
->flags
.type
= 0;
120 bPtr
->flags
.springLoaded
= (behaviourMask
& WBBSpringLoadedMask
) != 0;
121 bPtr
->flags
.pushIn
= (behaviourMask
& WBBPushInMask
) != 0;
122 bPtr
->flags
.pushChange
= (behaviourMask
& WBBPushChangeMask
) != 0;
123 bPtr
->flags
.pushLight
= (behaviourMask
& WBBPushLightMask
) != 0;
124 bPtr
->flags
.stateLight
= (behaviourMask
& WBBStateLightMask
) != 0;
125 bPtr
->flags
.stateChange
= (behaviourMask
& WBBStateChangeMask
) != 0;
126 bPtr
->flags
.statePush
= (behaviourMask
& WBBStatePushMask
) != 0;
128 W_ResizeView(bPtr
->view
, DEFAULT_BUTTON_WIDTH
, DEFAULT_BUTTON_HEIGHT
);
129 bPtr
->flags
.alignment
= DEFAULT_BUTTON_ALIGNMENT
;
130 bPtr
->flags
.bordered
= DEFAULT_BUTTON_IS_BORDERED
;
132 bPtr
->flags
.enabled
= 1;
133 bPtr
->flags
.dimsWhenDisabled
= 1;
135 WMCreateEventHandler(bPtr
->view
, ExposureMask
| StructureNotifyMask
, handleEvents
, bPtr
);
137 WMCreateEventHandler(bPtr
->view
, ButtonPressMask
| ButtonReleaseMask
138 | EnterWindowMask
| LeaveWindowMask
, handleActionEvents
, bPtr
);
140 W_ResizeView(bPtr
->view
, DEFAULT_BUTTON_WIDTH
, DEFAULT_BUTTON_HEIGHT
);
141 bPtr
->flags
.alignment
= DEFAULT_BUTTON_ALIGNMENT
;
142 bPtr
->flags
.bordered
= DEFAULT_BUTTON_IS_BORDERED
;
147 WMButton
*WMCreateButton(WMWidget
* parent
, WMButtonType type
)
149 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
153 case WBTMomentaryPush
:
154 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushInMask
| WBBPushLightMask
);
157 case WBTMomentaryChange
:
158 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushChangeMask
);
161 case WBTPushOnPushOff
:
162 bPtr
= WMCreateCustomButton(parent
, WBBPushInMask
| WBBStatePushMask
| WBBStateLightMask
);
166 bPtr
= WMCreateCustomButton(parent
, WBBPushInMask
| WBBStateChangeMask
| WBBStatePushMask
);
170 bPtr
= WMCreateCustomButton(parent
, WBBStateLightMask
);
174 bPtr
= WMCreateCustomButton(parent
, WBBStateChangeMask
);
175 bPtr
->flags
.bordered
= 0;
176 bPtr
->image
= WMRetainPixmap(scrPtr
->checkButtonImageOff
);
177 bPtr
->altImage
= WMRetainPixmap(scrPtr
->checkButtonImageOn
);
181 bPtr
= WMCreateCustomButton(parent
, WBBStateChangeMask
);
182 bPtr
->flags
.bordered
= 0;
183 bPtr
->image
= WMRetainPixmap(scrPtr
->radioButtonImageOff
);
184 bPtr
->altImage
= WMRetainPixmap(scrPtr
->radioButtonImageOn
);
188 bPtr
= WMCreateCustomButton(parent
, WBBStateChangeMask
);
189 bPtr
->flags
.bordered
= 0;
190 bPtr
->image
= WMRetainPixmap(scrPtr
->tristateButtonImageOff
);
191 bPtr
->altImage
= WMRetainPixmap(scrPtr
->tristateButtonImageOn
);
192 bPtr
->tsImage
= WMRetainPixmap(scrPtr
->tristateButtonImageTri
);
196 case WBTMomentaryLight
:
197 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushLightMask
);
198 bPtr
->flags
.bordered
= 1;
202 bPtr
->flags
.type
= type
;
204 if (type
== WBTRadio
) {
205 W_ResizeView(bPtr
->view
, DEFAULT_RADIO_WIDTH
, DEFAULT_RADIO_HEIGHT
);
206 WMSetButtonText(bPtr
, DEFAULT_RADIO_TEXT
);
207 bPtr
->flags
.alignment
= DEFAULT_RADIO_ALIGNMENT
;
208 bPtr
->flags
.imagePosition
= DEFAULT_RADIO_IMAGE_POSITION
;
209 } else if (type
== WBTSwitch
|| type
== WBTTriState
) {
210 W_ResizeView(bPtr
->view
, DEFAULT_SWITCH_WIDTH
, DEFAULT_SWITCH_HEIGHT
);
211 WMSetButtonText(bPtr
, DEFAULT_SWITCH_TEXT
);
212 bPtr
->flags
.alignment
= DEFAULT_SWITCH_ALIGNMENT
;
213 bPtr
->flags
.imagePosition
= DEFAULT_SWITCH_IMAGE_POSITION
;
219 static void updateDisabledMask(WMButton
* bPtr
)
221 WMScreen
*scr
= WMWidgetScreen(bPtr
);
222 Display
*dpy
= scr
->display
;
227 if (bPtr
->dimage
->mask
) {
228 XFreePixmap(dpy
, bPtr
->dimage
->mask
);
229 bPtr
->dimage
->mask
= None
;
232 if (bPtr
->flags
.dimsWhenDisabled
) {
233 bPtr
->dimage
->mask
= XCreatePixmap(dpy
, scr
->stipple
,
234 bPtr
->dimage
->width
, bPtr
->dimage
->height
, 1);
236 XSetForeground(dpy
, scr
->monoGC
, 0);
237 XFillRectangle(dpy
, bPtr
->dimage
->mask
, scr
->monoGC
, 0, 0,
238 bPtr
->dimage
->width
, bPtr
->dimage
->height
);
242 gcv
.stipple
= scr
->stipple
;
243 gcv
.fill_style
= FillStippled
;
244 gcv
.clip_mask
= bPtr
->image
->mask
;
245 gcv
.clip_x_origin
= 0;
246 gcv
.clip_y_origin
= 0;
248 XChangeGC(dpy
, scr
->monoGC
, GCForeground
| GCBackground
| GCStipple
249 | GCFillStyle
| GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
251 XFillRectangle(dpy
, bPtr
->dimage
->mask
, scr
->monoGC
, 0, 0,
252 bPtr
->dimage
->width
, bPtr
->dimage
->height
);
254 gcv
.fill_style
= FillSolid
;
255 gcv
.clip_mask
= None
;
256 XChangeGC(dpy
, scr
->monoGC
, GCFillStyle
| GCClipMask
, &gcv
);
261 void WMSetButtonImageDefault(WMButton
* bPtr
)
263 WMSetButtonImage(bPtr
, WMWidgetScreen(bPtr
)->buttonArrow
);
264 WMSetButtonAltImage(bPtr
, WMWidgetScreen(bPtr
)->pushedButtonArrow
);
267 void WMSetButtonImage(WMButton
* bPtr
, WMPixmap
* image
)
269 if (bPtr
->image
!= NULL
)
270 WMReleasePixmap(bPtr
->image
);
271 bPtr
->image
= WMRetainPixmap(image
);
274 bPtr
->dimage
->pixmap
= None
;
275 WMReleasePixmap(bPtr
->dimage
);
280 bPtr
->dimage
= WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr
),
282 image
->width
, image
->height
, image
->depth
);
283 updateDisabledMask(bPtr
);
286 if (bPtr
->view
->flags
.realized
) {
291 void WMSetButtonAltImage(WMButton
* bPtr
, WMPixmap
* image
)
293 if (bPtr
->altImage
!= NULL
)
294 WMReleasePixmap(bPtr
->altImage
);
295 bPtr
->altImage
= WMRetainPixmap(image
);
297 if (bPtr
->view
->flags
.realized
) {
302 void WMSetButtonImagePosition(WMButton
* bPtr
, WMImagePosition position
)
304 bPtr
->flags
.imagePosition
= position
;
306 if (bPtr
->view
->flags
.realized
) {
311 void WMSetButtonTextAlignment(WMButton
* bPtr
, WMAlignment alignment
)
313 bPtr
->flags
.alignment
= alignment
;
315 if (bPtr
->view
->flags
.realized
) {
320 void WMSetButtonText(WMButton
* bPtr
, const char *text
)
323 wfree(bPtr
->caption
);
326 bPtr
->caption
= wstrdup(text
);
328 bPtr
->caption
= NULL
;
331 if (bPtr
->view
->flags
.realized
) {
336 const char *WMGetButtonText(WMButton
*bPtr
)
338 return bPtr
->caption
;
341 void WMSetButtonAltText(WMButton
* bPtr
, const char *text
)
343 if (bPtr
->altCaption
)
344 wfree(bPtr
->altCaption
);
347 bPtr
->altCaption
= wstrdup(text
);
349 bPtr
->altCaption
= NULL
;
352 if (bPtr
->view
->flags
.realized
) {
357 void WMSetButtonTextColor(WMButton
* bPtr
, WMColor
* color
)
360 WMReleaseColor(bPtr
->textColor
);
362 bPtr
->textColor
= WMRetainColor(color
);
365 void WMSetButtonAltTextColor(WMButton
* bPtr
, WMColor
* color
)
367 if (bPtr
->altTextColor
)
368 WMReleaseColor(bPtr
->altTextColor
);
370 bPtr
->altTextColor
= WMRetainColor(color
);
373 void WMSetButtonDisabledTextColor(WMButton
* bPtr
, WMColor
* color
)
375 if (bPtr
->disTextColor
)
376 WMReleaseColor(bPtr
->disTextColor
);
378 bPtr
->disTextColor
= WMRetainColor(color
);
381 void WMSetButtonSelected(WMButton
* bPtr
, int isSelected
)
383 if ((bPtr
->flags
.type
== WBTTriState
) && (isSelected
< 0))
384 bPtr
->flags
.selected
= 2;
386 bPtr
->flags
.selected
= isSelected
? 1 : 0;
388 if (bPtr
->view
->flags
.realized
) {
391 if (bPtr
->groupIndex
> 0)
392 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
395 int WMGetButtonSelected(WMButton
* bPtr
)
397 CHECK_CLASS(bPtr
, WC_Button
);
399 if ((bPtr
->flags
.type
== WBTTriState
) && (bPtr
->flags
.selected
== 2))
402 return bPtr
->flags
.selected
;
405 void WMSetButtonBordered(WMButton
* bPtr
, int isBordered
)
407 bPtr
->flags
.bordered
= isBordered
;
409 if (bPtr
->view
->flags
.realized
) {
414 void WMSetButtonFont(WMButton
* bPtr
, WMFont
* font
)
417 WMReleaseFont(bPtr
->font
);
419 bPtr
->font
= WMRetainFont(font
);
422 void WMSetButtonEnabled(WMButton
* bPtr
, Bool flag
)
424 bPtr
->flags
.enabled
= ((flag
== 0) ? 0 : 1);
426 if (bPtr
->view
->flags
.mapped
) {
431 int WMGetButtonEnabled(WMButton
* bPtr
)
433 CHECK_CLASS(bPtr
, WC_Button
);
435 return bPtr
->flags
.enabled
;
438 void WMSetButtonImageDimsWhenDisabled(WMButton
* bPtr
, Bool flag
)
440 bPtr
->flags
.dimsWhenDisabled
= ((flag
== 0) ? 0 : 1);
442 updateDisabledMask(bPtr
);
445 void WMSetButtonTag(WMButton
* bPtr
, int tag
)
450 void WMPerformButtonClick(WMButton
* bPtr
)
452 CHECK_CLASS(bPtr
, WC_Button
);
454 if (!bPtr
->flags
.enabled
)
457 bPtr
->flags
.pushed
= 1;
458 bPtr
->flags
.selected
= 1;
460 if (bPtr
->view
->flags
.mapped
) {
462 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr
)));
466 bPtr
->flags
.pushed
= 0;
468 if (bPtr
->groupIndex
> 0) {
469 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
473 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
475 if (bPtr
->view
->flags
.mapped
)
479 void WMSetButtonAction(WMButton
* bPtr
, WMAction
* action
, void *clientData
)
481 CHECK_CLASS(bPtr
, WC_Button
);
483 bPtr
->action
= action
;
485 bPtr
->clientData
= clientData
;
488 static void radioPushObserver(void *observerData
, WMNotification
* notification
)
490 WMButton
*bPtr
= (WMButton
*) observerData
;
491 WMButton
*pushedButton
= (WMButton
*) WMGetNotificationObject(notification
);
493 if (bPtr
!= pushedButton
&& pushedButton
->groupIndex
== bPtr
->groupIndex
&& bPtr
->groupIndex
!= 0) {
494 if (bPtr
->flags
.selected
) {
495 bPtr
->flags
.selected
= 0;
501 void WMGroupButtons(WMButton
* bPtr
, WMButton
* newMember
)
503 static int tagIndex
= 0;
505 CHECK_CLASS(bPtr
, WC_Button
);
506 CHECK_CLASS(newMember
, WC_Button
);
508 if (!bPtr
->flags
.addedObserver
) {
509 WMAddNotificationObserver(radioPushObserver
, bPtr
, WMPushedRadioNotification
, NULL
);
510 bPtr
->flags
.addedObserver
= 1;
512 if (!newMember
->flags
.addedObserver
) {
513 WMAddNotificationObserver(radioPushObserver
, newMember
, WMPushedRadioNotification
, NULL
);
514 newMember
->flags
.addedObserver
= 1;
517 if (bPtr
->groupIndex
== 0) {
518 bPtr
->groupIndex
= ++tagIndex
;
520 newMember
->groupIndex
= bPtr
->groupIndex
;
523 void WMSetButtonContinuous(WMButton
* bPtr
, Bool flag
)
525 bPtr
->flags
.continuous
= ((flag
== 0) ? 0 : 1);
527 WMDeleteTimerHandler(bPtr
->timer
);
532 void WMSetButtonPeriodicDelay(WMButton
* bPtr
, float delay
, float interval
)
534 bPtr
->periodicInterval
= interval
;
535 bPtr
->periodicDelay
= delay
;
538 static void paintButton(Button
* bPtr
)
540 W_Screen
*scrPtr
= bPtr
->view
->screen
;
549 caption
= bPtr
->caption
;
551 if (bPtr
->flags
.enabled
) {
552 textColor
= (bPtr
->textColor
!= NULL
? bPtr
->textColor
: scrPtr
->black
);
554 textColor
= (bPtr
->disTextColor
!= NULL
? bPtr
->disTextColor
: scrPtr
->darkGray
);
557 if (bPtr
->flags
.enabled
|| !bPtr
->dimage
)
560 image
= bPtr
->dimage
;
562 if (bPtr
->flags
.bordered
)
567 if (bPtr
->flags
.selected
) {
568 if (bPtr
->flags
.stateLight
) {
569 backColor
= scrPtr
->white
;
570 textColor
= scrPtr
->black
;
573 if (bPtr
->flags
.stateChange
) {
574 if (bPtr
->altCaption
)
575 caption
= bPtr
->altCaption
;
576 if (bPtr
->flags
.selected
== 2)
577 image
= bPtr
->tsImage
;
578 else if (bPtr
->altImage
)
579 image
= bPtr
->altImage
;
580 if (bPtr
->altTextColor
)
581 textColor
= bPtr
->altTextColor
;
584 if (bPtr
->flags
.statePush
&& bPtr
->flags
.bordered
) {
590 if (bPtr
->flags
.pushed
) {
591 if (bPtr
->flags
.pushIn
) {
595 if (bPtr
->flags
.pushLight
) {
596 backColor
= scrPtr
->white
;
597 textColor
= scrPtr
->black
;
600 if (bPtr
->flags
.pushChange
) {
601 if (bPtr
->altCaption
)
602 caption
= bPtr
->altCaption
;
604 image
= bPtr
->altImage
;
605 if (bPtr
->altTextColor
)
606 textColor
= bPtr
->altTextColor
;
610 W_PaintTextAndImage(bPtr
->view
, True
, textColor
,
611 (bPtr
->font
!= NULL
? bPtr
->font
: scrPtr
->normalFont
),
612 relief
, caption
, bPtr
->flags
.alignment
, image
,
613 bPtr
->flags
.imagePosition
, backColor
, offset
);
616 static void handleEvents(XEvent
* event
, void *data
)
618 Button
*bPtr
= (Button
*) data
;
620 CHECK_CLASS(data
, WC_Button
);
622 switch (event
->type
) {
624 if (event
->xexpose
.count
!= 0)
635 static void autoRepeat(void *data
)
637 Button
*bPtr
= (Button
*) data
;
639 if (bPtr
->action
&& bPtr
->flags
.pushed
)
640 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
642 bPtr
->timer
= WMAddTimerHandler((int)(bPtr
->periodicInterval
* 1000), autoRepeat
, bPtr
);
645 static void handleActionEvents(XEvent
* event
, void *data
)
647 Button
*bPtr
= (Button
*) data
;
648 int doclick
= 0, dopaint
= 0;
650 CHECK_CLASS(data
, WC_Button
);
652 if (!bPtr
->flags
.enabled
)
655 switch (event
->type
) {
657 if (bPtr
->groupIndex
== 0) {
658 bPtr
->flags
.pushed
= bPtr
->flags
.wasPushed
;
659 if (bPtr
->flags
.pushed
) {
660 bPtr
->flags
.selected
= !bPtr
->flags
.prevSelected
;
667 if (bPtr
->groupIndex
== 0) {
668 bPtr
->flags
.wasPushed
= bPtr
->flags
.pushed
;
669 if (bPtr
->flags
.pushed
) {
670 bPtr
->flags
.selected
= bPtr
->flags
.prevSelected
;
673 bPtr
->flags
.pushed
= 0;
678 if (event
->xbutton
.button
== Button1
) {
679 static const unsigned int next_state
[4] = { [0] = 1, [1] = 2, [2] = 0 };
681 bPtr
->flags
.prevSelected
= bPtr
->flags
.selected
;
682 bPtr
->flags
.wasPushed
= 0;
683 bPtr
->flags
.pushed
= 1;
684 if (bPtr
->groupIndex
> 0) {
685 bPtr
->flags
.selected
= 1;
689 if (bPtr
->flags
.type
== WBTTriState
)
690 bPtr
->flags
.selected
= next_state
[bPtr
->flags
.selected
];
692 bPtr
->flags
.selected
= !bPtr
->flags
.selected
;
695 if (bPtr
->flags
.continuous
&& !bPtr
->timer
) {
696 bPtr
->timer
= WMAddTimerHandler((int)(bPtr
->periodicDelay
* 1000),
703 if (event
->xbutton
.button
== Button1
) {
704 if (bPtr
->flags
.pushed
) {
705 if (bPtr
->groupIndex
== 0 || (bPtr
->flags
.selected
&& bPtr
->groupIndex
> 0))
708 if (bPtr
->flags
.springLoaded
) {
709 bPtr
->flags
.selected
= bPtr
->flags
.prevSelected
;
712 bPtr
->flags
.pushed
= 0;
715 WMDeleteTimerHandler(bPtr
->timer
);
725 if (bPtr
->flags
.selected
&& bPtr
->groupIndex
> 0) {
726 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
730 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
734 static void destroyButton(Button
* bPtr
)
736 if (bPtr
->flags
.addedObserver
) {
737 WMRemoveNotificationObserver(bPtr
);
741 WMDeleteTimerHandler(bPtr
->timer
);
744 WMReleaseFont(bPtr
->font
);
747 wfree(bPtr
->caption
);
749 if (bPtr
->altCaption
)
750 wfree(bPtr
->altCaption
);
753 WMReleaseColor(bPtr
->textColor
);
755 if (bPtr
->altTextColor
)
756 WMReleaseColor(bPtr
->altTextColor
);
758 if (bPtr
->disTextColor
)
759 WMReleaseColor(bPtr
->disTextColor
);
762 WMReleasePixmap(bPtr
->image
);
766 bPtr
->dimage
->pixmap
= None
;
768 WMReleasePixmap(bPtr
->dimage
);
771 WMReleasePixmap(bPtr
->altImage
);
774 WMReleasePixmap(bPtr
->tsImage
);