WINGs buttons and labels now have text
[wmaker-crm.git] / WINGs / wbutton.c
blob77e4cdd09ccfe2b1f863750a4de8f79db3daa71f
2 #include "WINGsP.h"
4 typedef struct W_Button {
5 W_Class widgetClass;
6 WMView *view;
8 char *caption;
10 char *altCaption;
12 WMFont *font;
14 WMColorSpec textColor;
15 WMColorSpec altTextColor;
16 WMColorSpec disTextColor;
18 WMImage *image;
19 WMImage *altImage;
21 void *clientData;
22 WMAction *action;
24 int tag;
26 int groupIndex;
28 float periodicDelay;
29 float periodicInterval;
31 WMHandlerID *timer; /* for continuous mode */
33 struct {
34 WMButtonType type:4;
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;
71 } flags;
72 } Button;
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)
103 Button *bPtr;
105 bPtr = wmalloc(sizeof(Button));
106 memset(bPtr, 0, sizeof(Button));
108 bPtr->widgetClass = WC_Button;
110 bPtr->view = W_CreateView(W_VIEW(parent));
111 if (!bPtr->view) {
112 wfree(bPtr);
113 return NULL;
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;
147 return bPtr;
150 WMButton *WMCreateButton(WMWidget * parent, WMButtonType type)
152 W_Screen *scrPtr = W_VIEW(parent)->screen;
153 Button *bPtr;
155 switch (type) {
156 case WBTMomentaryPush:
157 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushInMask | WBBPushLightMask);
158 break;
160 case WBTMomentaryChange:
161 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushChangeMask);
162 break;
164 case WBTPushOnPushOff:
165 bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStatePushMask | WBBStateLightMask);
166 break;
168 case WBTToggle:
169 bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStateChangeMask | WBBStatePushMask);
170 break;
172 case WBTOnOff:
173 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
174 break;
176 case WBTSwitch:
177 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
178 bPtr->flags.bordered = 0;
179 bPtr->image = WMRetainImage(scrPtr->checkButtonImageOff);
180 bPtr->altImage = WMRetainImage(scrPtr->checkButtonImageOn);
181 break;
183 case WBTRadio:
184 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
185 bPtr->flags.bordered = 0;
186 bPtr->image = WMRetainImage(scrPtr->radioButtonImageOff);
187 bPtr->altImage = WMRetainImage(scrPtr->radioButtonImageOn);
188 break;
190 default:
191 case WBTMomentaryLight:
192 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushLightMask);
193 bPtr->flags.bordered = 1;
194 break;
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;
211 return bPtr;
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) {
227 paintButton(bPtr);
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) {
238 paintButton(bPtr);
242 void WMSetButtonImagePosition(WMButton * bPtr, WMImagePosition position)
244 bPtr->flags.imagePosition = position;
246 if (bPtr->view->flags.realized) {
247 paintButton(bPtr);
251 void WMSetButtonTextAlignment(WMButton * bPtr, WMAlignment alignment)
253 bPtr->flags.alignment = alignment;
255 if (bPtr->view->flags.realized) {
256 paintButton(bPtr);
260 void WMSetButtonText(WMButton * bPtr, char *text)
262 if (bPtr->caption)
263 wfree(bPtr->caption);
265 if (text != NULL) {
266 bPtr->caption = wstrdup(text);
267 } else {
268 bPtr->caption = NULL;
271 if (bPtr->view->flags.realized) {
272 paintButton(bPtr);
276 void WMSetButtonAltText(WMButton * bPtr, char *text)
278 if (bPtr->altCaption)
279 wfree(bPtr->altCaption);
281 if (text != NULL) {
282 bPtr->altCaption = wstrdup(text);
283 } else {
284 bPtr->altCaption = NULL;
287 if (bPtr->view->flags.realized) {
288 paintButton(bPtr);
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) {
312 paintButton(bPtr);
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) {
330 paintButton(bPtr);
334 void WMSetButtonFont(WMButton * bPtr, WMFont * font)
336 if (bPtr->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) {
347 paintButton(bPtr);
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)
365 bPtr->tag = tag;
368 void WMPerformButtonClick(WMButton * bPtr)
370 CHECK_CLASS(bPtr, WC_Button);
372 if (!bPtr->flags.enabled)
373 return;
375 bPtr->flags.pushed = 1;
376 bPtr->flags.selected = 1;
378 if (bPtr->view->flags.mapped) {
379 paintButton(bPtr);
380 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
381 wusleep(20000);
384 bPtr->flags.pushed = 0;
386 if (bPtr->groupIndex > 0) {
387 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
390 if (bPtr->action)
391 (*bPtr->action) (bPtr, bPtr->clientData);
393 if (bPtr->view->flags.mapped)
394 paintButton(bPtr);
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;
414 paintButton(bPtr);
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);
444 if (bPtr->timer) {
445 WMDeleteTimerHandler(bPtr->timer);
446 bPtr->timer = NULL;
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;
460 WMReliefType relief;
461 int offset;
462 const char *caption;
463 WMImage *image;
464 WMColorSpec textColor;
465 WMColorSpec backColor;
467 backColor = bPtr->view->backColor;
468 caption = bPtr->caption;
470 if (bPtr->flags.enabled) {
471 textColor = bPtr->textColor;
472 } else {
473 textColor = bPtr->disTextColor;
476 if (bPtr->flags.enabled || !bPtr->flags.dimsWhenDisabled)
477 image = WMRetainImage(bPtr->image);
478 else
480 //XXX make dimmited image
481 image = bPtr->image;
483 offset = 0;
484 if (bPtr->flags.bordered)
485 relief = WRRaised;
486 else
487 relief = WRFlat;
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;
498 if (bPtr->altImage)
499 image = bPtr->altImage;
500 textColor = bPtr->altTextColor;
503 if (bPtr->flags.statePush && bPtr->flags.bordered) {
504 relief = WRSunken;
505 offset = 1;
509 if (bPtr->flags.pushed) {
510 if (bPtr->flags.pushIn) {
511 relief = WRPushed;
512 offset = 1;
514 if (bPtr->flags.pushLight) {
515 backColor = WMWhiteColorSpec();
516 textColor = WMBlackColorSpec();
519 if (bPtr->flags.pushChange) {
520 if (bPtr->altCaption)
521 caption = bPtr->altCaption;
522 if (bPtr->altImage)
523 image = bPtr->altImage;
524 textColor = bPtr->altTextColor;
529 W_DrawButtonRelief(scrPtr, cr, 0, 0,
530 bPtr->view->size.width, bPtr->view->size.height,
531 relief);
533 relief= WRFlat;
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);
539 if (image)
540 WMDestroyImage(image);
542 cairo_destroy(cr);
546 static void handleEvents(XEvent * event, void *data)
548 Button *bPtr = (Button *) data;
550 CHECK_CLASS(data, WC_Button);
552 switch (event->type) {
553 case Expose:
554 if (event->xexpose.count != 0)
555 break;
556 paintButton(bPtr);
557 break;
559 case DestroyNotify:
560 destroyButton(bPtr);
561 break;
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)
583 return;
585 switch (event->type) {
586 case EnterNotify:
587 if (bPtr->groupIndex == 0) {
588 bPtr->flags.pushed = bPtr->flags.wasPushed;
589 if (bPtr->flags.pushed) {
590 bPtr->flags.selected = !bPtr->flags.prevSelected;
591 dopaint = 1;
594 break;
596 case LeaveNotify:
597 if (bPtr->groupIndex == 0) {
598 bPtr->flags.wasPushed = bPtr->flags.pushed;
599 if (bPtr->flags.pushed) {
600 bPtr->flags.selected = bPtr->flags.prevSelected;
601 dopaint = 1;
603 bPtr->flags.pushed = 0;
605 break;
607 case ButtonPress:
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;
614 dopaint = 1;
615 break;
617 bPtr->flags.selected = !bPtr->flags.selected;
618 dopaint = 1;
620 if (bPtr->flags.continuous && !bPtr->timer) {
621 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay * 1000),
622 autoRepeat, bPtr);
625 break;
627 case ButtonRelease:
628 if (event->xbutton.button == Button1) {
629 if (bPtr->flags.pushed) {
630 if (bPtr->groupIndex == 0 || (bPtr->flags.selected && bPtr->groupIndex > 0))
631 doclick = 1;
632 dopaint = 1;
633 if (bPtr->flags.springLoaded) {
634 bPtr->flags.selected = bPtr->flags.prevSelected;
637 bPtr->flags.pushed = 0;
639 if (bPtr->timer) {
640 WMDeleteTimerHandler(bPtr->timer);
641 bPtr->timer = NULL;
643 break;
646 if (dopaint)
647 paintButton(bPtr);
649 if (doclick) {
650 if (bPtr->flags.selected && bPtr->groupIndex > 0) {
651 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
654 if (bPtr->action)
655 (*bPtr->action) (bPtr, bPtr->clientData);
659 static void destroyButton(Button * bPtr)
661 if (bPtr->flags.addedObserver) {
662 WMRemoveNotificationObserver(bPtr);
665 if (bPtr->timer)
666 WMDeleteTimerHandler(bPtr->timer);
668 if (bPtr->font)
669 WMReleaseFont(bPtr->font);
671 if (bPtr->caption)
672 wfree(bPtr->caption);
674 if (bPtr->altCaption)
675 wfree(bPtr->altCaption);
677 if (bPtr->image)
678 WMDestroyImage(bPtr->image);
680 if (bPtr->altImage)
681 WMDestroyImage(bPtr->altImage);
683 wfree(bPtr);