replaced linked lists with WMBag, added progress indicator
[wmaker-crm.git] / WINGs / wbutton.c
blob062dd3822e6e058f167b1d1afba37fa9457281a3
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 static char *WMPushedRadioNotification="WMPushedRadioNotification";
105 #define NFONT(b) (b)->view->screen->normalFont
108 WMButton*
109 WMCreateCustomButton(WMWidget *parent, int behaviourMask)
111 Button *bPtr;
113 bPtr = wmalloc(sizeof(Button));
114 memset(bPtr, 0, sizeof(Button));
116 bPtr->widgetClass = WC_Button;
118 bPtr->view = W_CreateView(W_VIEW(parent));
119 if (!bPtr->view) {
120 free(bPtr);
121 return NULL;
123 bPtr->view->self = bPtr;
125 bPtr->flags.type = 0;
127 bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask)!=0;
128 bPtr->flags.pushIn = (behaviourMask & WBBPushInMask)!=0;
129 bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask)!=0;
130 bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask)!=0;
131 bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask)!=0;
132 bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask)!=0;
133 bPtr->flags.statePush = (behaviourMask & WBBStatePushMask)!=0;
135 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
136 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
137 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
139 bPtr->flags.enabled = 1;
142 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask,
143 handleEvents, bPtr);
145 WMCreateEventHandler(bPtr->view, ButtonPressMask|ButtonReleaseMask
146 |EnterWindowMask|LeaveWindowMask,
147 handleActionEvents, bPtr);
149 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
150 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
151 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
153 return bPtr;
158 WMButton*
159 WMCreateButton(WMWidget *parent, WMButtonType type)
161 W_Screen *scrPtr = W_VIEW(parent)->screen;
162 Button *bPtr;
164 switch (type) {
165 case WBTMomentaryPush:
166 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
167 |WBBPushInMask|WBBPushLightMask);
168 break;
170 case WBTMomentaryChange:
171 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
172 |WBBPushChangeMask);
173 break;
175 case WBTPushOnPushOff:
176 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStatePushMask
177 |WBBStateLightMask);
178 break;
180 case WBTToggle:
181 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStateChangeMask
182 |WBBStatePushMask);
183 break;
185 case WBTOnOff:
186 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
187 break;
189 case WBTSwitch:
190 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
191 bPtr->flags.bordered = 0;
192 bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
193 bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
194 break;
196 case WBTRadio:
197 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
198 bPtr->flags.bordered = 0;
199 bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
200 bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
201 break;
203 default:
204 case WBTMomentaryLight:
205 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
206 |WBBPushLightMask);
207 bPtr->flags.bordered = 1;
208 break;
211 bPtr->flags.type = type;
213 if (type==WBTRadio) {
214 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
215 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
216 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
217 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
218 } else if (type==WBTSwitch) {
219 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
220 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
221 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
222 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
225 return bPtr;
229 static void
230 updateDisabledMask(WMButton *bPtr)
232 WMScreen *scr = WMWidgetScreen(bPtr);
233 Display *dpy = scr->display;
235 if (bPtr->image) {
236 XGCValues gcv;
238 bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
239 bPtr->dimage->width,
240 bPtr->dimage->height, 1);
242 XSetForeground(dpy, scr->monoGC, 0);
243 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
244 bPtr->dimage->width, bPtr->dimage->height);
246 gcv.foreground = 1;
247 gcv.background = 0;
248 gcv.stipple = scr->stipple;
249 gcv.fill_style = FillStippled;
250 gcv.clip_mask = bPtr->image->mask;
251 gcv.clip_x_origin = 0;
252 gcv.clip_y_origin = 0;
254 XChangeGC(dpy, scr->monoGC, GCForeground|GCBackground|GCStipple
255 |GCFillStyle|GCClipMask|GCClipXOrigin|GCClipYOrigin, &gcv);
257 XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
258 bPtr->dimage->width, bPtr->dimage->height);
260 gcv.fill_style = FillSolid;
261 gcv.clip_mask = None;
262 XChangeGC(dpy, scr->monoGC, GCFillStyle|GCClipMask, &gcv);
266 void
267 WMSetButtonImageDefault(WMButton *bPtr)
269 WMSetButtonImage (bPtr, WMWidgetScreen(bPtr)->buttonArrow);
270 WMSetButtonAltImage (bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
273 void
274 WMSetButtonImage(WMButton *bPtr, WMPixmap *image)
276 if (bPtr->image!=NULL)
277 WMReleasePixmap(bPtr->image);
278 bPtr->image = WMRetainPixmap(image);
280 if (bPtr->dimage) {
281 bPtr->dimage->pixmap = None;
282 WMReleasePixmap(bPtr->dimage);
283 bPtr->dimage = NULL;
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);