added spec for rpm..
[wmaker-crm.git] / WINGs / wbutton.c
blob1235c7aebc876728635d003dc843961399f24bf2
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 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
417 if (bPtr->view->flags.realized) {
418 paintButton(bPtr);
424 WMGetButtonSelected(WMButton *bPtr)
426 CHECK_CLASS(bPtr, WC_Button);
428 return bPtr->flags.selected;
432 void
433 WMSetButtonBordered(WMButton *bPtr, int isBordered)
435 bPtr->flags.bordered = isBordered;
437 if (bPtr->view->flags.realized) {
438 paintButton(bPtr);
443 void
444 WMSetButtonFont(WMButton *bPtr, WMFont *font)
446 if (bPtr->font)
447 WMReleaseFont(bPtr->font);
449 bPtr->font = WMRetainFont(font);
453 void
454 WMSetButtonEnabled(WMButton *bPtr, Bool flag)
456 bPtr->flags.enabled = flag;
458 if (bPtr->view->flags.mapped) {
459 paintButton(bPtr);
464 void
465 WMSetButtonTag(WMButton *bPtr, int tag)
467 bPtr->tag = tag;
472 void
473 WMPerformButtonClick(WMButton *bPtr)
475 CHECK_CLASS(bPtr, WC_Button);
477 if (!bPtr->flags.enabled)
478 return;
480 bPtr->flags.pushed = 1;
481 bPtr->flags.selected = 1;
483 if (bPtr->view->flags.mapped) {
484 paintButton(bPtr);
485 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
486 wusleep(20000);
489 if (bPtr->groupIndex>0) {
490 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
493 if (bPtr->action)
494 (*bPtr->action)(bPtr, bPtr->clientData);
496 bPtr->flags.pushed = 0;
498 if (bPtr->view->flags.mapped)
499 paintButton(bPtr);
504 void
505 WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData)
507 CHECK_CLASS(bPtr, WC_Button);
509 bPtr->action = action;
511 bPtr->clientData = clientData;
517 static void
518 radioPushObserver(void *observerData, WMNotification *notification)
520 WMButton *bPtr = (WMButton*)observerData;
521 WMButton *pushedButton = (WMButton*)WMGetNotificationObject(notification);
523 if (bPtr!=pushedButton && pushedButton->groupIndex == bPtr->groupIndex
524 && bPtr->groupIndex!=0) {
525 if (bPtr->flags.selected) {
526 bPtr->flags.selected = 0;
527 paintButton(bPtr);
534 void
535 WMGroupButtons(WMButton *bPtr, WMButton *newMember)
537 static int tagIndex = 0;
539 CHECK_CLASS(bPtr, WC_Button);
540 CHECK_CLASS(newMember, WC_Button);
542 if (!bPtr->flags.addedObserver) {
543 WMAddNotificationObserver(radioPushObserver, bPtr,
544 WMPushedRadioNotification, NULL);
545 bPtr->flags.addedObserver = 1;
547 if (!newMember->flags.addedObserver) {
548 WMAddNotificationObserver(radioPushObserver, newMember,
549 WMPushedRadioNotification, NULL);
550 newMember->flags.addedObserver = 1;
553 if (bPtr->groupIndex==0) {
554 bPtr->groupIndex = ++tagIndex;
556 newMember->groupIndex = bPtr->groupIndex;
560 void
561 WMSetButtonContinuous(WMButton *bPtr, Bool flag)
563 bPtr->flags.continuous = flag;
564 if (bPtr->timer) {
565 WMDeleteTimerHandler(bPtr->timer);
566 bPtr->timer = NULL;
571 void
572 WMSetButtonPeriodicDelay(WMButton *bPtr, float delay, float interval)
574 bPtr->periodicInterval = interval;
575 bPtr->periodicDelay = delay;
580 static void
581 paintButton(Button *bPtr)
583 W_Screen *scrPtr = bPtr->view->screen;
584 WMReliefType relief;
585 int offset;
586 char *caption;
587 WMPixmap *image;
588 WMColor *textColor;
589 GC gc;
591 gc = NULL;
592 caption = bPtr->caption;
594 if (bPtr->flags.enabled) {
595 textColor = (bPtr->textColor!=NULL
596 ? bPtr->textColor : scrPtr->black);
597 } else {
598 textColor = (bPtr->disTextColor!=NULL
599 ? bPtr->disTextColor : scrPtr->darkGray);
602 if (bPtr->flags.enabled || !bPtr->dimage)
603 image = bPtr->image;
604 else
605 image = bPtr->dimage;
606 offset = 0;
607 if (bPtr->flags.bordered)
608 relief = WRRaised;
609 else
610 relief = WRFlat;
612 if (bPtr->flags.selected) {
613 if (bPtr->flags.stateLight) {
614 gc = WMColorGC(scrPtr->white);
615 textColor = scrPtr->black;
618 if (bPtr->flags.stateChange) {
619 if (bPtr->altCaption)
620 caption = bPtr->altCaption;
621 if (bPtr->altImage)
622 image = bPtr->altImage;
623 if (bPtr->altTextColor)
624 textColor = bPtr->altTextColor;
627 if (bPtr->flags.statePush && bPtr->flags.bordered) {
628 relief = WRSunken;
629 offset = 1;
633 if (bPtr->flags.pushed) {
634 if (bPtr->flags.pushIn) {
635 relief = WRPushed;
636 offset = 1;
638 if (bPtr->flags.pushLight) {
639 gc = WMColorGC(scrPtr->white);
640 textColor = scrPtr->black;
643 if (bPtr->flags.pushChange) {
644 if (bPtr->altCaption)
645 caption = bPtr->altCaption;
646 if (bPtr->altImage)
647 image = bPtr->altImage;
648 if (bPtr->altTextColor)
649 textColor = bPtr->altTextColor;
653 W_PaintTextAndImage(bPtr->view, True, WMColorGC(textColor),
654 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
655 relief, caption, bPtr->flags.alignment, image,
656 bPtr->flags.imagePosition, gc, offset);
661 static void
662 handleEvents(XEvent *event, void *data)
664 Button *bPtr = (Button*)data;
666 CHECK_CLASS(data, WC_Button);
669 switch (event->type) {
670 case Expose:
671 if (event->xexpose.count!=0)
672 break;
673 paintButton(bPtr);
674 break;
676 case DestroyNotify:
677 destroyButton(bPtr);
678 break;
683 static void
684 autoRepeat(void *data)
686 Button *bPtr = (Button*)data;
688 if (bPtr->action && bPtr->flags.pushed)
689 (*bPtr->action)(bPtr, bPtr->clientData);
691 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval*1000),
692 autoRepeat, bPtr);
696 static void
697 handleActionEvents(XEvent *event, void *data)
699 Button *bPtr = (Button*)data;
700 int doclick = 0, dopaint=0;
702 CHECK_CLASS(data, WC_Button);
704 if (!bPtr->flags.enabled)
705 return;
707 switch (event->type) {
708 case EnterNotify:
709 if (bPtr->groupIndex == 0) {
710 bPtr->flags.pushed = bPtr->flags.wasPushed;
711 if (bPtr->flags.pushed) {
712 bPtr->flags.selected = !bPtr->flags.prevSelected;
713 dopaint = 1;
716 break;
718 case LeaveNotify:
719 if (bPtr->groupIndex == 0) {
720 bPtr->flags.wasPushed = bPtr->flags.pushed;
721 if (bPtr->flags.pushed) {
722 bPtr->flags.selected = bPtr->flags.prevSelected;
723 dopaint = 1;
725 bPtr->flags.pushed = 0;
727 break;
729 case ButtonPress:
730 if (event->xbutton.button == Button1) {
731 if (bPtr->groupIndex>0) {
732 if (!bPtr->flags.selected)
733 doclick = 1;
734 bPtr->flags.pushed = 1;
735 bPtr->flags.selected = 1;
736 dopaint = 1;
737 break;
739 bPtr->flags.wasPushed = 0;
740 bPtr->flags.pushed = 1;
741 bPtr->flags.prevSelected = bPtr->flags.selected;
742 bPtr->flags.selected = !bPtr->flags.selected;
743 dopaint = 1;
745 if (bPtr->flags.continuous && !bPtr->timer) {
746 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay*1000),
747 autoRepeat, bPtr);
750 break;
752 case ButtonRelease:
753 if (event->xbutton.button == Button1) {
754 if (bPtr->flags.pushed) {
755 if (bPtr->groupIndex==0)
756 doclick = 1;
757 dopaint = 1;
758 if (bPtr->flags.springLoaded) {
759 bPtr->flags.selected = bPtr->flags.prevSelected;
762 bPtr->flags.pushed = 0;
764 if (bPtr->timer) {
765 WMDeleteTimerHandler(bPtr->timer);
766 bPtr->timer = NULL;
768 break;
771 if (dopaint)
772 paintButton(bPtr);
774 if (doclick) {
775 if (bPtr->flags.selected && bPtr->groupIndex>0) {
776 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
779 if (bPtr->action)
780 (*bPtr->action)(bPtr, bPtr->clientData);
786 static void
787 destroyButton(Button *bPtr)
789 if (bPtr->flags.addedObserver) {
790 WMRemoveNotificationObserver(bPtr);
793 if (bPtr->timer)
794 WMDeleteTimerHandler(bPtr->timer);
796 if (bPtr->font)
797 WMReleaseFont(bPtr->font);
799 if (bPtr->caption)
800 wfree(bPtr->caption);
802 if (bPtr->altCaption)
803 wfree(bPtr->altCaption);
805 if (bPtr->textColor)
806 WMReleaseColor(bPtr->textColor);
808 if (bPtr->altTextColor)
809 WMReleaseColor(bPtr->altTextColor);
811 if (bPtr->disTextColor)
812 WMReleaseColor(bPtr->disTextColor);
814 if (bPtr->image)
815 WMReleasePixmap(bPtr->image);
817 if (bPtr->dimage) {
818 /* yuck.. kluge */
819 bPtr->dimage->pixmap = None;
821 WMReleasePixmap(bPtr->dimage);
823 if (bPtr->altImage)
824 WMReleasePixmap(bPtr->altImage);
826 wfree(bPtr);