many bug fixes, finished some delegate code, updated menu file bug from EXEC
[wmaker-crm.git] / WINGs / wbutton.c
blob33ffa11e6be6a6039bb809bfb2c991c52cdc2d74
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 W_Pixmap *image;
18 W_Pixmap *altImage;
20 W_Pixmap *dimage;
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 bordered:1;
45 unsigned int springLoaded:1;
47 unsigned int pushIn:1; /* change relief while pushed */
49 unsigned int pushLight:1; /* highlight while pushed */
51 unsigned int pushChange:1; /* change caption while pushed */
53 unsigned int stateLight:1; /* state indicated by highlight */
55 unsigned int stateChange:1; /* state indicated by caption change */
57 unsigned int statePush:1; /* state indicated by relief */
59 unsigned int continuous:1; /* continually perform action */
60 /* */
61 unsigned int prevSelected:1;
63 unsigned int pushed:1;
65 unsigned int wasPushed:1;
67 unsigned int redrawPending:1;
69 unsigned int addedObserver:1;
70 } flags;
71 } 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
81 #define DEFAULT_RADIO_WIDTH 100
82 #define DEFAULT_RADIO_HEIGHT 20
83 #define DEFAULT_RADIO_ALIGNMENT WALeft
84 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
85 #define DEFAULT_RADIO_TEXT "Radio"
88 #define DEFAULT_SWITCH_WIDTH 100
89 #define DEFAULT_SWITCH_HEIGHT 20
90 #define DEFAULT_SWITCH_ALIGNMENT WALeft
91 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
92 #define DEFAULT_SWITCH_TEXT "Switch"
95 static void destroyButton(Button *bPtr);
96 static void paintButton(Button *bPtr);
98 static void handleEvents(XEvent *event, void *data);
99 static void handleActionEvents(XEvent *event, void *data);
102 static char *WMPushedRadioNotification="WMPushedRadioNotification";
105 #define NFONT(b) (b)->view->screen->normalFont
108 WMButton*
109 WMCreateCustomButton(WMWidget *parent, int behaviourMask)
111 Button *bPtr;
113 bPtr = wmalloc(sizeof(Button));
114 memset(bPtr, 0, sizeof(Button));
116 bPtr->widgetClass = WC_Button;
118 bPtr->view = W_CreateView(W_VIEW(parent));
119 if (!bPtr->view) {
120 free(bPtr);
121 return NULL;
123 bPtr->view->self = bPtr;
125 bPtr->flags.type = 0;
127 bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask)!=0;
128 bPtr->flags.pushIn = (behaviourMask & WBBPushInMask)!=0;
129 bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask)!=0;
130 bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask)!=0;
131 bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask)!=0;
132 bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask)!=0;
133 bPtr->flags.statePush = (behaviourMask & WBBStatePushMask)!=0;
135 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
136 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
137 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
139 bPtr->flags.enabled = 1;
142 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask,
143 handleEvents, bPtr);
145 WMCreateEventHandler(bPtr->view, ButtonPressMask|ButtonReleaseMask
146 |EnterWindowMask|LeaveWindowMask,
147 handleActionEvents, bPtr);
149 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
150 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
151 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
153 return bPtr;
158 WMButton*
159 WMCreateButton(WMWidget *parent, WMButtonType type)
161 W_Screen *scrPtr = W_VIEW(parent)->screen;
162 Button *bPtr;
164 switch (type) {
165 case WBTMomentaryPush:
166 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
167 |WBBPushInMask|WBBPushLightMask);
168 break;
170 case WBTMomentaryChange:
171 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
172 |WBBPushChangeMask);
173 break;
175 case WBTPushOnPushOff:
176 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStatePushMask
177 |WBBStateLightMask);
178 break;
180 case WBTToggle:
181 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStateChangeMask
182 |WBBStatePushMask);
183 break;
185 case WBTOnOff:
186 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
187 break;
189 case WBTSwitch:
190 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
191 bPtr->flags.bordered = 0;
192 bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
193 bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
194 break;
196 case WBTRadio:
197 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
198 bPtr->flags.bordered = 0;
199 bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
200 bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
201 break;
203 default:
204 case WBTMomentaryLight:
205 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
206 |WBBPushLightMask);
207 bPtr->flags.bordered = 1;
208 break;
211 bPtr->flags.type = type;
213 if (type==WBTRadio) {
214 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
215 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
216 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
217 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
218 } else if (type==WBTSwitch) {
219 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
220 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
221 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
222 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
225 return bPtr;
229 static void
230 updateDisabledMask(WMButton *bPtr)
232 WMScreen *scr = WMWidgetScreen(bPtr);
233 Display *dpy = scr->display;
235 if (bPtr->image) {
236 XGCValues gcv;
238 bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
239 bPtr->dimage->width,
240 bPtr->dimage->height, 1);
242 XSetForeground(dpy, scr->monoGC, 0);
243 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
244 bPtr->dimage->width, bPtr->dimage->height);
246 gcv.foreground = 1;
247 gcv.background = 0;
248 gcv.stipple = scr->stipple;
249 gcv.fill_style = FillStippled;
250 gcv.clip_mask = bPtr->image->mask;
251 gcv.clip_x_origin = 0;
252 gcv.clip_y_origin = 0;
254 XChangeGC(dpy, scr->monoGC, GCForeground|GCBackground|GCStipple
255 |GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin, &gcv);
257 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
258 bPtr->dimage->width, bPtr->dimage->height);
260 gcv.fill_style = FillSolid;
261 gcv.clip_mask = None;
262 XChangeGC(dpy, scr->monoGC, GCFillStyle|GCClipMask, &gcv);
267 void
268 WMSetButtonImage(WMButton *bPtr, WMPixmap *image)
270 if (bPtr->image!=NULL)
271 WMReleasePixmap(bPtr->image);
272 bPtr->image = WMRetainPixmap(image);
274 if (bPtr->dimage) {
275 bPtr->dimage->pixmap = None;
276 WMReleasePixmap(bPtr->dimage);
277 bPtr->dimage = NULL;
280 if (image) {
281 bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
282 image->pixmap, None,
283 image->width, image->height,
284 image->depth);
285 updateDisabledMask(bPtr);
288 if (bPtr->view->flags.realized) {
289 paintButton(bPtr);
294 void
295 WMSetButtonAltImage(WMButton *bPtr, WMPixmap *image)
297 if (bPtr->altImage!=NULL)
298 WMReleasePixmap(bPtr->altImage);
299 bPtr->altImage = WMRetainPixmap(image);
302 if (bPtr->view->flags.realized) {
303 paintButton(bPtr);
308 void
309 WMSetButtonImagePosition(WMButton *bPtr, WMImagePosition position)
311 bPtr->flags.imagePosition = position;
314 if (bPtr->view->flags.realized) {
315 paintButton(bPtr);
322 void
323 WMSetButtonTextAlignment(WMButton *bPtr, WMAlignment alignment)
325 bPtr->flags.alignment = alignment;
328 if (bPtr->view->flags.realized) {
329 paintButton(bPtr);
333 void
334 WMSetButtonText(WMButton *bPtr, char *text)
336 if (bPtr->caption)
337 free(bPtr->caption);
339 if (text!=NULL) {
340 bPtr->caption = wstrdup(text);
341 } else {
342 bPtr->caption = NULL;
346 if (bPtr->view->flags.realized) {
347 paintButton(bPtr);
352 void
353 WMSetButtonAltText(WMButton *bPtr, char *text)
355 if (bPtr->altCaption)
356 free(bPtr->altCaption);
358 if (text!=NULL) {
359 bPtr->altCaption = wstrdup(text);
360 } else {
361 bPtr->altCaption = NULL;
364 if (bPtr->view->flags.realized) {
365 paintButton(bPtr);
370 void
371 WMSetButtonSelected(WMButton *bPtr, int isSelected)
373 bPtr->flags.selected = isSelected;
375 if (bPtr->view->flags.realized) {
376 paintButton(bPtr);
382 WMGetButtonSelected(WMButton *bPtr)
384 CHECK_CLASS(bPtr, WC_Button);
386 return bPtr->flags.selected;
390 void
391 WMSetButtonBordered(WMButton *bPtr, int isBordered)
393 bPtr->flags.bordered = isBordered;
395 if (bPtr->view->flags.realized) {
396 paintButton(bPtr);
401 void
402 WMSetButtonFont(WMButton *bPtr, WMFont *font)
404 if (bPtr->font)
405 WMReleaseFont(bPtr->font);
407 bPtr->font = WMRetainFont(font);
411 void
412 WMSetButtonEnabled(WMButton *bPtr, Bool flag)
414 bPtr->flags.enabled = flag;
416 if (bPtr->view->flags.mapped) {
417 paintButton(bPtr);
422 void
423 WMSetButtonTag(WMButton *bPtr, int tag)
425 bPtr->tag = tag;
430 void
431 WMPerformButtonClick(WMButton *bPtr)
433 CHECK_CLASS(bPtr, WC_Button);
435 if (!bPtr->flags.enabled)
436 return;
438 bPtr->flags.pushed = 1;
439 bPtr->flags.selected = 1;
441 if (bPtr->view->flags.mapped) {
442 paintButton(bPtr);
443 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
444 wusleep(20000);
447 if (bPtr->groupIndex>0) {
448 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
451 if (bPtr->action)
452 (*bPtr->action)(bPtr, bPtr->clientData);
454 bPtr->flags.pushed = 0;
456 if (bPtr->view->flags.mapped)
457 paintButton(bPtr);
462 void
463 WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData)
465 CHECK_CLASS(bPtr, WC_Button);
467 bPtr->action = action;
469 bPtr->clientData = clientData;
475 static void
476 radioPushObserver(void *observerData, WMNotification *notification)
478 WMButton *bPtr = (WMButton*)observerData;
479 WMButton *pushedButton = (WMButton*)WMGetNotificationObject(notification);
481 if (bPtr!=pushedButton && pushedButton->groupIndex == bPtr->groupIndex
482 && bPtr->groupIndex!=0) {
483 if (bPtr->flags.selected) {
484 bPtr->flags.selected = 0;
485 paintButton(bPtr);
492 void
493 WMGroupButtons(WMButton *bPtr, WMButton *newMember)
495 static int tagIndex = 0;
497 CHECK_CLASS(bPtr, WC_Button);
498 CHECK_CLASS(newMember, WC_Button);
500 if (!bPtr->flags.addedObserver) {
501 WMAddNotificationObserver(radioPushObserver, bPtr,
502 WMPushedRadioNotification, NULL);
503 bPtr->flags.addedObserver = 1;
505 if (!newMember->flags.addedObserver) {
506 WMAddNotificationObserver(radioPushObserver, newMember,
507 WMPushedRadioNotification, NULL);
508 newMember->flags.addedObserver = 1;
511 if (bPtr->groupIndex==0) {
512 bPtr->groupIndex = ++tagIndex;
514 newMember->groupIndex = bPtr->groupIndex;
518 void
519 WMSetButtonContinuous(WMButton *bPtr, Bool flag)
521 bPtr->flags.continuous = flag;
522 if (bPtr->timer) {
523 WMDeleteTimerHandler(bPtr->timer);
524 bPtr->timer = NULL;
529 void
530 WMSetButtonPeriodicDelay(WMButton *bPtr, float delay, float interval)
532 bPtr->periodicInterval = interval;
533 bPtr->periodicDelay = delay;
538 static void
539 paintButton(Button *bPtr)
541 W_Screen *scrPtr = bPtr->view->screen;
542 GC gc;
543 WMReliefType relief;
544 int offset;
545 char *caption;
546 WMPixmap *image;
547 GC textGC;
549 gc = NULL;
550 caption = bPtr->caption;
551 if (bPtr->flags.enabled || !bPtr->dimage)
552 image = bPtr->image;
553 else
554 image = bPtr->dimage;
555 offset = 0;
556 if (bPtr->flags.bordered)
557 relief = WRRaised;
558 else
559 relief = WRFlat;
561 if (bPtr->flags.selected) {
562 if (bPtr->flags.stateLight)
563 gc = WMColorGC(scrPtr->white);
565 if (bPtr->flags.stateChange) {
566 if (bPtr->altCaption) {
567 caption = bPtr->altCaption;
569 if (bPtr->altImage)
570 image = bPtr->altImage;
573 if (bPtr->flags.statePush && bPtr->flags.bordered) {
574 relief = WRSunken;
575 offset = 1;
579 if (bPtr->flags.pushed) {
580 if (bPtr->flags.pushIn) {
581 relief = WRPushed;
582 offset = 1;
584 if (bPtr->flags.pushLight)
585 gc = WMColorGC(scrPtr->white);
587 if (bPtr->flags.pushChange) {
588 if (bPtr->altCaption) {
589 caption = bPtr->altCaption;
591 if (bPtr->altImage)
592 image = bPtr->altImage;
597 if (bPtr->flags.enabled)
598 textGC = WMColorGC(scrPtr->black);
599 else
600 textGC = WMColorGC(scrPtr->darkGray);
602 W_PaintTextAndImage(bPtr->view, True, textGC,
603 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
604 relief, caption, bPtr->flags.alignment, image,
605 bPtr->flags.imagePosition, gc, offset);
610 static void
611 handleEvents(XEvent *event, void *data)
613 Button *bPtr = (Button*)data;
615 CHECK_CLASS(data, WC_Button);
618 switch (event->type) {
619 case Expose:
620 if (event->xexpose.count!=0)
621 break;
622 paintButton(bPtr);
623 break;
625 case DestroyNotify:
626 destroyButton(bPtr);
627 break;
632 static void
633 autoRepeat(void *data)
635 Button *bPtr = (Button*)data;
637 if (bPtr->action && bPtr->flags.pushed)
638 (*bPtr->action)(bPtr, bPtr->clientData);
640 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval*1000),
641 autoRepeat, bPtr);
645 static void
646 handleActionEvents(XEvent *event, void *data)
648 Button *bPtr = (Button*)data;
649 int doclick = 0, dopaint=0;
651 CHECK_CLASS(data, WC_Button);
653 if (!bPtr->flags.enabled)
654 return;
656 switch (event->type) {
657 case EnterNotify:
658 if (bPtr->groupIndex == 0) {
659 bPtr->flags.pushed = bPtr->flags.wasPushed;
660 if (bPtr->flags.pushed) {
661 bPtr->flags.selected = !bPtr->flags.prevSelected;
662 dopaint = 1;
665 break;
667 case LeaveNotify:
668 if (bPtr->groupIndex == 0) {
669 bPtr->flags.wasPushed = bPtr->flags.pushed;
670 if (bPtr->flags.pushed) {
671 bPtr->flags.selected = bPtr->flags.prevSelected;
672 dopaint = 1;
674 bPtr->flags.pushed = 0;
676 break;
678 case ButtonPress:
679 if (event->xbutton.button == Button1) {
680 if (bPtr->groupIndex>0) {
681 if (!bPtr->flags.selected)
682 doclick = 1;
683 bPtr->flags.pushed = 1;
684 bPtr->flags.selected = 1;
685 dopaint = 1;
686 break;
688 bPtr->flags.wasPushed = 0;
689 bPtr->flags.pushed = 1;
690 bPtr->flags.prevSelected = bPtr->flags.selected;
691 bPtr->flags.selected = !bPtr->flags.selected;
692 dopaint = 1;
694 if (bPtr->flags.continuous && !bPtr->timer) {
695 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay*1000),
696 autoRepeat, bPtr);
699 break;
701 case ButtonRelease:
702 if (event->xbutton.button == Button1) {
703 if (bPtr->flags.pushed) {
704 if (bPtr->groupIndex==0)
705 doclick = 1;
706 dopaint = 1;
707 if (bPtr->flags.springLoaded) {
708 bPtr->flags.selected = bPtr->flags.prevSelected;
711 bPtr->flags.pushed = 0;
713 if (bPtr->timer) {
714 WMDeleteTimerHandler(bPtr->timer);
715 bPtr->timer = NULL;
717 break;
720 if (dopaint)
721 paintButton(bPtr);
723 if (doclick) {
724 if (bPtr->flags.selected && bPtr->groupIndex>0) {
725 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
728 if (bPtr->action)
729 (*bPtr->action)(bPtr, bPtr->clientData);
735 static void
736 destroyButton(Button *bPtr)
738 if (bPtr->flags.addedObserver) {
739 WMRemoveNotificationObserver(bPtr);
742 if (bPtr->timer)
743 WMDeleteTimerHandler(bPtr->timer);
745 if (bPtr->font)
746 WMReleaseFont(bPtr->font);
748 if (bPtr->caption)
749 free(bPtr->caption);
751 if (bPtr->altCaption)
752 free(bPtr->altCaption);
754 if (bPtr->image)
755 WMReleasePixmap(bPtr->image);
757 if (bPtr->dimage) {
758 /* yuck.. kluge */
759 bPtr->dimage->pixmap = None;
761 WMReleasePixmap(bPtr->dimage);
763 if (bPtr->altImage)
764 WMReleasePixmap(bPtr->altImage);
766 free(bPtr);