wmaker: minor fixes for the size of an aperçu
[wmaker-crm.git] / WINGs / wbutton.c
blobc217edd48976e7ed69b0d805f25ef4535a8b64ab
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, const 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 const char *WMGetButtonText(WMButton *bPtr)
330 return bPtr->caption;
333 void WMSetButtonAltText(WMButton * bPtr, const char *text)
335 if (bPtr->altCaption)
336 wfree(bPtr->altCaption);
338 if (text != NULL) {
339 bPtr->altCaption = wstrdup(text);
340 } else {
341 bPtr->altCaption = NULL;
344 if (bPtr->view->flags.realized) {
345 paintButton(bPtr);
349 void WMSetButtonTextColor(WMButton * bPtr, WMColor * color)
351 if (bPtr->textColor)
352 WMReleaseColor(bPtr->textColor);
354 bPtr->textColor = WMRetainColor(color);
357 void WMSetButtonAltTextColor(WMButton * bPtr, WMColor * color)
359 if (bPtr->altTextColor)
360 WMReleaseColor(bPtr->altTextColor);
362 bPtr->altTextColor = WMRetainColor(color);
365 void WMSetButtonDisabledTextColor(WMButton * bPtr, WMColor * color)
367 if (bPtr->disTextColor)
368 WMReleaseColor(bPtr->disTextColor);
370 bPtr->disTextColor = WMRetainColor(color);
373 void WMSetButtonSelected(WMButton * bPtr, int isSelected)
375 bPtr->flags.selected = isSelected ? 1 : 0;
377 if (bPtr->view->flags.realized) {
378 paintButton(bPtr);
380 if (bPtr->groupIndex > 0)
381 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
384 int WMGetButtonSelected(WMButton * bPtr)
386 CHECK_CLASS(bPtr, WC_Button);
388 return bPtr->flags.selected;
391 void WMSetButtonBordered(WMButton * bPtr, int isBordered)
393 bPtr->flags.bordered = isBordered;
395 if (bPtr->view->flags.realized) {
396 paintButton(bPtr);
400 void WMSetButtonFont(WMButton * bPtr, WMFont * font)
402 if (bPtr->font)
403 WMReleaseFont(bPtr->font);
405 bPtr->font = WMRetainFont(font);
408 void WMSetButtonEnabled(WMButton * bPtr, Bool flag)
410 bPtr->flags.enabled = ((flag == 0) ? 0 : 1);
412 if (bPtr->view->flags.mapped) {
413 paintButton(bPtr);
417 int WMGetButtonEnabled(WMButton * bPtr)
419 CHECK_CLASS(bPtr, WC_Button);
421 return bPtr->flags.enabled;
424 void WMSetButtonImageDimsWhenDisabled(WMButton * bPtr, Bool flag)
426 bPtr->flags.dimsWhenDisabled = ((flag == 0) ? 0 : 1);
428 updateDisabledMask(bPtr);
431 void WMSetButtonTag(WMButton * bPtr, int tag)
433 bPtr->tag = tag;
436 void WMPerformButtonClick(WMButton * bPtr)
438 CHECK_CLASS(bPtr, WC_Button);
440 if (!bPtr->flags.enabled)
441 return;
443 bPtr->flags.pushed = 1;
444 bPtr->flags.selected = 1;
446 if (bPtr->view->flags.mapped) {
447 paintButton(bPtr);
448 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
449 wusleep(20000);
452 bPtr->flags.pushed = 0;
454 if (bPtr->groupIndex > 0) {
455 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
458 if (bPtr->action)
459 (*bPtr->action) (bPtr, bPtr->clientData);
461 if (bPtr->view->flags.mapped)
462 paintButton(bPtr);
465 void WMSetButtonAction(WMButton * bPtr, WMAction * action, void *clientData)
467 CHECK_CLASS(bPtr, WC_Button);
469 bPtr->action = action;
471 bPtr->clientData = clientData;
474 static void radioPushObserver(void *observerData, WMNotification * notification)
476 WMButton *bPtr = (WMButton *) observerData;
477 WMButton *pushedButton = (WMButton *) WMGetNotificationObject(notification);
479 if (bPtr != pushedButton && pushedButton->groupIndex == bPtr->groupIndex && bPtr->groupIndex != 0) {
480 if (bPtr->flags.selected) {
481 bPtr->flags.selected = 0;
482 paintButton(bPtr);
487 void WMGroupButtons(WMButton * bPtr, WMButton * newMember)
489 static int tagIndex = 0;
491 CHECK_CLASS(bPtr, WC_Button);
492 CHECK_CLASS(newMember, WC_Button);
494 if (!bPtr->flags.addedObserver) {
495 WMAddNotificationObserver(radioPushObserver, bPtr, WMPushedRadioNotification, NULL);
496 bPtr->flags.addedObserver = 1;
498 if (!newMember->flags.addedObserver) {
499 WMAddNotificationObserver(radioPushObserver, newMember, WMPushedRadioNotification, NULL);
500 newMember->flags.addedObserver = 1;
503 if (bPtr->groupIndex == 0) {
504 bPtr->groupIndex = ++tagIndex;
506 newMember->groupIndex = bPtr->groupIndex;
509 void WMSetButtonContinuous(WMButton * bPtr, Bool flag)
511 bPtr->flags.continuous = ((flag == 0) ? 0 : 1);
512 if (bPtr->timer) {
513 WMDeleteTimerHandler(bPtr->timer);
514 bPtr->timer = NULL;
518 void WMSetButtonPeriodicDelay(WMButton * bPtr, float delay, float interval)
520 bPtr->periodicInterval = interval;
521 bPtr->periodicDelay = delay;
524 static void paintButton(Button * bPtr)
526 W_Screen *scrPtr = bPtr->view->screen;
527 WMReliefType relief;
528 int offset;
529 char *caption;
530 WMPixmap *image;
531 WMColor *textColor;
532 WMColor *backColor;
534 backColor = NULL;
535 caption = bPtr->caption;
537 if (bPtr->flags.enabled) {
538 textColor = (bPtr->textColor != NULL ? bPtr->textColor : scrPtr->black);
539 } else {
540 textColor = (bPtr->disTextColor != NULL ? bPtr->disTextColor : scrPtr->darkGray);
543 if (bPtr->flags.enabled || !bPtr->dimage)
544 image = bPtr->image;
545 else
546 image = bPtr->dimage;
547 offset = 0;
548 if (bPtr->flags.bordered)
549 relief = WRRaised;
550 else
551 relief = WRFlat;
553 if (bPtr->flags.selected) {
554 if (bPtr->flags.stateLight) {
555 backColor = scrPtr->white;
556 textColor = scrPtr->black;
559 if (bPtr->flags.stateChange) {
560 if (bPtr->altCaption)
561 caption = bPtr->altCaption;
562 if (bPtr->altImage)
563 image = bPtr->altImage;
564 if (bPtr->altTextColor)
565 textColor = bPtr->altTextColor;
568 if (bPtr->flags.statePush && bPtr->flags.bordered) {
569 relief = WRSunken;
570 offset = 1;
574 if (bPtr->flags.pushed) {
575 if (bPtr->flags.pushIn) {
576 relief = WRPushed;
577 offset = 1;
579 if (bPtr->flags.pushLight) {
580 backColor = scrPtr->white;
581 textColor = scrPtr->black;
584 if (bPtr->flags.pushChange) {
585 if (bPtr->altCaption)
586 caption = bPtr->altCaption;
587 if (bPtr->altImage)
588 image = bPtr->altImage;
589 if (bPtr->altTextColor)
590 textColor = bPtr->altTextColor;
594 W_PaintTextAndImage(bPtr->view, True, textColor,
595 (bPtr->font != NULL ? bPtr->font : scrPtr->normalFont),
596 relief, caption, bPtr->flags.alignment, image,
597 bPtr->flags.imagePosition, backColor, offset);
600 static void handleEvents(XEvent * event, void *data)
602 Button *bPtr = (Button *) data;
604 CHECK_CLASS(data, WC_Button);
606 switch (event->type) {
607 case Expose:
608 if (event->xexpose.count != 0)
609 break;
610 paintButton(bPtr);
611 break;
613 case DestroyNotify:
614 destroyButton(bPtr);
615 break;
619 static void autoRepeat(void *data)
621 Button *bPtr = (Button *) data;
623 if (bPtr->action && bPtr->flags.pushed)
624 (*bPtr->action) (bPtr, bPtr->clientData);
626 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval * 1000), autoRepeat, bPtr);
629 static void handleActionEvents(XEvent * event, void *data)
631 Button *bPtr = (Button *) data;
632 int doclick = 0, dopaint = 0;
634 CHECK_CLASS(data, WC_Button);
636 if (!bPtr->flags.enabled)
637 return;
639 switch (event->type) {
640 case EnterNotify:
641 if (bPtr->groupIndex == 0) {
642 bPtr->flags.pushed = bPtr->flags.wasPushed;
643 if (bPtr->flags.pushed) {
644 bPtr->flags.selected = !bPtr->flags.prevSelected;
645 dopaint = 1;
648 break;
650 case LeaveNotify:
651 if (bPtr->groupIndex == 0) {
652 bPtr->flags.wasPushed = bPtr->flags.pushed;
653 if (bPtr->flags.pushed) {
654 bPtr->flags.selected = bPtr->flags.prevSelected;
655 dopaint = 1;
657 bPtr->flags.pushed = 0;
659 break;
661 case ButtonPress:
662 if (event->xbutton.button == Button1) {
663 bPtr->flags.prevSelected = bPtr->flags.selected;
664 bPtr->flags.wasPushed = 0;
665 bPtr->flags.pushed = 1;
666 if (bPtr->groupIndex > 0) {
667 bPtr->flags.selected = 1;
668 dopaint = 1;
669 break;
671 bPtr->flags.selected = !bPtr->flags.selected;
672 dopaint = 1;
674 if (bPtr->flags.continuous && !bPtr->timer) {
675 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay * 1000),
676 autoRepeat, bPtr);
679 break;
681 case ButtonRelease:
682 if (event->xbutton.button == Button1) {
683 if (bPtr->flags.pushed) {
684 if (bPtr->groupIndex == 0 || (bPtr->flags.selected && bPtr->groupIndex > 0))
685 doclick = 1;
686 dopaint = 1;
687 if (bPtr->flags.springLoaded) {
688 bPtr->flags.selected = bPtr->flags.prevSelected;
691 bPtr->flags.pushed = 0;
693 if (bPtr->timer) {
694 WMDeleteTimerHandler(bPtr->timer);
695 bPtr->timer = NULL;
697 break;
700 if (dopaint)
701 paintButton(bPtr);
703 if (doclick) {
704 if (bPtr->flags.selected && bPtr->groupIndex > 0) {
705 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
708 if (bPtr->action)
709 (*bPtr->action) (bPtr, bPtr->clientData);
713 static void destroyButton(Button * bPtr)
715 if (bPtr->flags.addedObserver) {
716 WMRemoveNotificationObserver(bPtr);
719 if (bPtr->timer)
720 WMDeleteTimerHandler(bPtr->timer);
722 if (bPtr->font)
723 WMReleaseFont(bPtr->font);
725 if (bPtr->caption)
726 wfree(bPtr->caption);
728 if (bPtr->altCaption)
729 wfree(bPtr->altCaption);
731 if (bPtr->textColor)
732 WMReleaseColor(bPtr->textColor);
734 if (bPtr->altTextColor)
735 WMReleaseColor(bPtr->altTextColor);
737 if (bPtr->disTextColor)
738 WMReleaseColor(bPtr->disTextColor);
740 if (bPtr->image)
741 WMReleasePixmap(bPtr->image);
743 if (bPtr->dimage) {
744 /* yuck.. kluge */
745 bPtr->dimage->pixmap = None;
747 WMReleasePixmap(bPtr->dimage);
749 if (bPtr->altImage)
750 WMReleasePixmap(bPtr->altImage);
752 wfree(bPtr);