Radio and Check buttons now work
[wmaker-crm.git] / WINGs / wbutton.c
blob3c8eb655c42afdb23e5eca6a548b4e8c5ad8e387
2 #include "WINGsP.h"
3 #include "wtheme.h"
5 typedef struct W_Button {
6 W_Class widgetClass;
7 WMView *view;
9 char *caption;
11 char *altCaption;
13 WMFont *font;
15 WMColorSpec textColor;
16 WMColorSpec altTextColor;
17 WMColorSpec disTextColor;
19 WMImage *image;
20 WMImage *altImage;
22 void *clientData;
23 WMAction *action;
25 int tag;
27 int groupIndex;
29 float periodicDelay;
30 float periodicInterval;
32 WMHandlerID *timer; /* for continuous mode */
34 struct {
35 WMButtonType type:4;
36 WMImagePosition imagePosition:4;
37 WMAlignment alignment:2;
39 unsigned int selected:1;
41 unsigned int enabled:1;
43 unsigned int dimsWhenDisabled:1;
45 unsigned int bordered:1;
47 unsigned int springLoaded:1;
49 unsigned int pushIn:1; /* change relief while pushed */
51 unsigned int pushLight:1; /* highlight while pushed */
53 unsigned int pushChange:1; /* change caption while pushed */
55 unsigned int stateLight:1; /* state indicated by highlight */
57 unsigned int stateChange:1; /* state indicated by caption change */
59 unsigned int statePush:1; /* state indicated by relief */
61 unsigned int continuous:1; /* continually perform action */
63 unsigned int prevSelected:1;
65 unsigned int pushed:1;
67 unsigned int wasPushed:1;
69 unsigned int redrawPending:1;
71 unsigned int addedObserver:1;
72 } flags;
73 } Button;
75 #define DEFAULT_BUTTON_WIDTH 60
76 #define DEFAULT_BUTTON_HEIGHT 24
77 #define DEFAULT_BUTTON_ALIGNMENT WACenter
78 #define DEFAULT_BUTTON_IS_BORDERED True
80 #define DEFAULT_RADIO_WIDTH 100
81 #define DEFAULT_RADIO_HEIGHT 20
82 #define DEFAULT_RADIO_ALIGNMENT WALeft
83 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
84 #define DEFAULT_RADIO_TEXT "Radio"
86 #define DEFAULT_SWITCH_WIDTH 100
87 #define DEFAULT_SWITCH_HEIGHT 20
88 #define DEFAULT_SWITCH_ALIGNMENT WALeft
89 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
90 #define DEFAULT_SWITCH_TEXT "Switch"
92 static void destroyButton(Button * bPtr);
93 static void paintButton(Button * bPtr);
95 static void handleEvents(XEvent * event, void *data);
96 static void handleActionEvents(XEvent * event, void *data);
98 static char *WMPushedRadioNotification = "WMPushedRadioNotification";
100 #define NFONT(b) (b)->view->screen->normalFont
102 WMButton *WMCreateCustomButton(WMWidget * parent, int behaviourMask)
104 Button *bPtr;
106 bPtr = wmalloc(sizeof(Button));
107 memset(bPtr, 0, sizeof(Button));
109 bPtr->widgetClass = WC_Button;
111 bPtr->view = W_CreateView(W_VIEW(parent));
112 if (!bPtr->view) {
113 wfree(bPtr);
114 return NULL;
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 bPtr->textColor= WMBlackColorSpec();
136 bPtr->altTextColor= WMBlackColorSpec();
137 bPtr->disTextColor= WMDarkGrayColorSpec();
139 WMCreateEventHandler(bPtr->view, ExposureMask | StructureNotifyMask, handleEvents, bPtr);
141 WMCreateEventHandler(bPtr->view, ButtonPressMask | ButtonReleaseMask
142 | EnterWindowMask | LeaveWindowMask, handleActionEvents, bPtr);
144 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
145 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
146 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
148 return bPtr;
151 WMButton *WMCreateButton(WMWidget * parent, WMButtonType type)
153 W_Screen *scrPtr = W_VIEW(parent)->screen;
154 Button *bPtr;
156 switch (type) {
157 case WBTMomentaryPush:
158 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushInMask | WBBPushLightMask);
159 break;
161 case WBTMomentaryChange:
162 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushChangeMask);
163 break;
165 case WBTPushOnPushOff:
166 bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStatePushMask | WBBStateLightMask);
167 break;
169 case WBTToggle:
170 bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStateChangeMask | WBBStatePushMask);
171 break;
173 case WBTOnOff:
174 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
175 break;
177 case WBTSwitch:
178 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
179 bPtr->flags.bordered = 0;
180 bPtr->image = WMRetainImage(scrPtr->checkButtonImageOff);
181 bPtr->altImage = WMRetainImage(scrPtr->checkButtonImageOn);
182 break;
184 case WBTRadio:
185 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
186 bPtr->flags.bordered = 0;
187 bPtr->image = WMRetainImage(scrPtr->radioButtonImageOff);
188 bPtr->altImage = WMRetainImage(scrPtr->radioButtonImageOn);
189 break;
191 default:
192 case WBTMomentaryLight:
193 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushLightMask);
194 bPtr->flags.bordered = 1;
195 break;
198 bPtr->flags.type = type;
200 if (type == WBTRadio) {
201 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
202 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
203 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
204 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
205 } else if (type == WBTSwitch) {
206 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
207 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
208 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
209 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
212 return bPtr;
215 void WMSetButtonImageDefault(WMButton * bPtr)
217 WMSetButtonImage(bPtr, WMWidgetScreen(bPtr)->buttonArrow);
218 WMSetButtonAltImage(bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
221 void WMSetButtonImage(WMButton *bPtr, WMImage *image)
223 if (bPtr->image != NULL)
224 WMDestroyImage(bPtr->image);
225 bPtr->image = WMRetainImage(image);
227 if (bPtr->view->flags.realized) {
228 paintButton(bPtr);
232 void WMSetButtonAltImage(WMButton * bPtr, WMImage * image)
234 if (bPtr->altImage != NULL)
235 WMDestroyImage(bPtr->altImage);
236 bPtr->altImage = WMRetainImage(image);
238 if (bPtr->view->flags.realized) {
239 paintButton(bPtr);
243 void WMSetButtonImagePosition(WMButton * bPtr, WMImagePosition position)
245 bPtr->flags.imagePosition = position;
247 if (bPtr->view->flags.realized) {
248 paintButton(bPtr);
252 void WMSetButtonTextAlignment(WMButton * bPtr, WMAlignment alignment)
254 bPtr->flags.alignment = alignment;
256 if (bPtr->view->flags.realized) {
257 paintButton(bPtr);
261 void WMSetButtonText(WMButton * bPtr, char *text)
263 if (bPtr->caption)
264 wfree(bPtr->caption);
266 if (text != NULL) {
267 bPtr->caption = wstrdup(text);
268 } else {
269 bPtr->caption = NULL;
272 if (bPtr->view->flags.realized) {
273 paintButton(bPtr);
277 void WMSetButtonAltText(WMButton * bPtr, char *text)
279 if (bPtr->altCaption)
280 wfree(bPtr->altCaption);
282 if (text != NULL) {
283 bPtr->altCaption = wstrdup(text);
284 } else {
285 bPtr->altCaption = NULL;
288 if (bPtr->view->flags.realized) {
289 paintButton(bPtr);
293 void WMSetButtonTextColor(WMButton *bPtr, WMColorSpec *color)
295 bPtr->textColor = *color;
298 void WMSetButtonAltTextColor(WMButton *bPtr, WMColorSpec *color)
300 bPtr->altTextColor = *color;
303 void WMSetButtonDisabledTextColor(WMButton *bPtr, WMColorSpec *color)
305 bPtr->disTextColor = *color;
308 void WMSetButtonSelected(WMButton * bPtr, int isSelected)
310 bPtr->flags.selected = isSelected ? 1 : 0;
312 if (bPtr->view->flags.realized) {
313 paintButton(bPtr);
315 if (bPtr->groupIndex > 0)
316 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
319 int WMGetButtonSelected(WMButton * bPtr)
321 CHECK_CLASS(bPtr, WC_Button);
323 return bPtr->flags.selected;
326 void WMSetButtonBordered(WMButton * bPtr, int isBordered)
328 bPtr->flags.bordered = isBordered;
330 if (bPtr->view->flags.realized) {
331 paintButton(bPtr);
335 void WMSetButtonFont(WMButton * bPtr, WMFont * font)
337 if (bPtr->font)
338 WMReleaseFont(bPtr->font);
340 bPtr->font = WMRetainFont(font);
343 void WMSetButtonEnabled(WMButton * bPtr, Bool flag)
345 bPtr->flags.enabled = ((flag == 0) ? 0 : 1);
347 if (bPtr->view->flags.mapped) {
348 paintButton(bPtr);
352 int WMGetButtonEnabled(WMButton * bPtr)
354 CHECK_CLASS(bPtr, WC_Button);
356 return bPtr->flags.enabled;
359 void WMSetButtonImageDimsWhenDisabled(WMButton * bPtr, Bool flag)
361 bPtr->flags.dimsWhenDisabled = ((flag == 0) ? 0 : 1);
364 void WMSetButtonTag(WMButton * bPtr, int tag)
366 bPtr->tag = tag;
369 void WMPerformButtonClick(WMButton * bPtr)
371 CHECK_CLASS(bPtr, WC_Button);
373 if (!bPtr->flags.enabled)
374 return;
376 bPtr->flags.pushed = 1;
377 bPtr->flags.selected = 1;
379 if (bPtr->view->flags.mapped) {
380 paintButton(bPtr);
381 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
382 wusleep(20000);
385 bPtr->flags.pushed = 0;
387 if (bPtr->groupIndex > 0) {
388 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
391 if (bPtr->action)
392 (*bPtr->action) (bPtr, bPtr->clientData);
394 if (bPtr->view->flags.mapped)
395 paintButton(bPtr);
398 void WMSetButtonAction(WMButton * bPtr, WMAction * action, void *clientData)
400 CHECK_CLASS(bPtr, WC_Button);
402 bPtr->action = action;
404 bPtr->clientData = clientData;
407 static void radioPushObserver(void *observerData, WMNotification * notification)
409 WMButton *bPtr = (WMButton *) observerData;
410 WMButton *pushedButton = (WMButton *) WMGetNotificationObject(notification);
412 if (bPtr != pushedButton && pushedButton->groupIndex == bPtr->groupIndex && bPtr->groupIndex != 0) {
413 if (bPtr->flags.selected) {
414 bPtr->flags.selected = 0;
415 paintButton(bPtr);
420 void WMGroupButtons(WMButton * bPtr, WMButton * newMember)
422 static int tagIndex = 0;
424 CHECK_CLASS(bPtr, WC_Button);
425 CHECK_CLASS(newMember, WC_Button);
427 if (!bPtr->flags.addedObserver) {
428 WMAddNotificationObserver(radioPushObserver, bPtr, WMPushedRadioNotification, NULL);
429 bPtr->flags.addedObserver = 1;
431 if (!newMember->flags.addedObserver) {
432 WMAddNotificationObserver(radioPushObserver, newMember, WMPushedRadioNotification, NULL);
433 newMember->flags.addedObserver = 1;
436 if (bPtr->groupIndex == 0) {
437 bPtr->groupIndex = ++tagIndex;
439 newMember->groupIndex = bPtr->groupIndex;
442 void WMSetButtonContinuous(WMButton * bPtr, Bool flag)
444 bPtr->flags.continuous = ((flag == 0) ? 0 : 1);
445 if (bPtr->timer) {
446 WMDeleteTimerHandler(bPtr->timer);
447 bPtr->timer = NULL;
451 void WMSetButtonPeriodicDelay(WMButton * bPtr, float delay, float interval)
453 bPtr->periodicInterval = interval;
454 bPtr->periodicDelay = delay;
457 static void paintButton(WMButton * bPtr)
459 cairo_t *cr = W_CreateCairoForView(W_VIEW(bPtr));
460 W_Screen *scrPtr = bPtr->view->screen;
461 WMReliefType relief;
462 int offset;
463 const char *caption;
464 WMImage *image;
465 unsigned int lightbg = 0;
467 WMColorSpec textColor;
468 WMColorSpec backColor;
470 backColor = bPtr->view->backColor;
471 caption = bPtr->caption;
473 if (bPtr->flags.enabled) {
474 textColor = bPtr->textColor;
475 } else {
476 textColor = bPtr->disTextColor;
479 if (bPtr->flags.enabled || !bPtr->flags.dimsWhenDisabled) {
480 image = WMRetainImage(bPtr->image);
481 } else {
482 //XXX make dimmited image
483 image = WMRetainImage(bPtr->image);
485 offset = 0;
486 if (bPtr->flags.bordered)
487 relief = WRRaised;
488 else
489 relief = WRFlat;
491 if (bPtr->flags.selected) {
492 if (bPtr->flags.stateLight) {
493 lightbg = 1;
496 if (bPtr->flags.stateChange) {
497 if (bPtr->altCaption)
498 caption = bPtr->altCaption;
499 if (bPtr->altImage) {
500 WMDestroyImage(image);
501 image = WMRetainImage(bPtr->altImage);
502 textColor = bPtr->altTextColor;
506 if (bPtr->flags.statePush && bPtr->flags.bordered) {
507 relief = WRSunken;
508 offset = 1;
512 if (bPtr->flags.pushed) {
513 if (bPtr->flags.pushIn) {
514 relief = WRPushed;
515 offset = 1;
517 if (bPtr->flags.pushLight) {
518 lightbg = 1;
521 if (bPtr->flags.pushChange) {
522 if (bPtr->altCaption)
523 caption = bPtr->altCaption;
524 if (bPtr->altImage) {
525 WMDestroyImage(image);
526 image = WMRetainImage(bPtr->altImage);
527 textColor = bPtr->altTextColor;
532 if (lightbg == 1) {
533 W_DrawButtonLightBack(cr, bPtr, bPtr->view->size.width, bPtr->view->size.height,relief);
534 } else {
535 W_DrawButtonDarkBack(cr, bPtr, bPtr->view->size.width, bPtr->view->size.height,relief);
538 W_DrawButtonRelief(cr, bPtr->view->size.width, bPtr->view->size.height, relief);
540 W_PaintTextAndImage(scrPtr, cr, bPtr->view, True, &textColor,
541 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
542 relief, caption, bPtr->flags.alignment, image,
543 bPtr->flags.imagePosition, &backColor, offset);
545 if (image) {
546 WMDestroyImage(image);
549 cairo_destroy(cr);
553 static void handleEvents(XEvent * event, void *data)
555 Button *bPtr = (Button *) data;
557 CHECK_CLASS(data, WC_Button);
559 switch (event->type) {
560 case Expose:
561 if (event->xexpose.count != 0)
562 break;
563 paintButton(bPtr);
564 break;
566 case DestroyNotify:
567 destroyButton(bPtr);
568 break;
572 static void autoRepeat(void *data)
574 Button *bPtr = (Button *) data;
576 if (bPtr->action && bPtr->flags.pushed)
577 (*bPtr->action) (bPtr, bPtr->clientData);
579 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval * 1000), autoRepeat, bPtr);
582 static void handleActionEvents(XEvent * event, void *data)
584 Button *bPtr = (Button *) data;
585 int doclick = 0, dopaint = 0;
587 CHECK_CLASS(data, WC_Button);
589 if (!bPtr->flags.enabled)
590 return;
592 switch (event->type) {
593 case EnterNotify:
594 if (bPtr->groupIndex == 0) {
595 bPtr->flags.pushed = bPtr->flags.wasPushed;
596 if (bPtr->flags.pushed) {
597 bPtr->flags.selected = !bPtr->flags.prevSelected;
598 dopaint = 1;
601 break;
603 case LeaveNotify:
604 if (bPtr->groupIndex == 0) {
605 bPtr->flags.wasPushed = bPtr->flags.pushed;
606 if (bPtr->flags.pushed) {
607 bPtr->flags.selected = bPtr->flags.prevSelected;
608 dopaint = 1;
610 bPtr->flags.pushed = 0;
612 break;
614 case ButtonPress:
615 if (event->xbutton.button == Button1) {
616 bPtr->flags.prevSelected = bPtr->flags.selected;
617 bPtr->flags.wasPushed = 0;
618 bPtr->flags.pushed = 1;
619 if (bPtr->groupIndex > 0) {
620 bPtr->flags.selected = 1;
621 dopaint = 1;
622 break;
624 bPtr->flags.selected = !bPtr->flags.selected;
625 dopaint = 1;
627 if (bPtr->flags.continuous && !bPtr->timer) {
628 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay * 1000),
629 autoRepeat, bPtr);
632 break;
634 case ButtonRelease:
635 if (event->xbutton.button == Button1) {
636 if (bPtr->flags.pushed) {
637 if (bPtr->groupIndex == 0 || (bPtr->flags.selected && bPtr->groupIndex > 0))
638 doclick = 1;
639 dopaint = 1;
640 if (bPtr->flags.springLoaded) {
641 bPtr->flags.selected = bPtr->flags.prevSelected;
644 bPtr->flags.pushed = 0;
646 if (bPtr->timer) {
647 WMDeleteTimerHandler(bPtr->timer);
648 bPtr->timer = NULL;
650 break;
653 if (dopaint)
654 paintButton(bPtr);
656 if (doclick) {
657 if (bPtr->flags.selected && bPtr->groupIndex > 0) {
658 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
661 if (bPtr->action)
662 (*bPtr->action) (bPtr, bPtr->clientData);
666 static void destroyButton(Button * bPtr)
668 if (bPtr->flags.addedObserver) {
669 WMRemoveNotificationObserver(bPtr);
672 if (bPtr->timer)
673 WMDeleteTimerHandler(bPtr->timer);
675 if (bPtr->font)
676 WMReleaseFont(bPtr->font);
678 if (bPtr->caption)
679 wfree(bPtr->caption);
681 if (bPtr->altCaption)
682 wfree(bPtr->altCaption);
684 if (bPtr->image)
685 WMDestroyImage(bPtr->image);
687 if (bPtr->altImage)
688 WMDestroyImage(bPtr->altImage);
690 wfree(bPtr);