- Fixed a bug in popup button code.
[wmaker-crm.git] / WINGs / wbutton.c
blobf5e21bd16d348d557d7fe2fc788dbd29d8283710
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 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 */
64 /* */
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;
74 } flags;
75 } Button;
79 #define DEFAULT_BUTTON_WIDTH 60
80 #define DEFAULT_BUTTON_HEIGHT 24
81 #define DEFAULT_BUTTON_ALIGNMENT WACenter
82 #define DEFAULT_BUTTON_IS_BORDERED True
85 #define DEFAULT_RADIO_WIDTH 100
86 #define DEFAULT_RADIO_HEIGHT 20
87 #define DEFAULT_RADIO_ALIGNMENT WALeft
88 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
89 #define DEFAULT_RADIO_TEXT "Radio"
92 #define DEFAULT_SWITCH_WIDTH 100
93 #define DEFAULT_SWITCH_HEIGHT 20
94 #define DEFAULT_SWITCH_ALIGNMENT WALeft
95 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
96 #define DEFAULT_SWITCH_TEXT "Switch"
99 static void destroyButton(Button *bPtr);
100 static void paintButton(Button *bPtr);
102 static void handleEvents(XEvent *event, void *data);
103 static void handleActionEvents(XEvent *event, void *data);
106 static char *WMPushedRadioNotification="WMPushedRadioNotification";
109 #define NFONT(b) (b)->view->screen->normalFont
112 WMButton*
113 WMCreateCustomButton(WMWidget *parent, int behaviourMask)
115 Button *bPtr;
117 bPtr = wmalloc(sizeof(Button));
118 memset(bPtr, 0, sizeof(Button));
120 bPtr->widgetClass = WC_Button;
122 bPtr->view = W_CreateView(W_VIEW(parent));
123 if (!bPtr->view) {
124 wfree(bPtr);
125 return NULL;
127 bPtr->view->self = bPtr;
129 bPtr->flags.type = 0;
131 bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask)!=0;
132 bPtr->flags.pushIn = (behaviourMask & WBBPushInMask)!=0;
133 bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask)!=0;
134 bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask)!=0;
135 bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask)!=0;
136 bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask)!=0;
137 bPtr->flags.statePush = (behaviourMask & WBBStatePushMask)!=0;
139 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
140 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
141 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
143 bPtr->flags.enabled = 1;
146 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask,
147 handleEvents, bPtr);
149 WMCreateEventHandler(bPtr->view, ButtonPressMask|ButtonReleaseMask
150 |EnterWindowMask|LeaveWindowMask,
151 handleActionEvents, bPtr);
153 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
154 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
155 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
157 return bPtr;
162 WMButton*
163 WMCreateButton(WMWidget *parent, WMButtonType type)
165 W_Screen *scrPtr = W_VIEW(parent)->screen;
166 Button *bPtr;
168 switch (type) {
169 case WBTMomentaryPush:
170 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
171 |WBBPushInMask|WBBPushLightMask);
172 break;
174 case WBTMomentaryChange:
175 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
176 |WBBPushChangeMask);
177 break;
179 case WBTPushOnPushOff:
180 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStatePushMask
181 |WBBStateLightMask);
182 break;
184 case WBTToggle:
185 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStateChangeMask
186 |WBBStatePushMask);
187 break;
189 case WBTOnOff:
190 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
191 break;
193 case WBTSwitch:
194 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
195 bPtr->flags.bordered = 0;
196 bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
197 bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
198 break;
200 case WBTRadio:
201 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
202 bPtr->flags.bordered = 0;
203 bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
204 bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
205 break;
207 default:
208 case WBTMomentaryLight:
209 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
210 |WBBPushLightMask);
211 bPtr->flags.bordered = 1;
212 break;
215 bPtr->flags.type = type;
217 if (type==WBTRadio) {
218 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
219 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
220 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
221 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
222 } else if (type==WBTSwitch) {
223 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
224 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
225 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
226 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
229 return bPtr;
233 static void
234 updateDisabledMask(WMButton *bPtr)
236 WMScreen *scr = WMWidgetScreen(bPtr);
237 Display *dpy = scr->display;
239 if (bPtr->image) {
240 XGCValues gcv;
242 bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
243 bPtr->dimage->width,
244 bPtr->dimage->height, 1);
246 XSetForeground(dpy, scr->monoGC, 0);
247 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
248 bPtr->dimage->width, bPtr->dimage->height);
250 gcv.foreground = 1;
251 gcv.background = 0;
252 gcv.stipple = scr->stipple;
253 gcv.fill_style = FillStippled;
254 gcv.clip_mask = bPtr->image->mask;
255 gcv.clip_x_origin = 0;
256 gcv.clip_y_origin = 0;
258 XChangeGC(dpy, scr->monoGC, GCForeground|GCBackground|GCStipple
259 |GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin, &gcv);
261 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
262 bPtr->dimage->width, bPtr->dimage->height);
264 gcv.fill_style = FillSolid;
265 gcv.clip_mask = None;
266 XChangeGC(dpy, scr->monoGC, GCFillStyle|GCClipMask, &gcv);
270 void
271 WMSetButtonImageDefault(WMButton *bPtr)
273 WMSetButtonImage (bPtr, WMWidgetScreen(bPtr)->buttonArrow);
274 WMSetButtonAltImage (bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
277 void
278 WMSetButtonImage(WMButton *bPtr, WMPixmap *image)
280 if (bPtr->image!=NULL)
281 WMReleasePixmap(bPtr->image);
282 bPtr->image = WMRetainPixmap(image);
284 if (bPtr->dimage) {
285 bPtr->dimage->pixmap = None;
286 WMReleasePixmap(bPtr->dimage);
287 bPtr->dimage = NULL;
290 if (image) {
291 bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
292 image->pixmap, None,
293 image->width, image->height,
294 image->depth);
295 updateDisabledMask(bPtr);
298 if (bPtr->view->flags.realized) {
299 paintButton(bPtr);
304 void
305 WMSetButtonAltImage(WMButton *bPtr, WMPixmap *image)
307 if (bPtr->altImage!=NULL)
308 WMReleasePixmap(bPtr->altImage);
309 bPtr->altImage = WMRetainPixmap(image);
312 if (bPtr->view->flags.realized) {
313 paintButton(bPtr);
318 void
319 WMSetButtonImagePosition(WMButton *bPtr, WMImagePosition position)
321 bPtr->flags.imagePosition = position;
324 if (bPtr->view->flags.realized) {
325 paintButton(bPtr);
332 void
333 WMSetButtonTextAlignment(WMButton *bPtr, WMAlignment alignment)
335 bPtr->flags.alignment = alignment;
338 if (bPtr->view->flags.realized) {
339 paintButton(bPtr);
343 void
344 WMSetButtonText(WMButton *bPtr, char *text)
346 if (bPtr->caption)
347 wfree(bPtr->caption);
349 if (text!=NULL) {
350 bPtr->caption = wstrdup(text);
351 } else {
352 bPtr->caption = NULL;
356 if (bPtr->view->flags.realized) {
357 paintButton(bPtr);
362 void
363 WMSetButtonAltText(WMButton *bPtr, char *text)
365 if (bPtr->altCaption)
366 wfree(bPtr->altCaption);
368 if (text!=NULL) {
369 bPtr->altCaption = wstrdup(text);
370 } else {
371 bPtr->altCaption = NULL;
374 if (bPtr->view->flags.realized) {
375 paintButton(bPtr);
380 void
381 WMSetButtonTextColor(WMButton *bPtr, WMColor *color)
383 if (bPtr->textColor)
384 WMReleaseColor(bPtr->textColor);
386 bPtr->textColor = WMRetainColor(color);
390 void
391 WMSetButtonAltTextColor(WMButton *bPtr, WMColor *color)
393 if (bPtr->altTextColor)
394 WMReleaseColor(bPtr->altTextColor);
396 bPtr->altTextColor = WMRetainColor(color);
400 void
401 WMSetButtonDisabledTextColor(WMButton *bPtr, WMColor *color)
403 if (bPtr->disTextColor)
404 WMReleaseColor(bPtr->disTextColor);
406 bPtr->disTextColor = WMRetainColor(color);
410 void
411 WMSetButtonSelected(WMButton *bPtr, int isSelected)
413 bPtr->flags.selected = isSelected;
415 if (bPtr->view->flags.realized) {
416 paintButton(bPtr);
422 WMGetButtonSelected(WMButton *bPtr)
424 CHECK_CLASS(bPtr, WC_Button);
426 return bPtr->flags.selected;
430 void
431 WMSetButtonBordered(WMButton *bPtr, int isBordered)
433 bPtr->flags.bordered = isBordered;
435 if (bPtr->view->flags.realized) {
436 paintButton(bPtr);
441 void
442 WMSetButtonFont(WMButton *bPtr, WMFont *font)
444 if (bPtr->font)
445 WMReleaseFont(bPtr->font);
447 bPtr->font = WMRetainFont(font);
451 void
452 WMSetButtonEnabled(WMButton *bPtr, Bool flag)
454 bPtr->flags.enabled = flag;
456 if (bPtr->view->flags.mapped) {
457 paintButton(bPtr);
462 void
463 WMSetButtonTag(WMButton *bPtr, int tag)
465 bPtr->tag = tag;
470 void
471 WMPerformButtonClick(WMButton *bPtr)
473 CHECK_CLASS(bPtr, WC_Button);
475 if (!bPtr->flags.enabled)
476 return;
478 bPtr->flags.pushed = 1;
479 bPtr->flags.selected = 1;
481 if (bPtr->view->flags.mapped) {
482 paintButton(bPtr);
483 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
484 wusleep(20000);
487 if (bPtr->groupIndex>0) {
488 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
491 if (bPtr->action)
492 (*bPtr->action)(bPtr, bPtr->clientData);
494 bPtr->flags.pushed = 0;
496 if (bPtr->view->flags.mapped)
497 paintButton(bPtr);
502 void
503 WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData)
505 CHECK_CLASS(bPtr, WC_Button);
507 bPtr->action = action;
509 bPtr->clientData = clientData;
515 static void
516 radioPushObserver(void *observerData, WMNotification *notification)
518 WMButton *bPtr = (WMButton*)observerData;
519 WMButton *pushedButton = (WMButton*)WMGetNotificationObject(notification);
521 if (bPtr!=pushedButton && pushedButton->groupIndex == bPtr->groupIndex
522 && bPtr->groupIndex!=0) {
523 if (bPtr->flags.selected) {
524 bPtr->flags.selected = 0;
525 paintButton(bPtr);
532 void
533 WMGroupButtons(WMButton *bPtr, WMButton *newMember)
535 static int tagIndex = 0;
537 CHECK_CLASS(bPtr, WC_Button);
538 CHECK_CLASS(newMember, WC_Button);
540 if (!bPtr->flags.addedObserver) {
541 WMAddNotificationObserver(radioPushObserver, bPtr,
542 WMPushedRadioNotification, NULL);
543 bPtr->flags.addedObserver = 1;
545 if (!newMember->flags.addedObserver) {
546 WMAddNotificationObserver(radioPushObserver, newMember,
547 WMPushedRadioNotification, NULL);
548 newMember->flags.addedObserver = 1;
551 if (bPtr->groupIndex==0) {
552 bPtr->groupIndex = ++tagIndex;
554 newMember->groupIndex = bPtr->groupIndex;
558 void
559 WMSetButtonContinuous(WMButton *bPtr, Bool flag)
561 bPtr->flags.continuous = flag;
562 if (bPtr->timer) {
563 WMDeleteTimerHandler(bPtr->timer);
564 bPtr->timer = NULL;
569 void
570 WMSetButtonPeriodicDelay(WMButton *bPtr, float delay, float interval)
572 bPtr->periodicInterval = interval;
573 bPtr->periodicDelay = delay;
578 static void
579 paintButton(Button *bPtr)
581 W_Screen *scrPtr = bPtr->view->screen;
582 WMReliefType relief;
583 int offset;
584 char *caption;
585 WMPixmap *image;
586 WMColor *textColor;
587 GC gc;
589 gc = NULL;
590 caption = bPtr->caption;
592 if (bPtr->flags.enabled) {
593 textColor = (bPtr->textColor!=NULL
594 ? bPtr->textColor : scrPtr->black);
595 } else {
596 textColor = (bPtr->disTextColor!=NULL
597 ? bPtr->disTextColor : scrPtr->darkGray);
600 if (bPtr->flags.enabled || !bPtr->dimage)
601 image = bPtr->image;
602 else
603 image = bPtr->dimage;
604 offset = 0;
605 if (bPtr->flags.bordered)
606 relief = WRRaised;
607 else
608 relief = WRFlat;
610 if (bPtr->flags.selected) {
611 if (bPtr->flags.stateLight) {
612 gc = WMColorGC(scrPtr->white);
613 textColor = scrPtr->black;
616 if (bPtr->flags.stateChange) {
617 if (bPtr->altCaption)
618 caption = bPtr->altCaption;
619 if (bPtr->altImage)
620 image = bPtr->altImage;
621 if (bPtr->altTextColor)
622 textColor = bPtr->altTextColor;
625 if (bPtr->flags.statePush && bPtr->flags.bordered) {
626 relief = WRSunken;
627 offset = 1;
631 if (bPtr->flags.pushed) {
632 if (bPtr->flags.pushIn) {
633 relief = WRPushed;
634 offset = 1;
636 if (bPtr->flags.pushLight) {
637 gc = WMColorGC(scrPtr->white);
638 textColor = scrPtr->black;
641 if (bPtr->flags.pushChange) {
642 if (bPtr->altCaption)
643 caption = bPtr->altCaption;
644 if (bPtr->altImage)
645 image = bPtr->altImage;
646 if (bPtr->altTextColor)
647 textColor = bPtr->altTextColor;
651 W_PaintTextAndImage(bPtr->view, True, WMColorGC(textColor),
652 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
653 relief, caption, bPtr->flags.alignment, image,
654 bPtr->flags.imagePosition, gc, offset);
659 static void
660 handleEvents(XEvent *event, void *data)
662 Button *bPtr = (Button*)data;
664 CHECK_CLASS(data, WC_Button);
667 switch (event->type) {
668 case Expose:
669 if (event->xexpose.count!=0)
670 break;
671 paintButton(bPtr);
672 break;
674 case DestroyNotify:
675 destroyButton(bPtr);
676 break;
681 static void
682 autoRepeat(void *data)
684 Button *bPtr = (Button*)data;
686 if (bPtr->action && bPtr->flags.pushed)
687 (*bPtr->action)(bPtr, bPtr->clientData);
689 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval*1000),
690 autoRepeat, bPtr);
694 static void
695 handleActionEvents(XEvent *event, void *data)
697 Button *bPtr = (Button*)data;
698 int doclick = 0, dopaint=0;
700 CHECK_CLASS(data, WC_Button);
702 if (!bPtr->flags.enabled)
703 return;
705 switch (event->type) {
706 case EnterNotify:
707 if (bPtr->groupIndex == 0) {
708 bPtr->flags.pushed = bPtr->flags.wasPushed;
709 if (bPtr->flags.pushed) {
710 bPtr->flags.selected = !bPtr->flags.prevSelected;
711 dopaint = 1;
714 break;
716 case LeaveNotify:
717 if (bPtr->groupIndex == 0) {
718 bPtr->flags.wasPushed = bPtr->flags.pushed;
719 if (bPtr->flags.pushed) {
720 bPtr->flags.selected = bPtr->flags.prevSelected;
721 dopaint = 1;
723 bPtr->flags.pushed = 0;
725 break;
727 case ButtonPress:
728 if (event->xbutton.button == Button1) {
729 if (bPtr->groupIndex>0) {
730 if (!bPtr->flags.selected)
731 doclick = 1;
732 bPtr->flags.pushed = 1;
733 bPtr->flags.selected = 1;
734 dopaint = 1;
735 break;
737 bPtr->flags.wasPushed = 0;
738 bPtr->flags.pushed = 1;
739 bPtr->flags.prevSelected = bPtr->flags.selected;
740 bPtr->flags.selected = !bPtr->flags.selected;
741 dopaint = 1;
743 if (bPtr->flags.continuous && !bPtr->timer) {
744 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay*1000),
745 autoRepeat, bPtr);
748 break;
750 case ButtonRelease:
751 if (event->xbutton.button == Button1) {
752 if (bPtr->flags.pushed) {
753 if (bPtr->groupIndex==0)
754 doclick = 1;
755 dopaint = 1;
756 if (bPtr->flags.springLoaded) {
757 bPtr->flags.selected = bPtr->flags.prevSelected;
760 bPtr->flags.pushed = 0;
762 if (bPtr->timer) {
763 WMDeleteTimerHandler(bPtr->timer);
764 bPtr->timer = NULL;
766 break;
769 if (dopaint)
770 paintButton(bPtr);
772 if (doclick) {
773 if (bPtr->flags.selected && bPtr->groupIndex>0) {
774 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
777 if (bPtr->action)
778 (*bPtr->action)(bPtr, bPtr->clientData);
784 static void
785 destroyButton(Button *bPtr)
787 if (bPtr->flags.addedObserver) {
788 WMRemoveNotificationObserver(bPtr);
791 if (bPtr->timer)
792 WMDeleteTimerHandler(bPtr->timer);
794 if (bPtr->font)
795 WMReleaseFont(bPtr->font);
797 if (bPtr->caption)
798 wfree(bPtr->caption);
800 if (bPtr->altCaption)
801 wfree(bPtr->altCaption);
803 if (bPtr->textColor)
804 WMReleaseColor(bPtr->textColor);
806 if (bPtr->altTextColor)
807 WMReleaseColor(bPtr->altTextColor);
809 if (bPtr->disTextColor)
810 WMReleaseColor(bPtr->disTextColor);
812 if (bPtr->image)
813 WMReleasePixmap(bPtr->image);
815 if (bPtr->dimage) {
816 /* yuck.. kluge */
817 bPtr->dimage->pixmap = None;
819 WMReleasePixmap(bPtr->dimage);
821 if (bPtr->altImage)
822 WMReleasePixmap(bPtr->altImage);
824 wfree(bPtr);