wNETWMCheckClientHints should be void
[wmaker-crm.git] / WINGs / wbutton.c
blob3771684d7598e11190a9592bfcc5f941a9727731
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;
21 W_Pixmap *dimage;
23 void *clientData;
24 WMAction *action;
26 int tag;
28 int groupIndex;
30 float periodicDelay;
31 float periodicInterval;
33 WMHandlerID *timer; /* for continuous mode */
35 struct {
36 WMButtonType type:4;
37 WMImagePosition imagePosition:4;
38 WMAlignment alignment:2;
40 unsigned int selected:1;
42 unsigned int enabled:1;
44 unsigned int dimsWhenDisabled:1;
46 unsigned int bordered:1;
48 unsigned int springLoaded:1;
50 unsigned int pushIn:1; /* change relief while pushed */
52 unsigned int pushLight:1; /* highlight while pushed */
54 unsigned int pushChange:1; /* change caption while pushed */
56 unsigned int stateLight:1; /* state indicated by highlight */
58 unsigned int stateChange:1; /* state indicated by caption change */
60 unsigned int statePush:1; /* state indicated by relief */
62 unsigned int continuous:1; /* continually perform action */
64 unsigned int prevSelected:1;
66 unsigned int pushed:1;
68 unsigned int wasPushed:1;
70 unsigned int redrawPending:1;
72 unsigned int addedObserver:1;
73 } flags;
74 } Button;
76 #define DEFAULT_BUTTON_WIDTH 60
77 #define DEFAULT_BUTTON_HEIGHT 24
78 #define DEFAULT_BUTTON_ALIGNMENT WACenter
79 #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"
87 #define DEFAULT_SWITCH_WIDTH 100
88 #define DEFAULT_SWITCH_HEIGHT 20
89 #define DEFAULT_SWITCH_ALIGNMENT WALeft
90 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
91 #define DEFAULT_SWITCH_TEXT "Switch"
93 static void destroyButton(Button * bPtr);
94 static void paintButton(Button * bPtr);
96 static void handleEvents(XEvent * event, void *data);
97 static void handleActionEvents(XEvent * event, void *data);
99 static char *WMPushedRadioNotification = "WMPushedRadioNotification";
101 #define NFONT(b) (b)->view->screen->normalFont
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 default:
188 case WBTMomentaryLight:
189 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushLightMask);
190 bPtr->flags.bordered = 1;
191 break;
194 bPtr->flags.type = type;
196 if (type == WBTRadio) {
197 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
198 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
199 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
200 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
201 } else if (type == WBTSwitch) {
202 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
203 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
204 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
205 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
208 return bPtr;
211 static void updateDisabledMask(WMButton * bPtr)
213 WMScreen *scr = WMWidgetScreen(bPtr);
214 Display *dpy = scr->display;
216 if (bPtr->image) {
217 XGCValues gcv;
219 if (bPtr->dimage->mask) {
220 XFreePixmap(dpy, bPtr->dimage->mask);
221 bPtr->dimage->mask = None;
224 if (bPtr->flags.dimsWhenDisabled) {
225 bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
226 bPtr->dimage->width, bPtr->dimage->height, 1);
228 XSetForeground(dpy, scr->monoGC, 0);
229 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
230 bPtr->dimage->width, bPtr->dimage->height);
232 gcv.foreground = 1;
233 gcv.background = 0;
234 gcv.stipple = scr->stipple;
235 gcv.fill_style = FillStippled;
236 gcv.clip_mask = bPtr->image->mask;
237 gcv.clip_x_origin = 0;
238 gcv.clip_y_origin = 0;
240 XChangeGC(dpy, scr->monoGC, GCForeground | GCBackground | GCStipple
241 | GCFillStyle | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
243 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
244 bPtr->dimage->width, bPtr->dimage->height);
246 gcv.fill_style = FillSolid;
247 gcv.clip_mask = None;
248 XChangeGC(dpy, scr->monoGC, GCFillStyle | GCClipMask, &gcv);
253 void WMSetButtonImageDefault(WMButton * bPtr)
255 WMSetButtonImage(bPtr, WMWidgetScreen(bPtr)->buttonArrow);
256 WMSetButtonAltImage(bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
259 void WMSetButtonImage(WMButton * bPtr, WMPixmap * image)
261 if (bPtr->image != NULL)
262 WMReleasePixmap(bPtr->image);
263 bPtr->image = WMRetainPixmap(image);
265 if (bPtr->dimage) {
266 bPtr->dimage->pixmap = None;
267 WMReleasePixmap(bPtr->dimage);
268 bPtr->dimage = NULL;
271 if (image) {
272 bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
273 image->pixmap, None,
274 image->width, image->height, image->depth);
275 updateDisabledMask(bPtr);
278 if (bPtr->view->flags.realized) {
279 paintButton(bPtr);
283 void WMSetButtonAltImage(WMButton * bPtr, WMPixmap * image)
285 if (bPtr->altImage != NULL)
286 WMReleasePixmap(bPtr->altImage);
287 bPtr->altImage = WMRetainPixmap(image);
289 if (bPtr->view->flags.realized) {
290 paintButton(bPtr);
294 void WMSetButtonImagePosition(WMButton * bPtr, WMImagePosition position)
296 bPtr->flags.imagePosition = position;
298 if (bPtr->view->flags.realized) {
299 paintButton(bPtr);
303 void WMSetButtonTextAlignment(WMButton * bPtr, WMAlignment alignment)
305 bPtr->flags.alignment = alignment;
307 if (bPtr->view->flags.realized) {
308 paintButton(bPtr);
312 void WMSetButtonText(WMButton * bPtr, char *text)
314 if (bPtr->caption)
315 wfree(bPtr->caption);
317 if (text != NULL) {
318 bPtr->caption = wstrdup(text);
319 } else {
320 bPtr->caption = NULL;
323 if (bPtr->view->flags.realized) {
324 paintButton(bPtr);
328 void WMSetButtonAltText(WMButton * bPtr, char *text)
330 if (bPtr->altCaption)
331 wfree(bPtr->altCaption);
333 if (text != NULL) {
334 bPtr->altCaption = wstrdup(text);
335 } else {
336 bPtr->altCaption = NULL;
339 if (bPtr->view->flags.realized) {
340 paintButton(bPtr);
344 void WMSetButtonTextColor(WMButton * bPtr, WMColor * color)
346 if (bPtr->textColor)
347 WMReleaseColor(bPtr->textColor);
349 bPtr->textColor = WMRetainColor(color);
352 void WMSetButtonAltTextColor(WMButton * bPtr, WMColor * color)
354 if (bPtr->altTextColor)
355 WMReleaseColor(bPtr->altTextColor);
357 bPtr->altTextColor = WMRetainColor(color);
360 void WMSetButtonDisabledTextColor(WMButton * bPtr, WMColor * color)
362 if (bPtr->disTextColor)
363 WMReleaseColor(bPtr->disTextColor);
365 bPtr->disTextColor = WMRetainColor(color);
368 void WMSetButtonSelected(WMButton * bPtr, int isSelected)
370 bPtr->flags.selected = isSelected ? 1 : 0;
372 if (bPtr->view->flags.realized) {
373 paintButton(bPtr);
375 if (bPtr->groupIndex > 0)
376 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
379 int WMGetButtonSelected(WMButton * bPtr)
381 CHECK_CLASS(bPtr, WC_Button);
383 return bPtr->flags.selected;
386 void WMSetButtonBordered(WMButton * bPtr, int isBordered)
388 bPtr->flags.bordered = isBordered;
390 if (bPtr->view->flags.realized) {
391 paintButton(bPtr);
395 void WMSetButtonFont(WMButton * bPtr, WMFont * font)
397 if (bPtr->font)
398 WMReleaseFont(bPtr->font);
400 bPtr->font = WMRetainFont(font);
403 void WMSetButtonEnabled(WMButton * bPtr, Bool flag)
405 bPtr->flags.enabled = ((flag == 0) ? 0 : 1);
407 if (bPtr->view->flags.mapped) {
408 paintButton(bPtr);
412 int WMGetButtonEnabled(WMButton * bPtr)
414 CHECK_CLASS(bPtr, WC_Button);
416 return bPtr->flags.enabled;
419 void WMSetButtonImageDimsWhenDisabled(WMButton * bPtr, Bool flag)
421 bPtr->flags.dimsWhenDisabled = ((flag == 0) ? 0 : 1);
423 updateDisabledMask(bPtr);
426 void WMSetButtonTag(WMButton * bPtr, int tag)
428 bPtr->tag = tag;
431 void 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 bPtr->flags.pushed = 0;
449 if (bPtr->groupIndex > 0) {
450 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
453 if (bPtr->action)
454 (*bPtr->action) (bPtr, bPtr->clientData);
456 if (bPtr->view->flags.mapped)
457 paintButton(bPtr);
460 void WMSetButtonAction(WMButton * bPtr, WMAction * action, void *clientData)
462 CHECK_CLASS(bPtr, WC_Button);
464 bPtr->action = action;
466 bPtr->clientData = clientData;
469 static void radioPushObserver(void *observerData, WMNotification * notification)
471 WMButton *bPtr = (WMButton *) observerData;
472 WMButton *pushedButton = (WMButton *) WMGetNotificationObject(notification);
474 if (bPtr != pushedButton && pushedButton->groupIndex == bPtr->groupIndex && bPtr->groupIndex != 0) {
475 if (bPtr->flags.selected) {
476 bPtr->flags.selected = 0;
477 paintButton(bPtr);
482 void WMGroupButtons(WMButton * bPtr, WMButton * newMember)
484 static int tagIndex = 0;
486 CHECK_CLASS(bPtr, WC_Button);
487 CHECK_CLASS(newMember, WC_Button);
489 if (!bPtr->flags.addedObserver) {
490 WMAddNotificationObserver(radioPushObserver, bPtr, WMPushedRadioNotification, NULL);
491 bPtr->flags.addedObserver = 1;
493 if (!newMember->flags.addedObserver) {
494 WMAddNotificationObserver(radioPushObserver, newMember, WMPushedRadioNotification, NULL);
495 newMember->flags.addedObserver = 1;
498 if (bPtr->groupIndex == 0) {
499 bPtr->groupIndex = ++tagIndex;
501 newMember->groupIndex = bPtr->groupIndex;
504 void WMSetButtonContinuous(WMButton * bPtr, Bool flag)
506 bPtr->flags.continuous = ((flag == 0) ? 0 : 1);
507 if (bPtr->timer) {
508 WMDeleteTimerHandler(bPtr->timer);
509 bPtr->timer = NULL;
513 void WMSetButtonPeriodicDelay(WMButton * bPtr, float delay, float interval)
515 bPtr->periodicInterval = interval;
516 bPtr->periodicDelay = delay;
519 static void paintButton(Button * bPtr)
521 W_Screen *scrPtr = bPtr->view->screen;
522 WMReliefType relief;
523 int offset;
524 char *caption;
525 WMPixmap *image;
526 WMColor *textColor;
527 WMColor *backColor;
529 backColor = NULL;
530 caption = bPtr->caption;
532 if (bPtr->flags.enabled) {
533 textColor = (bPtr->textColor != NULL ? bPtr->textColor : scrPtr->black);
534 } else {
535 textColor = (bPtr->disTextColor != NULL ? bPtr->disTextColor : scrPtr->darkGray);
538 if (bPtr->flags.enabled || !bPtr->dimage)
539 image = bPtr->image;
540 else
541 image = bPtr->dimage;
542 offset = 0;
543 if (bPtr->flags.bordered)
544 relief = WRRaised;
545 else
546 relief = WRFlat;
548 if (bPtr->flags.selected) {
549 if (bPtr->flags.stateLight) {
550 backColor = scrPtr->white;
551 textColor = scrPtr->black;
554 if (bPtr->flags.stateChange) {
555 if (bPtr->altCaption)
556 caption = bPtr->altCaption;
557 if (bPtr->altImage)
558 image = bPtr->altImage;
559 if (bPtr->altTextColor)
560 textColor = bPtr->altTextColor;
563 if (bPtr->flags.statePush && bPtr->flags.bordered) {
564 relief = WRSunken;
565 offset = 1;
569 if (bPtr->flags.pushed) {
570 if (bPtr->flags.pushIn) {
571 relief = WRPushed;
572 offset = 1;
574 if (bPtr->flags.pushLight) {
575 backColor = scrPtr->white;
576 textColor = scrPtr->black;
579 if (bPtr->flags.pushChange) {
580 if (bPtr->altCaption)
581 caption = bPtr->altCaption;
582 if (bPtr->altImage)
583 image = bPtr->altImage;
584 if (bPtr->altTextColor)
585 textColor = bPtr->altTextColor;
589 W_PaintTextAndImage(bPtr->view, True, textColor,
590 (bPtr->font != NULL ? bPtr->font : scrPtr->normalFont),
591 relief, caption, bPtr->flags.alignment, image,
592 bPtr->flags.imagePosition, backColor, offset);
595 static void handleEvents(XEvent * event, void *data)
597 Button *bPtr = (Button *) data;
599 CHECK_CLASS(data, WC_Button);
601 switch (event->type) {
602 case Expose:
603 if (event->xexpose.count != 0)
604 break;
605 paintButton(bPtr);
606 break;
608 case DestroyNotify:
609 destroyButton(bPtr);
610 break;
614 static void autoRepeat(void *data)
616 Button *bPtr = (Button *) data;
618 if (bPtr->action && bPtr->flags.pushed)
619 (*bPtr->action) (bPtr, bPtr->clientData);
621 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval * 1000), autoRepeat, bPtr);
624 static void handleActionEvents(XEvent * event, void *data)
626 Button *bPtr = (Button *) data;
627 int doclick = 0, dopaint = 0;
629 CHECK_CLASS(data, WC_Button);
631 if (!bPtr->flags.enabled)
632 return;
634 switch (event->type) {
635 case EnterNotify:
636 if (bPtr->groupIndex == 0) {
637 bPtr->flags.pushed = bPtr->flags.wasPushed;
638 if (bPtr->flags.pushed) {
639 bPtr->flags.selected = !bPtr->flags.prevSelected;
640 dopaint = 1;
643 break;
645 case LeaveNotify:
646 if (bPtr->groupIndex == 0) {
647 bPtr->flags.wasPushed = bPtr->flags.pushed;
648 if (bPtr->flags.pushed) {
649 bPtr->flags.selected = bPtr->flags.prevSelected;
650 dopaint = 1;
652 bPtr->flags.pushed = 0;
654 break;
656 case ButtonPress:
657 if (event->xbutton.button == Button1) {
658 bPtr->flags.prevSelected = bPtr->flags.selected;
659 bPtr->flags.wasPushed = 0;
660 bPtr->flags.pushed = 1;
661 if (bPtr->groupIndex > 0) {
662 bPtr->flags.selected = 1;
663 dopaint = 1;
664 break;
666 bPtr->flags.selected = !bPtr->flags.selected;
667 dopaint = 1;
669 if (bPtr->flags.continuous && !bPtr->timer) {
670 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay * 1000),
671 autoRepeat, bPtr);
674 break;
676 case ButtonRelease:
677 if (event->xbutton.button == Button1) {
678 if (bPtr->flags.pushed) {
679 if (bPtr->groupIndex == 0 || (bPtr->flags.selected && bPtr->groupIndex > 0))
680 doclick = 1;
681 dopaint = 1;
682 if (bPtr->flags.springLoaded) {
683 bPtr->flags.selected = bPtr->flags.prevSelected;
686 bPtr->flags.pushed = 0;
688 if (bPtr->timer) {
689 WMDeleteTimerHandler(bPtr->timer);
690 bPtr->timer = NULL;
692 break;
695 if (dopaint)
696 paintButton(bPtr);
698 if (doclick) {
699 if (bPtr->flags.selected && bPtr->groupIndex > 0) {
700 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
703 if (bPtr->action)
704 (*bPtr->action) (bPtr, bPtr->clientData);
708 static void destroyButton(Button * bPtr)
710 if (bPtr->flags.addedObserver) {
711 WMRemoveNotificationObserver(bPtr);
714 if (bPtr->timer)
715 WMDeleteTimerHandler(bPtr->timer);
717 if (bPtr->font)
718 WMReleaseFont(bPtr->font);
720 if (bPtr->caption)
721 wfree(bPtr->caption);
723 if (bPtr->altCaption)
724 wfree(bPtr->altCaption);
726 if (bPtr->textColor)
727 WMReleaseColor(bPtr->textColor);
729 if (bPtr->altTextColor)
730 WMReleaseColor(bPtr->altTextColor);
732 if (bPtr->disTextColor)
733 WMReleaseColor(bPtr->disTextColor);
735 if (bPtr->image)
736 WMReleasePixmap(bPtr->image);
738 if (bPtr->dimage) {
739 /* yuck.. kluge */
740 bPtr->dimage->pixmap = None;
742 WMReleasePixmap(bPtr->dimage);
744 if (bPtr->altImage)
745 WMReleasePixmap(bPtr->altImage);
747 wfree(bPtr);