changed indentation to use spaces only
[wmaker-crm.git] / WINGs / wbutton.c
blob3ea6752bd9e1c716c03c1959248d5b6e2e944749
5 #include "WINGsP.h"
7 typedef struct W_Button {
8 W_Class widgetClass;
9 WMView *view;
11 char *caption;
13 char *altCaption;
15 WMFont *font;
17 WMColor *textColor;
18 WMColor *altTextColor;
19 WMColor *disTextColor;
21 W_Pixmap *image;
22 W_Pixmap *altImage;
24 W_Pixmap *dimage;
26 void *clientData;
27 WMAction *action;
29 int tag;
31 int groupIndex;
33 float periodicDelay;
34 float periodicInterval;
36 WMHandlerID *timer; /* for continuous mode */
38 struct {
39 WMButtonType type:4;
40 WMImagePosition imagePosition:4;
41 WMAlignment alignment:2;
43 unsigned int selected:1;
45 unsigned int enabled:1;
47 unsigned int dimsWhenDisabled:1;
49 unsigned int bordered:1;
51 unsigned int springLoaded:1;
53 unsigned int pushIn:1; /* change relief while pushed */
55 unsigned int pushLight:1; /* highlight while pushed */
57 unsigned int pushChange:1; /* change caption while pushed */
59 unsigned int stateLight:1; /* state indicated by highlight */
61 unsigned int stateChange:1; /* state indicated by caption change */
63 unsigned int statePush:1; /* state indicated by relief */
65 unsigned int continuous:1; /* continually perform action */
67 unsigned int prevSelected:1;
69 unsigned int pushed:1;
71 unsigned int wasPushed:1;
73 unsigned int redrawPending:1;
75 unsigned int addedObserver:1;
76 } flags;
77 } Button;
81 #define DEFAULT_BUTTON_WIDTH 60
82 #define DEFAULT_BUTTON_HEIGHT 24
83 #define DEFAULT_BUTTON_ALIGNMENT WACenter
84 #define DEFAULT_BUTTON_IS_BORDERED True
87 #define DEFAULT_RADIO_WIDTH 100
88 #define DEFAULT_RADIO_HEIGHT 20
89 #define DEFAULT_RADIO_ALIGNMENT WALeft
90 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
91 #define DEFAULT_RADIO_TEXT "Radio"
94 #define DEFAULT_SWITCH_WIDTH 100
95 #define DEFAULT_SWITCH_HEIGHT 20
96 #define DEFAULT_SWITCH_ALIGNMENT WALeft
97 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
98 #define DEFAULT_SWITCH_TEXT "Switch"
101 static void destroyButton(Button *bPtr);
102 static void paintButton(Button *bPtr);
104 static void handleEvents(XEvent *event, void *data);
105 static void handleActionEvents(XEvent *event, void *data);
108 static char *WMPushedRadioNotification="WMPushedRadioNotification";
111 #define NFONT(b) (b)->view->screen->normalFont
114 WMButton*
115 WMCreateCustomButton(WMWidget *parent, int behaviourMask)
117 Button *bPtr;
119 bPtr = wmalloc(sizeof(Button));
120 memset(bPtr, 0, sizeof(Button));
122 bPtr->widgetClass = WC_Button;
124 bPtr->view = W_CreateView(W_VIEW(parent));
125 if (!bPtr->view) {
126 wfree(bPtr);
127 return NULL;
129 bPtr->view->self = bPtr;
131 bPtr->flags.type = 0;
133 bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask)!=0;
134 bPtr->flags.pushIn = (behaviourMask & WBBPushInMask)!=0;
135 bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask)!=0;
136 bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask)!=0;
137 bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask)!=0;
138 bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask)!=0;
139 bPtr->flags.statePush = (behaviourMask & WBBStatePushMask)!=0;
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;
145 bPtr->flags.enabled = 1;
146 bPtr->flags.dimsWhenDisabled = 1;
148 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask,
149 handleEvents, bPtr);
151 WMCreateEventHandler(bPtr->view, ButtonPressMask|ButtonReleaseMask
152 |EnterWindowMask|LeaveWindowMask,
153 handleActionEvents, bPtr);
155 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
156 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
157 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
159 return bPtr;
164 WMButton*
165 WMCreateButton(WMWidget *parent, WMButtonType type)
167 W_Screen *scrPtr = W_VIEW(parent)->screen;
168 Button *bPtr;
170 switch (type) {
171 case WBTMomentaryPush:
172 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
173 |WBBPushInMask|WBBPushLightMask);
174 break;
176 case WBTMomentaryChange:
177 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
178 |WBBPushChangeMask);
179 break;
181 case WBTPushOnPushOff:
182 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStatePushMask
183 |WBBStateLightMask);
184 break;
186 case WBTToggle:
187 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStateChangeMask
188 |WBBStatePushMask);
189 break;
191 case WBTOnOff:
192 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
193 break;
195 case WBTSwitch:
196 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
197 bPtr->flags.bordered = 0;
198 bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
199 bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
200 break;
202 case WBTRadio:
203 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
204 bPtr->flags.bordered = 0;
205 bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
206 bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
207 break;
209 default:
210 case WBTMomentaryLight:
211 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
212 |WBBPushLightMask);
213 bPtr->flags.bordered = 1;
214 break;
217 bPtr->flags.type = type;
219 if (type==WBTRadio) {
220 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
221 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
222 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
223 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
224 } else if (type==WBTSwitch) {
225 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
226 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
227 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
228 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
231 return bPtr;
235 static void
236 updateDisabledMask(WMButton *bPtr)
238 WMScreen *scr = WMWidgetScreen(bPtr);
239 Display *dpy = scr->display;
241 if (bPtr->image) {
242 XGCValues gcv;
244 if (bPtr->dimage->mask) {
245 XFreePixmap(dpy, bPtr->dimage->mask);
246 bPtr->dimage->mask = None;
249 if (bPtr->flags.dimsWhenDisabled) {
250 bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
251 bPtr->dimage->width,
252 bPtr->dimage->height, 1);
254 XSetForeground(dpy, scr->monoGC, 0);
255 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
256 bPtr->dimage->width, bPtr->dimage->height);
258 gcv.foreground = 1;
259 gcv.background = 0;
260 gcv.stipple = scr->stipple;
261 gcv.fill_style = FillStippled;
262 gcv.clip_mask = bPtr->image->mask;
263 gcv.clip_x_origin = 0;
264 gcv.clip_y_origin = 0;
266 XChangeGC(dpy, scr->monoGC, GCForeground|GCBackground|GCStipple
267 |GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin, &gcv);
269 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
270 bPtr->dimage->width, bPtr->dimage->height);
272 gcv.fill_style = FillSolid;
273 gcv.clip_mask = None;
274 XChangeGC(dpy, scr->monoGC, GCFillStyle|GCClipMask, &gcv);
279 void
280 WMSetButtonImageDefault(WMButton *bPtr)
282 WMSetButtonImage (bPtr, WMWidgetScreen(bPtr)->buttonArrow);
283 WMSetButtonAltImage (bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
286 void
287 WMSetButtonImage(WMButton *bPtr, WMPixmap *image)
289 if (bPtr->image!=NULL)
290 WMReleasePixmap(bPtr->image);
291 bPtr->image = WMRetainPixmap(image);
293 if (bPtr->dimage) {
294 bPtr->dimage->pixmap = None;
295 WMReleasePixmap(bPtr->dimage);
296 bPtr->dimage = NULL;
299 if (image) {
300 bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
301 image->pixmap, None,
302 image->width, image->height,
303 image->depth);
304 updateDisabledMask(bPtr);
307 if (bPtr->view->flags.realized) {
308 paintButton(bPtr);
313 void
314 WMSetButtonAltImage(WMButton *bPtr, WMPixmap *image)
316 if (bPtr->altImage!=NULL)
317 WMReleasePixmap(bPtr->altImage);
318 bPtr->altImage = WMRetainPixmap(image);
321 if (bPtr->view->flags.realized) {
322 paintButton(bPtr);
327 void
328 WMSetButtonImagePosition(WMButton *bPtr, WMImagePosition position)
330 bPtr->flags.imagePosition = position;
333 if (bPtr->view->flags.realized) {
334 paintButton(bPtr);
341 void
342 WMSetButtonTextAlignment(WMButton *bPtr, WMAlignment alignment)
344 bPtr->flags.alignment = alignment;
347 if (bPtr->view->flags.realized) {
348 paintButton(bPtr);
352 void
353 WMSetButtonText(WMButton *bPtr, char *text)
355 if (bPtr->caption)
356 wfree(bPtr->caption);
358 if (text!=NULL) {
359 bPtr->caption = wstrdup(text);
360 } else {
361 bPtr->caption = NULL;
365 if (bPtr->view->flags.realized) {
366 paintButton(bPtr);
371 void
372 WMSetButtonAltText(WMButton *bPtr, char *text)
374 if (bPtr->altCaption)
375 wfree(bPtr->altCaption);
377 if (text!=NULL) {
378 bPtr->altCaption = wstrdup(text);
379 } else {
380 bPtr->altCaption = NULL;
383 if (bPtr->view->flags.realized) {
384 paintButton(bPtr);
389 void
390 WMSetButtonTextColor(WMButton *bPtr, WMColor *color)
392 if (bPtr->textColor)
393 WMReleaseColor(bPtr->textColor);
395 bPtr->textColor = WMRetainColor(color);
399 void
400 WMSetButtonAltTextColor(WMButton *bPtr, WMColor *color)
402 if (bPtr->altTextColor)
403 WMReleaseColor(bPtr->altTextColor);
405 bPtr->altTextColor = WMRetainColor(color);
409 void
410 WMSetButtonDisabledTextColor(WMButton *bPtr, WMColor *color)
412 if (bPtr->disTextColor)
413 WMReleaseColor(bPtr->disTextColor);
415 bPtr->disTextColor = WMRetainColor(color);
419 void
420 WMSetButtonSelected(WMButton *bPtr, int isSelected)
422 bPtr->flags.selected = isSelected ? 1 : 0;
424 if (bPtr->view->flags.realized) {
425 paintButton(bPtr);
427 if (bPtr->groupIndex > 0)
428 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
433 WMGetButtonSelected(WMButton *bPtr)
435 CHECK_CLASS(bPtr, WC_Button);
437 return bPtr->flags.selected;
441 void
442 WMSetButtonBordered(WMButton *bPtr, int isBordered)
444 bPtr->flags.bordered = isBordered;
446 if (bPtr->view->flags.realized) {
447 paintButton(bPtr);
452 void
453 WMSetButtonFont(WMButton *bPtr, WMFont *font)
455 if (bPtr->font)
456 WMReleaseFont(bPtr->font);
458 bPtr->font = WMRetainFont(font);
462 void
463 WMSetButtonEnabled(WMButton *bPtr, Bool flag)
465 bPtr->flags.enabled = ((flag==0) ? 0 : 1);
467 if (bPtr->view->flags.mapped) {
468 paintButton(bPtr);
474 WMGetButtonEnabled(WMButton *bPtr)
476 CHECK_CLASS(bPtr, WC_Button);
478 return bPtr->flags.enabled;
482 void
483 WMSetButtonImageDimsWhenDisabled(WMButton *bPtr, Bool flag)
485 bPtr->flags.dimsWhenDisabled = ((flag==0) ? 0 : 1);
487 updateDisabledMask(bPtr);
491 void
492 WMSetButtonTag(WMButton *bPtr, int tag)
494 bPtr->tag = tag;
498 void
499 WMPerformButtonClick(WMButton *bPtr)
501 CHECK_CLASS(bPtr, WC_Button);
503 if (!bPtr->flags.enabled)
504 return;
506 bPtr->flags.pushed = 1;
507 bPtr->flags.selected = 1;
509 if (bPtr->view->flags.mapped) {
510 paintButton(bPtr);
511 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
512 wusleep(20000);
515 bPtr->flags.pushed = 0;
517 if (bPtr->groupIndex > 0) {
518 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
521 if (bPtr->action)
522 (*bPtr->action)(bPtr, bPtr->clientData);
524 if (bPtr->view->flags.mapped)
525 paintButton(bPtr);
530 void
531 WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData)
533 CHECK_CLASS(bPtr, WC_Button);
535 bPtr->action = action;
537 bPtr->clientData = clientData;
543 static void
544 radioPushObserver(void *observerData, WMNotification *notification)
546 WMButton *bPtr = (WMButton*)observerData;
547 WMButton *pushedButton = (WMButton*)WMGetNotificationObject(notification);
549 if (bPtr!=pushedButton && pushedButton->groupIndex == bPtr->groupIndex
550 && bPtr->groupIndex!=0) {
551 if (bPtr->flags.selected) {
552 bPtr->flags.selected = 0;
553 paintButton(bPtr);
560 void
561 WMGroupButtons(WMButton *bPtr, WMButton *newMember)
563 static int tagIndex = 0;
565 CHECK_CLASS(bPtr, WC_Button);
566 CHECK_CLASS(newMember, WC_Button);
568 if (!bPtr->flags.addedObserver) {
569 WMAddNotificationObserver(radioPushObserver, bPtr,
570 WMPushedRadioNotification, NULL);
571 bPtr->flags.addedObserver = 1;
573 if (!newMember->flags.addedObserver) {
574 WMAddNotificationObserver(radioPushObserver, newMember,
575 WMPushedRadioNotification, NULL);
576 newMember->flags.addedObserver = 1;
579 if (bPtr->groupIndex==0) {
580 bPtr->groupIndex = ++tagIndex;
582 newMember->groupIndex = bPtr->groupIndex;
586 void
587 WMSetButtonContinuous(WMButton *bPtr, Bool flag)
589 bPtr->flags.continuous = ((flag==0) ? 0 : 1);
590 if (bPtr->timer) {
591 WMDeleteTimerHandler(bPtr->timer);
592 bPtr->timer = NULL;
597 void
598 WMSetButtonPeriodicDelay(WMButton *bPtr, float delay, float interval)
600 bPtr->periodicInterval = interval;
601 bPtr->periodicDelay = delay;
606 static void
607 paintButton(Button *bPtr)
609 W_Screen *scrPtr = bPtr->view->screen;
610 WMReliefType relief;
611 int offset;
612 char *caption;
613 WMPixmap *image;
614 WMColor *textColor;
615 WMColor *backColor;
617 backColor = NULL;
618 caption = bPtr->caption;
620 if (bPtr->flags.enabled) {
621 textColor = (bPtr->textColor!=NULL
622 ? bPtr->textColor : scrPtr->black);
623 } else {
624 textColor = (bPtr->disTextColor!=NULL
625 ? bPtr->disTextColor : scrPtr->darkGray);
628 if (bPtr->flags.enabled || !bPtr->dimage)
629 image = bPtr->image;
630 else
631 image = bPtr->dimage;
632 offset = 0;
633 if (bPtr->flags.bordered)
634 relief = WRRaised;
635 else
636 relief = WRFlat;
638 if (bPtr->flags.selected) {
639 if (bPtr->flags.stateLight) {
640 backColor = scrPtr->white;
641 textColor = scrPtr->black;
644 if (bPtr->flags.stateChange) {
645 if (bPtr->altCaption)
646 caption = bPtr->altCaption;
647 if (bPtr->altImage)
648 image = bPtr->altImage;
649 if (bPtr->altTextColor)
650 textColor = bPtr->altTextColor;
653 if (bPtr->flags.statePush && bPtr->flags.bordered) {
654 relief = WRSunken;
655 offset = 1;
659 if (bPtr->flags.pushed) {
660 if (bPtr->flags.pushIn) {
661 relief = WRPushed;
662 offset = 1;
664 if (bPtr->flags.pushLight) {
665 backColor = scrPtr->white;
666 textColor = scrPtr->black;
669 if (bPtr->flags.pushChange) {
670 if (bPtr->altCaption)
671 caption = bPtr->altCaption;
672 if (bPtr->altImage)
673 image = bPtr->altImage;
674 if (bPtr->altTextColor)
675 textColor = bPtr->altTextColor;
679 W_PaintTextAndImage(bPtr->view, True, textColor,
680 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
681 relief, caption, bPtr->flags.alignment, image,
682 bPtr->flags.imagePosition, backColor, offset);
687 static void
688 handleEvents(XEvent *event, void *data)
690 Button *bPtr = (Button*)data;
692 CHECK_CLASS(data, WC_Button);
695 switch (event->type) {
696 case Expose:
697 if (event->xexpose.count!=0)
698 break;
699 paintButton(bPtr);
700 break;
702 case DestroyNotify:
703 destroyButton(bPtr);
704 break;
709 static void
710 autoRepeat(void *data)
712 Button *bPtr = (Button*)data;
714 if (bPtr->action && bPtr->flags.pushed)
715 (*bPtr->action)(bPtr, bPtr->clientData);
717 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval*1000),
718 autoRepeat, bPtr);
722 static void
723 handleActionEvents(XEvent *event, void *data)
725 Button *bPtr = (Button*)data;
726 int doclick = 0, dopaint=0;
728 CHECK_CLASS(data, WC_Button);
730 if (!bPtr->flags.enabled)
731 return;
733 switch (event->type) {
734 case EnterNotify:
735 if (bPtr->groupIndex == 0) {
736 bPtr->flags.pushed = bPtr->flags.wasPushed;
737 if (bPtr->flags.pushed) {
738 bPtr->flags.selected = !bPtr->flags.prevSelected;
739 dopaint = 1;
742 break;
744 case LeaveNotify:
745 if (bPtr->groupIndex == 0) {
746 bPtr->flags.wasPushed = bPtr->flags.pushed;
747 if (bPtr->flags.pushed) {
748 bPtr->flags.selected = bPtr->flags.prevSelected;
749 dopaint = 1;
751 bPtr->flags.pushed = 0;
753 break;
755 case ButtonPress:
756 if (event->xbutton.button == Button1) {
757 bPtr->flags.prevSelected = bPtr->flags.selected;
758 bPtr->flags.wasPushed = 0;
759 bPtr->flags.pushed = 1;
760 if (bPtr->groupIndex>0) {
761 bPtr->flags.selected = 1;
762 dopaint = 1;
763 break;
765 bPtr->flags.selected = !bPtr->flags.selected;
766 dopaint = 1;
768 if (bPtr->flags.continuous && !bPtr->timer) {
769 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay*1000),
770 autoRepeat, bPtr);
773 break;
775 case ButtonRelease:
776 if (event->xbutton.button == Button1) {
777 if (bPtr->flags.pushed) {
778 if (bPtr->groupIndex==0 ||
779 (bPtr->flags.selected && bPtr->groupIndex > 0))
780 doclick = 1;
781 dopaint = 1;
782 if (bPtr->flags.springLoaded) {
783 bPtr->flags.selected = bPtr->flags.prevSelected;
786 bPtr->flags.pushed = 0;
788 if (bPtr->timer) {
789 WMDeleteTimerHandler(bPtr->timer);
790 bPtr->timer = NULL;
792 break;
795 if (dopaint)
796 paintButton(bPtr);
798 if (doclick) {
799 if (bPtr->flags.selected && bPtr->groupIndex > 0) {
800 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
803 if (bPtr->action)
804 (*bPtr->action)(bPtr, bPtr->clientData);
810 static void
811 destroyButton(Button *bPtr)
813 if (bPtr->flags.addedObserver) {
814 WMRemoveNotificationObserver(bPtr);
817 if (bPtr->timer)
818 WMDeleteTimerHandler(bPtr->timer);
820 if (bPtr->font)
821 WMReleaseFont(bPtr->font);
823 if (bPtr->caption)
824 wfree(bPtr->caption);
826 if (bPtr->altCaption)
827 wfree(bPtr->altCaption);
829 if (bPtr->textColor)
830 WMReleaseColor(bPtr->textColor);
832 if (bPtr->altTextColor)
833 WMReleaseColor(bPtr->altTextColor);
835 if (bPtr->disTextColor)
836 WMReleaseColor(bPtr->disTextColor);
838 if (bPtr->image)
839 WMReleasePixmap(bPtr->image);
841 if (bPtr->dimage) {
842 /* yuck.. kluge */
843 bPtr->dimage->pixmap = None;
845 WMReleasePixmap(bPtr->dimage);
847 if (bPtr->altImage)
848 WMReleasePixmap(bPtr->altImage);
850 wfree(bPtr);