bug fix in wbutton.c, made mouse pointer go invisible when typing in
[wmaker-crm.git] / WINGs / wbutton.c
blobf147df6e816f8aa6cd95154df040e9a151e1dbce
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);
284 bPtr->dimage = NULL;
287 if (image) {
288 bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
289 image->pixmap, None,
290 image->width, image->height,
291 image->depth);
292 updateDisabledMask(bPtr);
295 if (bPtr->view->flags.realized) {
296 paintButton(bPtr);
301 void
302 WMSetButtonAltImage(WMButton *bPtr, WMPixmap *image)
304 if (bPtr->altImage!=NULL)
305 WMReleasePixmap(bPtr->altImage);
306 bPtr->altImage = WMRetainPixmap(image);
309 if (bPtr->view->flags.realized) {
310 paintButton(bPtr);
315 void
316 WMSetButtonImagePosition(WMButton *bPtr, WMImagePosition position)
318 bPtr->flags.imagePosition = position;
321 if (bPtr->view->flags.realized) {
322 paintButton(bPtr);
329 void
330 WMSetButtonTextAlignment(WMButton *bPtr, WMAlignment alignment)
332 bPtr->flags.alignment = alignment;
335 if (bPtr->view->flags.realized) {
336 paintButton(bPtr);
340 void
341 WMSetButtonText(WMButton *bPtr, char *text)
343 if (bPtr->caption)
344 free(bPtr->caption);
346 if (text!=NULL) {
347 bPtr->caption = wstrdup(text);
348 } else {
349 bPtr->caption = NULL;
353 if (bPtr->view->flags.realized) {
354 paintButton(bPtr);
359 void
360 WMSetButtonAltText(WMButton *bPtr, char *text)
362 if (bPtr->altCaption)
363 free(bPtr->altCaption);
365 if (text!=NULL) {
366 bPtr->altCaption = wstrdup(text);
367 } else {
368 bPtr->altCaption = NULL;
371 if (bPtr->view->flags.realized) {
372 paintButton(bPtr);
377 void
378 WMSetButtonSelected(WMButton *bPtr, int isSelected)
380 bPtr->flags.selected = isSelected;
382 if (bPtr->view->flags.realized) {
383 paintButton(bPtr);
389 WMGetButtonSelected(WMButton *bPtr)
391 CHECK_CLASS(bPtr, WC_Button);
393 return bPtr->flags.selected;
397 void
398 WMSetButtonBordered(WMButton *bPtr, int isBordered)
400 bPtr->flags.bordered = isBordered;
402 if (bPtr->view->flags.realized) {
403 paintButton(bPtr);
408 void
409 WMSetButtonFont(WMButton *bPtr, WMFont *font)
411 if (bPtr->font)
412 WMReleaseFont(bPtr->font);
414 bPtr->font = WMRetainFont(font);
418 void
419 WMSetButtonEnabled(WMButton *bPtr, Bool flag)
421 bPtr->flags.enabled = flag;
423 if (bPtr->view->flags.mapped) {
424 paintButton(bPtr);
429 void
430 WMSetButtonTag(WMButton *bPtr, int tag)
432 bPtr->tag = tag;
437 void
438 WMPerformButtonClick(WMButton *bPtr)
440 CHECK_CLASS(bPtr, WC_Button);
442 if (!bPtr->flags.enabled)
443 return;
445 bPtr->flags.pushed = 1;
446 bPtr->flags.selected = 1;
448 if (bPtr->view->flags.mapped) {
449 paintButton(bPtr);
450 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
451 wusleep(20000);
454 if (bPtr->groupIndex>0) {
455 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
458 if (bPtr->action)
459 (*bPtr->action)(bPtr, bPtr->clientData);
461 bPtr->flags.pushed = 0;
463 if (bPtr->view->flags.mapped)
464 paintButton(bPtr);
469 void
470 WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData)
472 CHECK_CLASS(bPtr, WC_Button);
474 bPtr->action = action;
476 bPtr->clientData = clientData;
482 static void
483 radioPushObserver(void *observerData, WMNotification *notification)
485 WMButton *bPtr = (WMButton*)observerData;
486 WMButton *pushedButton = (WMButton*)WMGetNotificationObject(notification);
488 if (bPtr!=pushedButton && pushedButton->groupIndex == bPtr->groupIndex
489 && bPtr->groupIndex!=0) {
490 if (bPtr->flags.selected) {
491 bPtr->flags.selected = 0;
492 paintButton(bPtr);
499 void
500 WMGroupButtons(WMButton *bPtr, WMButton *newMember)
502 static int tagIndex = 0;
504 CHECK_CLASS(bPtr, WC_Button);
505 CHECK_CLASS(newMember, WC_Button);
507 if (!bPtr->flags.addedObserver) {
508 WMAddNotificationObserver(radioPushObserver, bPtr,
509 WMPushedRadioNotification, NULL);
510 bPtr->flags.addedObserver = 1;
512 if (!newMember->flags.addedObserver) {
513 WMAddNotificationObserver(radioPushObserver, newMember,
514 WMPushedRadioNotification, NULL);
515 newMember->flags.addedObserver = 1;
518 if (bPtr->groupIndex==0) {
519 bPtr->groupIndex = ++tagIndex;
521 newMember->groupIndex = bPtr->groupIndex;
525 void
526 WMSetButtonContinuous(WMButton *bPtr, Bool flag)
528 bPtr->flags.continuous = flag;
529 if (bPtr->timer) {
530 WMDeleteTimerHandler(bPtr->timer);
531 bPtr->timer = NULL;
536 void
537 WMSetButtonPeriodicDelay(WMButton *bPtr, float delay, float interval)
539 bPtr->periodicInterval = interval;
540 bPtr->periodicDelay = delay;
545 static void
546 paintButton(Button *bPtr)
548 W_Screen *scrPtr = bPtr->view->screen;
549 GC gc;
550 WMReliefType relief;
551 int offset;
552 char *caption;
553 WMPixmap *image;
554 GC textGC;
556 gc = NULL;
557 caption = bPtr->caption;
558 if (bPtr->flags.enabled || !bPtr->dimage)
559 image = bPtr->image;
560 else
561 image = bPtr->dimage;
562 offset = 0;
563 if (bPtr->flags.bordered)
564 relief = WRRaised;
565 else
566 relief = WRFlat;
568 if (bPtr->flags.selected) {
569 if (bPtr->flags.stateLight)
570 gc = WMColorGC(scrPtr->white);
572 if (bPtr->flags.stateChange) {
573 if (bPtr->altCaption) {
574 caption = bPtr->altCaption;
576 if (bPtr->altImage)
577 image = bPtr->altImage;
580 if (bPtr->flags.statePush && bPtr->flags.bordered) {
581 relief = WRSunken;
582 offset = 1;
586 if (bPtr->flags.pushed) {
587 if (bPtr->flags.pushIn) {
588 relief = WRPushed;
589 offset = 1;
591 if (bPtr->flags.pushLight)
592 gc = WMColorGC(scrPtr->white);
594 if (bPtr->flags.pushChange) {
595 if (bPtr->altCaption) {
596 caption = bPtr->altCaption;
598 if (bPtr->altImage)
599 image = bPtr->altImage;
604 if (bPtr->flags.enabled)
605 textGC = WMColorGC(scrPtr->black);
606 else
607 textGC = WMColorGC(scrPtr->darkGray);
609 W_PaintTextAndImage(bPtr->view, True, textGC,
610 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
611 relief, caption, bPtr->flags.alignment, image,
612 bPtr->flags.imagePosition, gc, offset);
617 static void
618 handleEvents(XEvent *event, void *data)
620 Button *bPtr = (Button*)data;
622 CHECK_CLASS(data, WC_Button);
625 switch (event->type) {
626 case Expose:
627 if (event->xexpose.count!=0)
628 break;
629 paintButton(bPtr);
630 break;
632 case DestroyNotify:
633 destroyButton(bPtr);
634 break;
639 static void
640 autoRepeat(void *data)
642 Button *bPtr = (Button*)data;
644 if (bPtr->action && bPtr->flags.pushed)
645 (*bPtr->action)(bPtr, bPtr->clientData);
647 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval*1000),
648 autoRepeat, bPtr);
652 static void
653 handleActionEvents(XEvent *event, void *data)
655 Button *bPtr = (Button*)data;
656 int doclick = 0, dopaint=0;
658 CHECK_CLASS(data, WC_Button);
660 if (!bPtr->flags.enabled)
661 return;
663 switch (event->type) {
664 case EnterNotify:
665 if (bPtr->groupIndex == 0) {
666 bPtr->flags.pushed = bPtr->flags.wasPushed;
667 if (bPtr->flags.pushed) {
668 bPtr->flags.selected = !bPtr->flags.prevSelected;
669 dopaint = 1;
672 break;
674 case LeaveNotify:
675 if (bPtr->groupIndex == 0) {
676 bPtr->flags.wasPushed = bPtr->flags.pushed;
677 if (bPtr->flags.pushed) {
678 bPtr->flags.selected = bPtr->flags.prevSelected;
679 dopaint = 1;
681 bPtr->flags.pushed = 0;
683 break;
685 case ButtonPress:
686 if (event->xbutton.button == Button1) {
687 if (bPtr->groupIndex>0) {
688 if (!bPtr->flags.selected)
689 doclick = 1;
690 bPtr->flags.pushed = 1;
691 bPtr->flags.selected = 1;
692 dopaint = 1;
693 break;
695 bPtr->flags.wasPushed = 0;
696 bPtr->flags.pushed = 1;
697 bPtr->flags.prevSelected = bPtr->flags.selected;
698 bPtr->flags.selected = !bPtr->flags.selected;
699 dopaint = 1;
701 if (bPtr->flags.continuous && !bPtr->timer) {
702 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay*1000),
703 autoRepeat, bPtr);
706 break;
708 case ButtonRelease:
709 if (event->xbutton.button == Button1) {
710 if (bPtr->flags.pushed) {
711 if (bPtr->groupIndex==0)
712 doclick = 1;
713 dopaint = 1;
714 if (bPtr->flags.springLoaded) {
715 bPtr->flags.selected = bPtr->flags.prevSelected;
718 bPtr->flags.pushed = 0;
720 if (bPtr->timer) {
721 WMDeleteTimerHandler(bPtr->timer);
722 bPtr->timer = NULL;
724 break;
727 if (dopaint)
728 paintButton(bPtr);
730 if (doclick) {
731 if (bPtr->flags.selected && bPtr->groupIndex>0) {
732 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
735 if (bPtr->action)
736 (*bPtr->action)(bPtr, bPtr->clientData);
742 static void
743 destroyButton(Button *bPtr)
745 if (bPtr->flags.addedObserver) {
746 WMRemoveNotificationObserver(bPtr);
749 if (bPtr->timer)
750 WMDeleteTimerHandler(bPtr->timer);
752 if (bPtr->font)
753 WMReleaseFont(bPtr->font);
755 if (bPtr->caption)
756 free(bPtr->caption);
758 if (bPtr->altCaption)
759 free(bPtr->altCaption);
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 free(bPtr);