GNOME mouseclickproxy thing fix
[wmaker-crm.git] / WINGs / wbutton.c
blob21bc17b26f81857d045642680f72899dbdd910ef
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 W_ViewProcedureTable _ButtonViewProcedures = {
103 NULL,
104 NULL,
105 NULL
109 static char *WMPushedRadioNotification="WMPushedRadioNotification";
112 #define NFONT(b) (b)->view->screen->normalFont
115 WMButton*
116 WMCreateCustomButton(WMWidget *parent, int behaviourMask)
118 Button *bPtr;
120 bPtr = wmalloc(sizeof(Button));
121 memset(bPtr, 0, sizeof(Button));
123 bPtr->widgetClass = WC_Button;
125 bPtr->view = W_CreateView(W_VIEW(parent));
126 if (!bPtr->view) {
127 free(bPtr);
128 return NULL;
130 bPtr->view->self = bPtr;
132 bPtr->flags.type = 0;
134 bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask)!=0;
135 bPtr->flags.pushIn = (behaviourMask & WBBPushInMask)!=0;
136 bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask)!=0;
137 bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask)!=0;
138 bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask)!=0;
139 bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask)!=0;
140 bPtr->flags.statePush = (behaviourMask & WBBStatePushMask)!=0;
142 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
143 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
144 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
146 bPtr->flags.enabled = 1;
149 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask,
150 handleEvents, bPtr);
152 WMCreateEventHandler(bPtr->view, ButtonPressMask|ButtonReleaseMask
153 |EnterWindowMask|LeaveWindowMask,
154 handleActionEvents, bPtr);
156 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
157 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
158 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
160 return bPtr;
165 WMButton*
166 WMCreateButton(WMWidget *parent, WMButtonType type)
168 W_Screen *scrPtr = W_VIEW(parent)->screen;
169 Button *bPtr;
171 switch (type) {
172 case WBTMomentaryPush:
173 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
174 |WBBPushInMask|WBBPushLightMask);
175 break;
177 case WBTMomentaryChange:
178 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
179 |WBBPushChangeMask);
180 break;
182 case WBTPushOnPushOff:
183 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStatePushMask
184 |WBBStateLightMask);
185 break;
187 case WBTToggle:
188 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStateChangeMask
189 |WBBStatePushMask);
190 break;
192 case WBTOnOff:
193 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
194 break;
196 case WBTSwitch:
197 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
198 bPtr->flags.bordered = 0;
199 bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
200 bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
201 break;
203 case WBTRadio:
204 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
205 bPtr->flags.bordered = 0;
206 bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
207 bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
208 break;
210 default:
211 case WBTMomentaryLight:
212 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
213 |WBBPushLightMask);
214 bPtr->flags.bordered = 1;
215 break;
218 bPtr->flags.type = type;
220 if (type==WBTRadio) {
221 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
222 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
223 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
224 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
225 } else if (type==WBTSwitch) {
226 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
227 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
228 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
229 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
232 return bPtr;
236 static void
237 updateDisabledMask(WMButton *bPtr)
239 WMScreen *scr = WMWidgetScreen(bPtr);
240 Display *dpy = scr->display;
242 if (bPtr->image) {
243 XGCValues gcv;
245 bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
246 bPtr->dimage->width,
247 bPtr->dimage->height, 1);
249 XSetForeground(dpy, scr->monoGC, 0);
250 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
251 bPtr->dimage->width, bPtr->dimage->height);
253 gcv.foreground = 1;
254 gcv.background = 0;
255 gcv.stipple = scr->stipple;
256 gcv.fill_style = FillStippled;
257 gcv.clip_mask = bPtr->image->mask;
258 gcv.clip_x_origin = 0;
259 gcv.clip_y_origin = 0;
261 XChangeGC(dpy, scr->monoGC, GCForeground|GCBackground|GCStipple
262 |GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin, &gcv);
264 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
265 bPtr->dimage->width, bPtr->dimage->height);
267 gcv.fill_style = FillSolid;
268 gcv.clip_mask = None;
269 XChangeGC(dpy, scr->monoGC, GCFillStyle|GCClipMask, &gcv);
274 void
275 WMSetButtonImage(WMButton *bPtr, WMPixmap *image)
277 if (bPtr->image!=NULL)
278 WMReleasePixmap(bPtr->image);
279 bPtr->image = WMRetainPixmap(image);
281 if (bPtr->dimage) {
282 bPtr->dimage->pixmap = None;
283 WMReleasePixmap(bPtr->dimage);
286 if (image) {
287 bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
288 image->pixmap, None,
289 image->width, image->height,
290 image->depth);
291 updateDisabledMask(bPtr);
294 if (bPtr->view->flags.realized) {
295 paintButton(bPtr);
300 void
301 WMSetButtonAltImage(WMButton *bPtr, WMPixmap *image)
303 if (bPtr->altImage!=NULL)
304 WMReleasePixmap(bPtr->altImage);
305 bPtr->altImage = WMRetainPixmap(image);
308 if (bPtr->view->flags.realized) {
309 paintButton(bPtr);
314 void
315 WMSetButtonImagePosition(WMButton *bPtr, WMImagePosition position)
317 bPtr->flags.imagePosition = position;
320 if (bPtr->view->flags.realized) {
321 paintButton(bPtr);
328 void
329 WMSetButtonTextAlignment(WMButton *bPtr, WMAlignment alignment)
331 bPtr->flags.alignment = alignment;
334 if (bPtr->view->flags.realized) {
335 paintButton(bPtr);
339 void
340 WMSetButtonText(WMButton *bPtr, char *text)
342 if (bPtr->caption)
343 free(bPtr->caption);
345 if (text!=NULL) {
346 bPtr->caption = wstrdup(text);
347 } else {
348 bPtr->caption = NULL;
352 if (bPtr->view->flags.realized) {
353 paintButton(bPtr);
358 void
359 WMSetButtonAltText(WMButton *bPtr, char *text)
361 if (bPtr->altCaption)
362 free(bPtr->altCaption);
364 if (text!=NULL) {
365 bPtr->altCaption = wstrdup(text);
366 } else {
367 bPtr->altCaption = NULL;
370 if (bPtr->view->flags.realized) {
371 paintButton(bPtr);
376 void
377 WMSetButtonSelected(WMButton *bPtr, int isSelected)
379 bPtr->flags.selected = isSelected;
381 if (bPtr->view->flags.realized) {
382 paintButton(bPtr);
388 WMGetButtonSelected(WMButton *bPtr)
390 CHECK_CLASS(bPtr, WC_Button);
392 return bPtr->flags.selected;
396 void
397 WMSetButtonBordered(WMButton *bPtr, int isBordered)
399 bPtr->flags.bordered = isBordered;
401 if (bPtr->view->flags.realized) {
402 paintButton(bPtr);
407 void
408 WMSetButtonFont(WMButton *bPtr, WMFont *font)
410 if (bPtr->font)
411 WMReleaseFont(bPtr->font);
413 bPtr->font = WMRetainFont(font);
417 void
418 WMSetButtonEnabled(WMButton *bPtr, Bool flag)
420 bPtr->flags.enabled = flag;
422 if (bPtr->view->flags.mapped) {
423 paintButton(bPtr);
428 void
429 WMSetButtonTag(WMButton *bPtr, int tag)
431 bPtr->tag = tag;
436 void
437 WMPerformButtonClick(WMButton *bPtr)
439 CHECK_CLASS(bPtr, WC_Button);
441 if (!bPtr->flags.enabled)
442 return;
444 bPtr->flags.pushed = 1;
445 bPtr->flags.selected = 1;
447 if (bPtr->view->flags.mapped) {
448 paintButton(bPtr);
449 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
450 wusleep(20000);
453 if (bPtr->groupIndex>0) {
454 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
457 if (bPtr->action)
458 (*bPtr->action)(bPtr, bPtr->clientData);
460 bPtr->flags.pushed = 0;
462 if (bPtr->view->flags.mapped)
463 paintButton(bPtr);
468 void
469 WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData)
471 CHECK_CLASS(bPtr, WC_Button);
473 bPtr->action = action;
475 bPtr->clientData = clientData;
481 static void
482 radioPushObserver(void *observerData, WMNotification *notification)
484 WMButton *bPtr = (WMButton*)observerData;
485 WMButton *pushedButton = (WMButton*)WMGetNotificationObject(notification);
487 if (bPtr!=pushedButton && pushedButton->groupIndex == bPtr->groupIndex
488 && bPtr->groupIndex!=0) {
489 if (bPtr->flags.selected) {
490 bPtr->flags.selected = 0;
491 paintButton(bPtr);
498 void
499 WMGroupButtons(WMButton *bPtr, WMButton *newMember)
501 static int tagIndex = 0;
503 CHECK_CLASS(bPtr, WC_Button);
504 CHECK_CLASS(newMember, WC_Button);
506 if (!bPtr->flags.addedObserver) {
507 WMAddNotificationObserver(radioPushObserver, bPtr,
508 WMPushedRadioNotification, NULL);
509 bPtr->flags.addedObserver = 1;
511 if (!newMember->flags.addedObserver) {
512 WMAddNotificationObserver(radioPushObserver, newMember,
513 WMPushedRadioNotification, NULL);
514 newMember->flags.addedObserver = 1;
517 if (bPtr->groupIndex==0) {
518 bPtr->groupIndex = ++tagIndex;
520 newMember->groupIndex = bPtr->groupIndex;
524 void
525 WMSetButtonContinuous(WMButton *bPtr, Bool flag)
527 bPtr->flags.continuous = flag;
528 if (bPtr->timer) {
529 WMDeleteTimerHandler(bPtr->timer);
530 bPtr->timer = NULL;
535 void
536 WMSetButtonPeriodicDelay(WMButton *bPtr, float delay, float interval)
538 bPtr->periodicInterval = interval;
539 bPtr->periodicDelay = delay;
544 static void
545 paintButton(Button *bPtr)
547 W_Screen *scrPtr = bPtr->view->screen;
548 GC gc;
549 WMReliefType relief;
550 int offset;
551 char *caption;
552 WMPixmap *image;
553 GC textGC;
555 gc = NULL;
556 caption = bPtr->caption;
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 gc = WMColorGC(scrPtr->white);
571 if (bPtr->flags.stateChange) {
572 if (bPtr->altCaption) {
573 caption = bPtr->altCaption;
575 if (bPtr->altImage)
576 image = bPtr->altImage;
579 if (bPtr->flags.statePush && bPtr->flags.bordered) {
580 relief = WRSunken;
581 offset = 1;
585 if (bPtr->flags.pushed) {
586 if (bPtr->flags.pushIn) {
587 relief = WRPushed;
588 offset = 1;
590 if (bPtr->flags.pushLight)
591 gc = WMColorGC(scrPtr->white);
593 if (bPtr->flags.pushChange) {
594 if (bPtr->altCaption) {
595 caption = bPtr->altCaption;
597 if (bPtr->altImage)
598 image = bPtr->altImage;
603 if (bPtr->flags.enabled)
604 textGC = WMColorGC(scrPtr->black);
605 else
606 textGC = WMColorGC(scrPtr->darkGray);
608 W_PaintTextAndImage(bPtr->view, True, textGC,
609 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
610 relief, caption, bPtr->flags.alignment, image,
611 bPtr->flags.imagePosition, gc, offset);
616 static void
617 handleEvents(XEvent *event, void *data)
619 Button *bPtr = (Button*)data;
621 CHECK_CLASS(data, WC_Button);
624 switch (event->type) {
625 case Expose:
626 if (event->xexpose.count!=0)
627 break;
628 paintButton(bPtr);
629 break;
631 case DestroyNotify:
632 destroyButton(bPtr);
633 break;
638 static void
639 autoRepeat(void *data)
641 Button *bPtr = (Button*)data;
643 if (bPtr->action && bPtr->flags.pushed)
644 (*bPtr->action)(bPtr, bPtr->clientData);
646 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval*1000),
647 autoRepeat, bPtr);
651 static void
652 handleActionEvents(XEvent *event, void *data)
654 Button *bPtr = (Button*)data;
655 int doclick = 0, dopaint=0;
657 CHECK_CLASS(data, WC_Button);
659 if (!bPtr->flags.enabled)
660 return;
662 switch (event->type) {
663 case EnterNotify:
664 if (bPtr->groupIndex == 0) {
665 bPtr->flags.pushed = bPtr->flags.wasPushed;
666 if (bPtr->flags.pushed) {
667 bPtr->flags.selected = !bPtr->flags.prevSelected;
668 dopaint = 1;
671 break;
673 case LeaveNotify:
674 if (bPtr->groupIndex == 0) {
675 bPtr->flags.wasPushed = bPtr->flags.pushed;
676 if (bPtr->flags.pushed) {
677 bPtr->flags.selected = bPtr->flags.prevSelected;
678 dopaint = 1;
680 bPtr->flags.pushed = 0;
682 break;
684 case ButtonPress:
685 if (event->xbutton.button == Button1) {
686 if (bPtr->groupIndex>0) {
687 if (!bPtr->flags.selected)
688 doclick = 1;
689 bPtr->flags.pushed = 1;
690 bPtr->flags.selected = 1;
691 dopaint = 1;
692 break;
694 bPtr->flags.wasPushed = 0;
695 bPtr->flags.pushed = 1;
696 bPtr->flags.prevSelected = bPtr->flags.selected;
697 bPtr->flags.selected = !bPtr->flags.selected;
698 dopaint = 1;
700 if (bPtr->flags.continuous && !bPtr->timer) {
701 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay*1000),
702 autoRepeat, bPtr);
705 break;
707 case ButtonRelease:
708 if (event->xbutton.button == Button1) {
709 if (bPtr->flags.pushed) {
710 if (bPtr->groupIndex==0)
711 doclick = 1;
712 dopaint = 1;
713 if (bPtr->flags.springLoaded) {
714 bPtr->flags.selected = bPtr->flags.prevSelected;
717 bPtr->flags.pushed = 0;
719 if (bPtr->timer) {
720 WMDeleteTimerHandler(bPtr->timer);
721 bPtr->timer = NULL;
723 break;
726 if (dopaint)
727 paintButton(bPtr);
729 if (doclick) {
730 if (bPtr->flags.selected && bPtr->groupIndex>0) {
731 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
734 if (bPtr->action)
735 (*bPtr->action)(bPtr, bPtr->clientData);
741 static void
742 destroyButton(Button *bPtr)
744 if (bPtr->flags.addedObserver) {
745 WMRemoveNotificationObserver(bPtr);
748 if (bPtr->timer)
749 WMDeleteTimerHandler(bPtr->timer);
751 if (bPtr->font)
752 WMReleaseFont(bPtr->font);
754 if (bPtr->caption)
755 free(bPtr->caption);
757 if (bPtr->altCaption)
758 free(bPtr->altCaption);
760 if (bPtr->image)
761 WMReleasePixmap(bPtr->image);
763 if (bPtr->dimage) {
764 /* yuck.. kluge */
765 bPtr->dimage->pixmap = None;
767 WMReleasePixmap(bPtr->dimage);
769 if (bPtr->altImage)
770 WMReleasePixmap(bPtr->altImage);
772 free(bPtr);