4 typedef struct W_Button
{
15 WMColor
*altTextColor
;
16 WMColor
*disTextColor
;
31 float periodicInterval
;
33 WMHandlerID
*timer
; /* for continuous mode */
37 WMImagePosition imagePosition
:4;
38 WMAlignment alignment
:2;
40 unsigned int selected
:1;
42 unsigned int enabled
:1;
44 unsigned int dimsWhenDisabled
:1;
46 unsigned int bordered
:1;
48 unsigned int springLoaded
:1;
50 unsigned int pushIn
:1; /* change relief while pushed */
52 unsigned int pushLight
:1; /* highlight while pushed */
54 unsigned int pushChange
:1; /* change caption while pushed */
56 unsigned int stateLight
:1; /* state indicated by highlight */
58 unsigned int stateChange
:1; /* state indicated by caption change */
60 unsigned int statePush
:1; /* state indicated by relief */
62 unsigned int continuous
:1; /* continually perform action */
64 unsigned int prevSelected
:1;
66 unsigned int pushed
:1;
68 unsigned int wasPushed
:1;
70 unsigned int redrawPending
:1;
72 unsigned int addedObserver
:1;
76 #define DEFAULT_BUTTON_WIDTH 60
77 #define DEFAULT_BUTTON_HEIGHT 24
78 #define DEFAULT_BUTTON_ALIGNMENT WACenter
79 #define DEFAULT_BUTTON_IS_BORDERED True
81 #define DEFAULT_RADIO_WIDTH 100
82 #define DEFAULT_RADIO_HEIGHT 20
83 #define DEFAULT_RADIO_ALIGNMENT WALeft
84 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
85 #define DEFAULT_RADIO_TEXT "Radio"
87 #define DEFAULT_SWITCH_WIDTH 100
88 #define DEFAULT_SWITCH_HEIGHT 20
89 #define DEFAULT_SWITCH_ALIGNMENT WALeft
90 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
91 #define DEFAULT_SWITCH_TEXT "Switch"
93 static void destroyButton(Button
* bPtr
);
94 static void paintButton(Button
* bPtr
);
96 static void handleEvents(XEvent
* event
, void *data
);
97 static void handleActionEvents(XEvent
* event
, void *data
);
99 static char *WMPushedRadioNotification
= "WMPushedRadioNotification";
101 #define NFONT(b) (b)->view->screen->normalFont
103 WMButton
*WMCreateCustomButton(WMWidget
* parent
, int behaviourMask
)
107 bPtr
= wmalloc(sizeof(Button
));
108 memset(bPtr
, 0, sizeof(Button
));
110 bPtr
->widgetClass
= WC_Button
;
112 bPtr
->view
= W_CreateView(W_VIEW(parent
));
117 bPtr
->view
->self
= bPtr
;
119 bPtr
->flags
.type
= 0;
121 bPtr
->flags
.springLoaded
= (behaviourMask
& WBBSpringLoadedMask
) != 0;
122 bPtr
->flags
.pushIn
= (behaviourMask
& WBBPushInMask
) != 0;
123 bPtr
->flags
.pushChange
= (behaviourMask
& WBBPushChangeMask
) != 0;
124 bPtr
->flags
.pushLight
= (behaviourMask
& WBBPushLightMask
) != 0;
125 bPtr
->flags
.stateLight
= (behaviourMask
& WBBStateLightMask
) != 0;
126 bPtr
->flags
.stateChange
= (behaviourMask
& WBBStateChangeMask
) != 0;
127 bPtr
->flags
.statePush
= (behaviourMask
& WBBStatePushMask
) != 0;
129 W_ResizeView(bPtr
->view
, DEFAULT_BUTTON_WIDTH
, DEFAULT_BUTTON_HEIGHT
);
130 bPtr
->flags
.alignment
= DEFAULT_BUTTON_ALIGNMENT
;
131 bPtr
->flags
.bordered
= DEFAULT_BUTTON_IS_BORDERED
;
133 bPtr
->flags
.enabled
= 1;
134 bPtr
->flags
.dimsWhenDisabled
= 1;
136 WMCreateEventHandler(bPtr
->view
, ExposureMask
| StructureNotifyMask
, handleEvents
, bPtr
);
138 WMCreateEventHandler(bPtr
->view
, ButtonPressMask
| ButtonReleaseMask
139 | EnterWindowMask
| LeaveWindowMask
, handleActionEvents
, bPtr
);
141 W_ResizeView(bPtr
->view
, DEFAULT_BUTTON_WIDTH
, DEFAULT_BUTTON_HEIGHT
);
142 bPtr
->flags
.alignment
= DEFAULT_BUTTON_ALIGNMENT
;
143 bPtr
->flags
.bordered
= DEFAULT_BUTTON_IS_BORDERED
;
148 WMButton
*WMCreateButton(WMWidget
* parent
, WMButtonType type
)
150 W_Screen
*scrPtr
= W_VIEW(parent
)->screen
;
154 case WBTMomentaryPush
:
155 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushInMask
| WBBPushLightMask
);
158 case WBTMomentaryChange
:
159 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushChangeMask
);
162 case WBTPushOnPushOff
:
163 bPtr
= WMCreateCustomButton(parent
, WBBPushInMask
| WBBStatePushMask
| WBBStateLightMask
);
167 bPtr
= WMCreateCustomButton(parent
, WBBPushInMask
| WBBStateChangeMask
| WBBStatePushMask
);
171 bPtr
= WMCreateCustomButton(parent
, WBBStateLightMask
);
175 bPtr
= WMCreateCustomButton(parent
, WBBStateChangeMask
);
176 bPtr
->flags
.bordered
= 0;
177 bPtr
->image
= WMRetainPixmap(scrPtr
->checkButtonImageOff
);
178 bPtr
->altImage
= WMRetainPixmap(scrPtr
->checkButtonImageOn
);
182 bPtr
= WMCreateCustomButton(parent
, WBBStateChangeMask
);
183 bPtr
->flags
.bordered
= 0;
184 bPtr
->image
= WMRetainPixmap(scrPtr
->radioButtonImageOff
);
185 bPtr
->altImage
= WMRetainPixmap(scrPtr
->radioButtonImageOn
);
189 case WBTMomentaryLight
:
190 bPtr
= WMCreateCustomButton(parent
, WBBSpringLoadedMask
| WBBPushLightMask
);
191 bPtr
->flags
.bordered
= 1;
195 bPtr
->flags
.type
= type
;
197 if (type
== WBTRadio
) {
198 W_ResizeView(bPtr
->view
, DEFAULT_RADIO_WIDTH
, DEFAULT_RADIO_HEIGHT
);
199 WMSetButtonText(bPtr
, DEFAULT_RADIO_TEXT
);
200 bPtr
->flags
.alignment
= DEFAULT_RADIO_ALIGNMENT
;
201 bPtr
->flags
.imagePosition
= DEFAULT_RADIO_IMAGE_POSITION
;
202 } else if (type
== WBTSwitch
) {
203 W_ResizeView(bPtr
->view
, DEFAULT_SWITCH_WIDTH
, DEFAULT_SWITCH_HEIGHT
);
204 WMSetButtonText(bPtr
, DEFAULT_SWITCH_TEXT
);
205 bPtr
->flags
.alignment
= DEFAULT_SWITCH_ALIGNMENT
;
206 bPtr
->flags
.imagePosition
= DEFAULT_SWITCH_IMAGE_POSITION
;
212 static void updateDisabledMask(WMButton
* bPtr
)
214 WMScreen
*scr
= WMWidgetScreen(bPtr
);
215 Display
*dpy
= scr
->display
;
220 if (bPtr
->dimage
->mask
) {
221 XFreePixmap(dpy
, bPtr
->dimage
->mask
);
222 bPtr
->dimage
->mask
= None
;
225 if (bPtr
->flags
.dimsWhenDisabled
) {
226 bPtr
->dimage
->mask
= XCreatePixmap(dpy
, scr
->stipple
,
227 bPtr
->dimage
->width
, bPtr
->dimage
->height
, 1);
229 XSetForeground(dpy
, scr
->monoGC
, 0);
230 XFillRectangle(dpy
, bPtr
->dimage
->mask
, scr
->monoGC
, 0, 0,
231 bPtr
->dimage
->width
, bPtr
->dimage
->height
);
235 gcv
.stipple
= scr
->stipple
;
236 gcv
.fill_style
= FillStippled
;
237 gcv
.clip_mask
= bPtr
->image
->mask
;
238 gcv
.clip_x_origin
= 0;
239 gcv
.clip_y_origin
= 0;
241 XChangeGC(dpy
, scr
->monoGC
, GCForeground
| GCBackground
| GCStipple
242 | GCFillStyle
| GCClipMask
| GCClipXOrigin
| GCClipYOrigin
, &gcv
);
244 XFillRectangle(dpy
, bPtr
->dimage
->mask
, scr
->monoGC
, 0, 0,
245 bPtr
->dimage
->width
, bPtr
->dimage
->height
);
247 gcv
.fill_style
= FillSolid
;
248 gcv
.clip_mask
= None
;
249 XChangeGC(dpy
, scr
->monoGC
, GCFillStyle
| GCClipMask
, &gcv
);
254 void WMSetButtonImageDefault(WMButton
* bPtr
)
256 WMSetButtonImage(bPtr
, WMWidgetScreen(bPtr
)->buttonArrow
);
257 WMSetButtonAltImage(bPtr
, WMWidgetScreen(bPtr
)->pushedButtonArrow
);
260 void WMSetButtonImage(WMButton
* bPtr
, WMPixmap
* image
)
262 if (bPtr
->image
!= NULL
)
263 WMReleasePixmap(bPtr
->image
);
264 bPtr
->image
= WMRetainPixmap(image
);
267 bPtr
->dimage
->pixmap
= None
;
268 WMReleasePixmap(bPtr
->dimage
);
273 bPtr
->dimage
= WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr
),
275 image
->width
, image
->height
, image
->depth
);
276 updateDisabledMask(bPtr
);
279 if (bPtr
->view
->flags
.realized
) {
284 void WMSetButtonAltImage(WMButton
* bPtr
, WMPixmap
* image
)
286 if (bPtr
->altImage
!= NULL
)
287 WMReleasePixmap(bPtr
->altImage
);
288 bPtr
->altImage
= WMRetainPixmap(image
);
290 if (bPtr
->view
->flags
.realized
) {
295 void WMSetButtonImagePosition(WMButton
* bPtr
, WMImagePosition position
)
297 bPtr
->flags
.imagePosition
= position
;
299 if (bPtr
->view
->flags
.realized
) {
304 void WMSetButtonTextAlignment(WMButton
* bPtr
, WMAlignment alignment
)
306 bPtr
->flags
.alignment
= alignment
;
308 if (bPtr
->view
->flags
.realized
) {
313 void WMSetButtonText(WMButton
* bPtr
, char *text
)
316 wfree(bPtr
->caption
);
319 bPtr
->caption
= wstrdup(text
);
321 bPtr
->caption
= NULL
;
324 if (bPtr
->view
->flags
.realized
) {
329 void WMSetButtonAltText(WMButton
* bPtr
, char *text
)
331 if (bPtr
->altCaption
)
332 wfree(bPtr
->altCaption
);
335 bPtr
->altCaption
= wstrdup(text
);
337 bPtr
->altCaption
= NULL
;
340 if (bPtr
->view
->flags
.realized
) {
345 void WMSetButtonTextColor(WMButton
* bPtr
, WMColor
* color
)
348 WMReleaseColor(bPtr
->textColor
);
350 bPtr
->textColor
= WMRetainColor(color
);
353 void WMSetButtonAltTextColor(WMButton
* bPtr
, WMColor
* color
)
355 if (bPtr
->altTextColor
)
356 WMReleaseColor(bPtr
->altTextColor
);
358 bPtr
->altTextColor
= WMRetainColor(color
);
361 void WMSetButtonDisabledTextColor(WMButton
* bPtr
, WMColor
* color
)
363 if (bPtr
->disTextColor
)
364 WMReleaseColor(bPtr
->disTextColor
);
366 bPtr
->disTextColor
= WMRetainColor(color
);
369 void WMSetButtonSelected(WMButton
* bPtr
, int isSelected
)
371 bPtr
->flags
.selected
= isSelected
? 1 : 0;
373 if (bPtr
->view
->flags
.realized
) {
376 if (bPtr
->groupIndex
> 0)
377 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
380 int WMGetButtonSelected(WMButton
* bPtr
)
382 CHECK_CLASS(bPtr
, WC_Button
);
384 return bPtr
->flags
.selected
;
387 void WMSetButtonBordered(WMButton
* bPtr
, int isBordered
)
389 bPtr
->flags
.bordered
= isBordered
;
391 if (bPtr
->view
->flags
.realized
) {
396 void WMSetButtonFont(WMButton
* bPtr
, WMFont
* font
)
399 WMReleaseFont(bPtr
->font
);
401 bPtr
->font
= WMRetainFont(font
);
404 void WMSetButtonEnabled(WMButton
* bPtr
, Bool flag
)
406 bPtr
->flags
.enabled
= ((flag
== 0) ? 0 : 1);
408 if (bPtr
->view
->flags
.mapped
) {
413 int WMGetButtonEnabled(WMButton
* bPtr
)
415 CHECK_CLASS(bPtr
, WC_Button
);
417 return bPtr
->flags
.enabled
;
420 void WMSetButtonImageDimsWhenDisabled(WMButton
* bPtr
, Bool flag
)
422 bPtr
->flags
.dimsWhenDisabled
= ((flag
== 0) ? 0 : 1);
424 updateDisabledMask(bPtr
);
427 void WMSetButtonTag(WMButton
* bPtr
, int tag
)
432 void WMPerformButtonClick(WMButton
* bPtr
)
434 CHECK_CLASS(bPtr
, WC_Button
);
436 if (!bPtr
->flags
.enabled
)
439 bPtr
->flags
.pushed
= 1;
440 bPtr
->flags
.selected
= 1;
442 if (bPtr
->view
->flags
.mapped
) {
444 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr
)));
448 bPtr
->flags
.pushed
= 0;
450 if (bPtr
->groupIndex
> 0) {
451 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
455 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
457 if (bPtr
->view
->flags
.mapped
)
461 void WMSetButtonAction(WMButton
* bPtr
, WMAction
* action
, void *clientData
)
463 CHECK_CLASS(bPtr
, WC_Button
);
465 bPtr
->action
= action
;
467 bPtr
->clientData
= clientData
;
470 static void radioPushObserver(void *observerData
, WMNotification
* notification
)
472 WMButton
*bPtr
= (WMButton
*) observerData
;
473 WMButton
*pushedButton
= (WMButton
*) WMGetNotificationObject(notification
);
475 if (bPtr
!= pushedButton
&& pushedButton
->groupIndex
== bPtr
->groupIndex
&& bPtr
->groupIndex
!= 0) {
476 if (bPtr
->flags
.selected
) {
477 bPtr
->flags
.selected
= 0;
483 void WMGroupButtons(WMButton
* bPtr
, WMButton
* newMember
)
485 static int tagIndex
= 0;
487 CHECK_CLASS(bPtr
, WC_Button
);
488 CHECK_CLASS(newMember
, WC_Button
);
490 if (!bPtr
->flags
.addedObserver
) {
491 WMAddNotificationObserver(radioPushObserver
, bPtr
, WMPushedRadioNotification
, NULL
);
492 bPtr
->flags
.addedObserver
= 1;
494 if (!newMember
->flags
.addedObserver
) {
495 WMAddNotificationObserver(radioPushObserver
, newMember
, WMPushedRadioNotification
, NULL
);
496 newMember
->flags
.addedObserver
= 1;
499 if (bPtr
->groupIndex
== 0) {
500 bPtr
->groupIndex
= ++tagIndex
;
502 newMember
->groupIndex
= bPtr
->groupIndex
;
505 void WMSetButtonContinuous(WMButton
* bPtr
, Bool flag
)
507 bPtr
->flags
.continuous
= ((flag
== 0) ? 0 : 1);
509 WMDeleteTimerHandler(bPtr
->timer
);
514 void WMSetButtonPeriodicDelay(WMButton
* bPtr
, float delay
, float interval
)
516 bPtr
->periodicInterval
= interval
;
517 bPtr
->periodicDelay
= delay
;
520 static void paintButton(Button
* bPtr
)
522 W_Screen
*scrPtr
= bPtr
->view
->screen
;
531 caption
= bPtr
->caption
;
533 if (bPtr
->flags
.enabled
) {
534 textColor
= (bPtr
->textColor
!= NULL
? bPtr
->textColor
: scrPtr
->black
);
536 textColor
= (bPtr
->disTextColor
!= NULL
? bPtr
->disTextColor
: scrPtr
->darkGray
);
539 if (bPtr
->flags
.enabled
|| !bPtr
->dimage
)
542 image
= bPtr
->dimage
;
544 if (bPtr
->flags
.bordered
)
549 if (bPtr
->flags
.selected
) {
550 if (bPtr
->flags
.stateLight
) {
551 backColor
= scrPtr
->white
;
552 textColor
= scrPtr
->black
;
555 if (bPtr
->flags
.stateChange
) {
556 if (bPtr
->altCaption
)
557 caption
= bPtr
->altCaption
;
559 image
= bPtr
->altImage
;
560 if (bPtr
->altTextColor
)
561 textColor
= bPtr
->altTextColor
;
564 if (bPtr
->flags
.statePush
&& bPtr
->flags
.bordered
) {
570 if (bPtr
->flags
.pushed
) {
571 if (bPtr
->flags
.pushIn
) {
575 if (bPtr
->flags
.pushLight
) {
576 backColor
= scrPtr
->white
;
577 textColor
= scrPtr
->black
;
580 if (bPtr
->flags
.pushChange
) {
581 if (bPtr
->altCaption
)
582 caption
= bPtr
->altCaption
;
584 image
= bPtr
->altImage
;
585 if (bPtr
->altTextColor
)
586 textColor
= bPtr
->altTextColor
;
590 W_PaintTextAndImage(bPtr
->view
, True
, textColor
,
591 (bPtr
->font
!= NULL
? bPtr
->font
: scrPtr
->normalFont
),
592 relief
, caption
, bPtr
->flags
.alignment
, image
,
593 bPtr
->flags
.imagePosition
, backColor
, offset
);
596 static void handleEvents(XEvent
* event
, void *data
)
598 Button
*bPtr
= (Button
*) data
;
600 CHECK_CLASS(data
, WC_Button
);
602 switch (event
->type
) {
604 if (event
->xexpose
.count
!= 0)
615 static void autoRepeat(void *data
)
617 Button
*bPtr
= (Button
*) data
;
619 if (bPtr
->action
&& bPtr
->flags
.pushed
)
620 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
622 bPtr
->timer
= WMAddTimerHandler((int)(bPtr
->periodicInterval
* 1000), autoRepeat
, bPtr
);
625 static void handleActionEvents(XEvent
* event
, void *data
)
627 Button
*bPtr
= (Button
*) data
;
628 int doclick
= 0, dopaint
= 0;
630 CHECK_CLASS(data
, WC_Button
);
632 if (!bPtr
->flags
.enabled
)
635 switch (event
->type
) {
637 if (bPtr
->groupIndex
== 0) {
638 bPtr
->flags
.pushed
= bPtr
->flags
.wasPushed
;
639 if (bPtr
->flags
.pushed
) {
640 bPtr
->flags
.selected
= !bPtr
->flags
.prevSelected
;
647 if (bPtr
->groupIndex
== 0) {
648 bPtr
->flags
.wasPushed
= bPtr
->flags
.pushed
;
649 if (bPtr
->flags
.pushed
) {
650 bPtr
->flags
.selected
= bPtr
->flags
.prevSelected
;
653 bPtr
->flags
.pushed
= 0;
658 if (event
->xbutton
.button
== Button1
) {
659 bPtr
->flags
.prevSelected
= bPtr
->flags
.selected
;
660 bPtr
->flags
.wasPushed
= 0;
661 bPtr
->flags
.pushed
= 1;
662 if (bPtr
->groupIndex
> 0) {
663 bPtr
->flags
.selected
= 1;
667 bPtr
->flags
.selected
= !bPtr
->flags
.selected
;
670 if (bPtr
->flags
.continuous
&& !bPtr
->timer
) {
671 bPtr
->timer
= WMAddTimerHandler((int)(bPtr
->periodicDelay
* 1000),
678 if (event
->xbutton
.button
== Button1
) {
679 if (bPtr
->flags
.pushed
) {
680 if (bPtr
->groupIndex
== 0 || (bPtr
->flags
.selected
&& bPtr
->groupIndex
> 0))
683 if (bPtr
->flags
.springLoaded
) {
684 bPtr
->flags
.selected
= bPtr
->flags
.prevSelected
;
687 bPtr
->flags
.pushed
= 0;
690 WMDeleteTimerHandler(bPtr
->timer
);
700 if (bPtr
->flags
.selected
&& bPtr
->groupIndex
> 0) {
701 WMPostNotificationName(WMPushedRadioNotification
, bPtr
, NULL
);
705 (*bPtr
->action
) (bPtr
, bPtr
->clientData
);
709 static void destroyButton(Button
* bPtr
)
711 if (bPtr
->flags
.addedObserver
) {
712 WMRemoveNotificationObserver(bPtr
);
716 WMDeleteTimerHandler(bPtr
->timer
);
719 WMReleaseFont(bPtr
->font
);
722 wfree(bPtr
->caption
);
724 if (bPtr
->altCaption
)
725 wfree(bPtr
->altCaption
);
728 WMReleaseColor(bPtr
->textColor
);
730 if (bPtr
->altTextColor
)
731 WMReleaseColor(bPtr
->altTextColor
);
733 if (bPtr
->disTextColor
)
734 WMReleaseColor(bPtr
->disTextColor
);
737 WMReleasePixmap(bPtr
->image
);
741 bPtr
->dimage
->pixmap
= None
;
743 WMReleasePixmap(bPtr
->dimage
);
746 WMReleasePixmap(bPtr
->altImage
);