Include screen.h in dialog.h for definition of WScreen
[wmaker-crm.git] / WINGs / wbutton.c
blobc8d78d97a3f6abd858a344e30354972e0be4d1dd
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 WMColor *textColor;
15 WMColor *altTextColor;
16 WMColor *disTextColor;
18 W_Pixmap *image;
19 W_Pixmap *altImage;
20 W_Pixmap *tsImage;
22 W_Pixmap *dimage;
24 void *clientData;
25 WMAction *action;
27 int tag;
29 int groupIndex;
31 float periodicDelay;
32 float periodicInterval;
34 WMHandlerID *timer; /* for continuous mode */
36 struct {
37 WMButtonType type:4;
38 WMImagePosition imagePosition:4;
39 WMAlignment alignment:2;
41 unsigned int selected:2;
43 unsigned int enabled:1;
45 unsigned int dimsWhenDisabled: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 */
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;
77 #define DEFAULT_BUTTON_WIDTH 60
78 #define DEFAULT_BUTTON_HEIGHT 24
79 #define DEFAULT_BUTTON_ALIGNMENT WACenter
80 #define DEFAULT_BUTTON_IS_BORDERED True
82 #define DEFAULT_RADIO_WIDTH 100
83 #define DEFAULT_RADIO_HEIGHT 20
84 #define DEFAULT_RADIO_ALIGNMENT WALeft
85 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
86 #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"
94 static void destroyButton(Button * bPtr);
95 static void paintButton(Button * bPtr);
97 static void handleEvents(XEvent * event, void *data);
98 static void handleActionEvents(XEvent * event, void *data);
100 static char *WMPushedRadioNotification = "WMPushedRadioNotification";
103 WMButton *WMCreateCustomButton(WMWidget * parent, int behaviourMask)
105 Button *bPtr;
107 bPtr = wmalloc(sizeof(Button));
109 bPtr->widgetClass = WC_Button;
111 bPtr->view = W_CreateView(W_VIEW(parent));
112 if (!bPtr->view) {
113 wfree(bPtr);
114 return NULL;
116 bPtr->view->self = bPtr;
118 bPtr->flags.type = 0;
120 bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask) != 0;
121 bPtr->flags.pushIn = (behaviourMask & WBBPushInMask) != 0;
122 bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask) != 0;
123 bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask) != 0;
124 bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask) != 0;
125 bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask) != 0;
126 bPtr->flags.statePush = (behaviourMask & WBBStatePushMask) != 0;
128 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
129 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
130 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
132 bPtr->flags.enabled = 1;
133 bPtr->flags.dimsWhenDisabled = 1;
135 WMCreateEventHandler(bPtr->view, ExposureMask | StructureNotifyMask, handleEvents, bPtr);
137 WMCreateEventHandler(bPtr->view, ButtonPressMask | ButtonReleaseMask
138 | EnterWindowMask | LeaveWindowMask, handleActionEvents, bPtr);
140 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
141 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
142 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
144 return bPtr;
147 WMButton *WMCreateButton(WMWidget * parent, WMButtonType type)
149 W_Screen *scrPtr = W_VIEW(parent)->screen;
150 Button *bPtr;
152 switch (type) {
153 case WBTMomentaryPush:
154 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushInMask | WBBPushLightMask);
155 break;
157 case WBTMomentaryChange:
158 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushChangeMask);
159 break;
161 case WBTPushOnPushOff:
162 bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStatePushMask | WBBStateLightMask);
163 break;
165 case WBTToggle:
166 bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStateChangeMask | WBBStatePushMask);
167 break;
169 case WBTOnOff:
170 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
171 break;
173 case WBTSwitch:
174 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
175 bPtr->flags.bordered = 0;
176 bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
177 bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
178 break;
180 case WBTRadio:
181 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
182 bPtr->flags.bordered = 0;
183 bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
184 bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
185 break;
187 case WBTTriState:
188 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
189 bPtr->flags.bordered = 0;
190 bPtr->image = WMRetainPixmap(scrPtr->tristateButtonImageOff);
191 bPtr->altImage = WMRetainPixmap(scrPtr->tristateButtonImageOn);
192 bPtr->tsImage = WMRetainPixmap(scrPtr->tristateButtonImageTri);
193 break;
195 default:
196 case WBTMomentaryLight:
197 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushLightMask);
198 bPtr->flags.bordered = 1;
199 break;
202 bPtr->flags.type = type;
204 if (type == WBTRadio) {
205 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
206 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
207 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
208 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
209 } else if (type == WBTSwitch || type == WBTTriState) {
210 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
211 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
212 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
213 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
216 return bPtr;
219 static void updateDisabledMask(WMButton * bPtr)
221 WMScreen *scr = WMWidgetScreen(bPtr);
222 Display *dpy = scr->display;
224 if (bPtr->image) {
225 XGCValues gcv;
227 if (bPtr->dimage->mask) {
228 XFreePixmap(dpy, bPtr->dimage->mask);
229 bPtr->dimage->mask = None;
232 if (bPtr->flags.dimsWhenDisabled) {
233 bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
234 bPtr->dimage->width, bPtr->dimage->height, 1);
236 XSetForeground(dpy, scr->monoGC, 0);
237 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
238 bPtr->dimage->width, bPtr->dimage->height);
240 gcv.foreground = 1;
241 gcv.background = 0;
242 gcv.stipple = scr->stipple;
243 gcv.fill_style = FillStippled;
244 gcv.clip_mask = bPtr->image->mask;
245 gcv.clip_x_origin = 0;
246 gcv.clip_y_origin = 0;
248 XChangeGC(dpy, scr->monoGC, GCForeground | GCBackground | GCStipple
249 | GCFillStyle | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
251 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
252 bPtr->dimage->width, bPtr->dimage->height);
254 gcv.fill_style = FillSolid;
255 gcv.clip_mask = None;
256 XChangeGC(dpy, scr->monoGC, GCFillStyle | GCClipMask, &gcv);
261 void WMSetButtonImageDefault(WMButton * bPtr)
263 WMSetButtonImage(bPtr, WMWidgetScreen(bPtr)->buttonArrow);
264 WMSetButtonAltImage(bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
267 void WMSetButtonImage(WMButton * bPtr, WMPixmap * image)
269 if (bPtr->image != NULL)
270 WMReleasePixmap(bPtr->image);
271 bPtr->image = WMRetainPixmap(image);
273 if (bPtr->dimage) {
274 bPtr->dimage->pixmap = None;
275 WMReleasePixmap(bPtr->dimage);
276 bPtr->dimage = NULL;
279 if (image) {
280 bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
281 image->pixmap, None,
282 image->width, image->height, image->depth);
283 updateDisabledMask(bPtr);
286 if (bPtr->view->flags.realized) {
287 paintButton(bPtr);
291 void WMSetButtonAltImage(WMButton * bPtr, WMPixmap * image)
293 if (bPtr->altImage != NULL)
294 WMReleasePixmap(bPtr->altImage);
295 bPtr->altImage = WMRetainPixmap(image);
297 if (bPtr->view->flags.realized) {
298 paintButton(bPtr);
302 void WMSetButtonImagePosition(WMButton * bPtr, WMImagePosition position)
304 bPtr->flags.imagePosition = position;
306 if (bPtr->view->flags.realized) {
307 paintButton(bPtr);
311 void WMSetButtonTextAlignment(WMButton * bPtr, WMAlignment alignment)
313 bPtr->flags.alignment = alignment;
315 if (bPtr->view->flags.realized) {
316 paintButton(bPtr);
320 void WMSetButtonText(WMButton * bPtr, const char *text)
322 if (bPtr->caption)
323 wfree(bPtr->caption);
325 if (text != NULL) {
326 bPtr->caption = wstrdup(text);
327 } else {
328 bPtr->caption = NULL;
331 if (bPtr->view->flags.realized) {
332 paintButton(bPtr);
336 const char *WMGetButtonText(WMButton *bPtr)
338 return bPtr->caption;
341 void WMSetButtonAltText(WMButton * bPtr, const char *text)
343 if (bPtr->altCaption)
344 wfree(bPtr->altCaption);
346 if (text != NULL) {
347 bPtr->altCaption = wstrdup(text);
348 } else {
349 bPtr->altCaption = NULL;
352 if (bPtr->view->flags.realized) {
353 paintButton(bPtr);
357 void WMSetButtonTextColor(WMButton * bPtr, WMColor * color)
359 if (bPtr->textColor)
360 WMReleaseColor(bPtr->textColor);
362 bPtr->textColor = WMRetainColor(color);
365 void WMSetButtonAltTextColor(WMButton * bPtr, WMColor * color)
367 if (bPtr->altTextColor)
368 WMReleaseColor(bPtr->altTextColor);
370 bPtr->altTextColor = WMRetainColor(color);
373 void WMSetButtonDisabledTextColor(WMButton * bPtr, WMColor * color)
375 if (bPtr->disTextColor)
376 WMReleaseColor(bPtr->disTextColor);
378 bPtr->disTextColor = WMRetainColor(color);
381 void WMSetButtonSelected(WMButton * bPtr, int isSelected)
383 if ((bPtr->flags.type == WBTTriState) && (isSelected < 0))
384 bPtr->flags.selected = 2;
385 else
386 bPtr->flags.selected = isSelected ? 1 : 0;
388 if (bPtr->view->flags.realized) {
389 paintButton(bPtr);
391 if (bPtr->groupIndex > 0)
392 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
395 int WMGetButtonSelected(WMButton * bPtr)
397 CHECK_CLASS(bPtr, WC_Button);
399 if ((bPtr->flags.type == WBTTriState) && (bPtr->flags.selected == 2))
400 return -1;
402 return bPtr->flags.selected;
405 void WMSetButtonBordered(WMButton * bPtr, int isBordered)
407 bPtr->flags.bordered = isBordered;
409 if (bPtr->view->flags.realized) {
410 paintButton(bPtr);
414 void WMSetButtonFont(WMButton * bPtr, WMFont * font)
416 if (bPtr->font)
417 WMReleaseFont(bPtr->font);
419 bPtr->font = WMRetainFont(font);
422 void WMSetButtonEnabled(WMButton * bPtr, Bool flag)
424 bPtr->flags.enabled = ((flag == 0) ? 0 : 1);
426 if (bPtr->view->flags.mapped) {
427 paintButton(bPtr);
431 int WMGetButtonEnabled(WMButton * bPtr)
433 CHECK_CLASS(bPtr, WC_Button);
435 return bPtr->flags.enabled;
438 void WMSetButtonImageDimsWhenDisabled(WMButton * bPtr, Bool flag)
440 bPtr->flags.dimsWhenDisabled = ((flag == 0) ? 0 : 1);
442 updateDisabledMask(bPtr);
445 void WMSetButtonTag(WMButton * bPtr, int tag)
447 bPtr->tag = tag;
450 void WMPerformButtonClick(WMButton * bPtr)
452 CHECK_CLASS(bPtr, WC_Button);
454 if (!bPtr->flags.enabled)
455 return;
457 bPtr->flags.pushed = 1;
458 bPtr->flags.selected = 1;
460 if (bPtr->view->flags.mapped) {
461 paintButton(bPtr);
462 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
463 wusleep(20000);
466 bPtr->flags.pushed = 0;
468 if (bPtr->groupIndex > 0) {
469 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
472 if (bPtr->action)
473 (*bPtr->action) (bPtr, bPtr->clientData);
475 if (bPtr->view->flags.mapped)
476 paintButton(bPtr);
479 void WMSetButtonAction(WMButton * bPtr, WMAction * action, void *clientData)
481 CHECK_CLASS(bPtr, WC_Button);
483 bPtr->action = action;
485 bPtr->clientData = clientData;
488 static void radioPushObserver(void *observerData, WMNotification * notification)
490 WMButton *bPtr = (WMButton *) observerData;
491 WMButton *pushedButton = (WMButton *) WMGetNotificationObject(notification);
493 if (bPtr != pushedButton && pushedButton->groupIndex == bPtr->groupIndex && bPtr->groupIndex != 0) {
494 if (bPtr->flags.selected) {
495 bPtr->flags.selected = 0;
496 paintButton(bPtr);
501 void WMGroupButtons(WMButton * bPtr, WMButton * newMember)
503 static int tagIndex = 0;
505 CHECK_CLASS(bPtr, WC_Button);
506 CHECK_CLASS(newMember, WC_Button);
508 if (!bPtr->flags.addedObserver) {
509 WMAddNotificationObserver(radioPushObserver, bPtr, WMPushedRadioNotification, NULL);
510 bPtr->flags.addedObserver = 1;
512 if (!newMember->flags.addedObserver) {
513 WMAddNotificationObserver(radioPushObserver, newMember, WMPushedRadioNotification, NULL);
514 newMember->flags.addedObserver = 1;
517 if (bPtr->groupIndex == 0) {
518 bPtr->groupIndex = ++tagIndex;
520 newMember->groupIndex = bPtr->groupIndex;
523 void WMSetButtonContinuous(WMButton * bPtr, Bool flag)
525 bPtr->flags.continuous = ((flag == 0) ? 0 : 1);
526 if (bPtr->timer) {
527 WMDeleteTimerHandler(bPtr->timer);
528 bPtr->timer = NULL;
532 void WMSetButtonPeriodicDelay(WMButton * bPtr, float delay, float interval)
534 bPtr->periodicInterval = interval;
535 bPtr->periodicDelay = delay;
538 static void paintButton(Button * bPtr)
540 W_Screen *scrPtr = bPtr->view->screen;
541 WMReliefType relief;
542 int offset;
543 char *caption;
544 WMPixmap *image;
545 WMColor *textColor;
546 WMColor *backColor;
548 backColor = NULL;
549 caption = bPtr->caption;
551 if (bPtr->flags.enabled) {
552 textColor = (bPtr->textColor != NULL ? bPtr->textColor : scrPtr->black);
553 } else {
554 textColor = (bPtr->disTextColor != NULL ? bPtr->disTextColor : scrPtr->darkGray);
557 if (bPtr->flags.enabled || !bPtr->dimage)
558 image = bPtr->image;
559 else
560 image = bPtr->dimage;
561 offset = 0;
562 if (bPtr->flags.bordered)
563 relief = WRRaised;
564 else
565 relief = WRFlat;
567 if (bPtr->flags.selected) {
568 if (bPtr->flags.stateLight) {
569 backColor = scrPtr->white;
570 textColor = scrPtr->black;
573 if (bPtr->flags.stateChange) {
574 if (bPtr->altCaption)
575 caption = bPtr->altCaption;
576 if (bPtr->flags.selected == 2)
577 image = bPtr->tsImage;
578 else if (bPtr->altImage)
579 image = bPtr->altImage;
580 if (bPtr->altTextColor)
581 textColor = bPtr->altTextColor;
584 if (bPtr->flags.statePush && bPtr->flags.bordered) {
585 relief = WRSunken;
586 offset = 1;
590 if (bPtr->flags.pushed) {
591 if (bPtr->flags.pushIn) {
592 relief = WRPushed;
593 offset = 1;
595 if (bPtr->flags.pushLight) {
596 backColor = scrPtr->white;
597 textColor = scrPtr->black;
600 if (bPtr->flags.pushChange) {
601 if (bPtr->altCaption)
602 caption = bPtr->altCaption;
603 if (bPtr->altImage)
604 image = bPtr->altImage;
605 if (bPtr->altTextColor)
606 textColor = bPtr->altTextColor;
610 W_PaintTextAndImage(bPtr->view, True, textColor,
611 (bPtr->font != NULL ? bPtr->font : scrPtr->normalFont),
612 relief, caption, bPtr->flags.alignment, image,
613 bPtr->flags.imagePosition, backColor, offset);
616 static void handleEvents(XEvent * event, void *data)
618 Button *bPtr = (Button *) data;
620 CHECK_CLASS(data, WC_Button);
622 switch (event->type) {
623 case Expose:
624 if (event->xexpose.count != 0)
625 break;
626 paintButton(bPtr);
627 break;
629 case DestroyNotify:
630 destroyButton(bPtr);
631 break;
635 static void autoRepeat(void *data)
637 Button *bPtr = (Button *) data;
639 if (bPtr->action && bPtr->flags.pushed)
640 (*bPtr->action) (bPtr, bPtr->clientData);
642 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval * 1000), autoRepeat, bPtr);
645 static void handleActionEvents(XEvent * event, void *data)
647 Button *bPtr = (Button *) data;
648 int doclick = 0, dopaint = 0;
650 CHECK_CLASS(data, WC_Button);
652 if (!bPtr->flags.enabled)
653 return;
655 switch (event->type) {
656 case EnterNotify:
657 if (bPtr->groupIndex == 0) {
658 bPtr->flags.pushed = bPtr->flags.wasPushed;
659 if (bPtr->flags.pushed) {
660 bPtr->flags.selected = !bPtr->flags.prevSelected;
661 dopaint = 1;
664 break;
666 case LeaveNotify:
667 if (bPtr->groupIndex == 0) {
668 bPtr->flags.wasPushed = bPtr->flags.pushed;
669 if (bPtr->flags.pushed) {
670 bPtr->flags.selected = bPtr->flags.prevSelected;
671 dopaint = 1;
673 bPtr->flags.pushed = 0;
675 break;
677 case ButtonPress:
678 if (event->xbutton.button == Button1) {
679 static const unsigned int next_state[4] = { [0] = 1, [1] = 2, [2] = 0 };
681 bPtr->flags.prevSelected = bPtr->flags.selected;
682 bPtr->flags.wasPushed = 0;
683 bPtr->flags.pushed = 1;
684 if (bPtr->groupIndex > 0) {
685 bPtr->flags.selected = 1;
686 dopaint = 1;
687 break;
689 if (bPtr->flags.type == WBTTriState)
690 bPtr->flags.selected = next_state[bPtr->flags.selected];
691 else
692 bPtr->flags.selected = !bPtr->flags.selected;
693 dopaint = 1;
695 if (bPtr->flags.continuous && !bPtr->timer) {
696 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay * 1000),
697 autoRepeat, bPtr);
700 break;
702 case ButtonRelease:
703 if (event->xbutton.button == Button1) {
704 if (bPtr->flags.pushed) {
705 if (bPtr->groupIndex == 0 || (bPtr->flags.selected && bPtr->groupIndex > 0))
706 doclick = 1;
707 dopaint = 1;
708 if (bPtr->flags.springLoaded) {
709 bPtr->flags.selected = bPtr->flags.prevSelected;
712 bPtr->flags.pushed = 0;
714 if (bPtr->timer) {
715 WMDeleteTimerHandler(bPtr->timer);
716 bPtr->timer = NULL;
718 break;
721 if (dopaint)
722 paintButton(bPtr);
724 if (doclick) {
725 if (bPtr->flags.selected && bPtr->groupIndex > 0) {
726 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
729 if (bPtr->action)
730 (*bPtr->action) (bPtr, bPtr->clientData);
734 static void destroyButton(Button * bPtr)
736 if (bPtr->flags.addedObserver) {
737 WMRemoveNotificationObserver(bPtr);
740 if (bPtr->timer)
741 WMDeleteTimerHandler(bPtr->timer);
743 if (bPtr->font)
744 WMReleaseFont(bPtr->font);
746 if (bPtr->caption)
747 wfree(bPtr->caption);
749 if (bPtr->altCaption)
750 wfree(bPtr->altCaption);
752 if (bPtr->textColor)
753 WMReleaseColor(bPtr->textColor);
755 if (bPtr->altTextColor)
756 WMReleaseColor(bPtr->altTextColor);
758 if (bPtr->disTextColor)
759 WMReleaseColor(bPtr->disTextColor);
761 if (bPtr->image)
762 WMReleasePixmap(bPtr->image);
764 if (bPtr->dimage) {
765 /* yuck.. kluge */
766 bPtr->dimage->pixmap = None;
768 WMReleasePixmap(bPtr->dimage);
770 if (bPtr->altImage)
771 WMReleasePixmap(bPtr->altImage);
773 if (bPtr->tsImage)
774 WMReleasePixmap(bPtr->tsImage);
776 wfree(bPtr);