Initial revision
[wmaker-crm.git] / WINGs / wbutton.c
blob31ee5edeebcb3cbd2fe3ff059a9320b9d30c8eb6
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 void *clientData;
21 WMAction *action;
23 int tag;
25 int groupIndex;
27 float periodicDelay;
28 float periodicInterval;
30 WMHandlerID *timer; /* for continuous mode */
32 struct {
33 WMButtonType type:4;
34 WMImagePosition imagePosition:4;
35 WMAlignment alignment:2;
37 unsigned int selected:1;
39 unsigned int enabled:1;
41 unsigned int bordered:1;
43 unsigned int springLoaded:1;
45 unsigned int pushIn:1; /* change relief while pushed */
47 unsigned int pushLight:1; /* highlight while pushed */
49 unsigned int pushChange:1; /* change caption while pushed */
51 unsigned int stateLight:1; /* state indicated by highlight */
53 unsigned int stateChange:1; /* state indicated by caption change */
55 unsigned int statePush:1; /* state indicated by relief */
57 unsigned int continuous:1; /* continually perform action */
58 /* */
59 unsigned int prevSelected:1;
61 unsigned int pushed:1;
63 unsigned int wasPushed:1;
65 unsigned int redrawPending:1;
67 unsigned int addedObserver:1;
68 } flags;
69 } Button;
73 #define DEFAULT_BUTTON_WIDTH 60
74 #define DEFAULT_BUTTON_HEIGHT 24
75 #define DEFAULT_BUTTON_ALIGNMENT WACenter
76 #define DEFAULT_BUTTON_IS_BORDERED True
79 #define DEFAULT_RADIO_WIDTH 100
80 #define DEFAULT_RADIO_HEIGHT 20
81 #define DEFAULT_RADIO_ALIGNMENT WALeft
82 #define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
83 #define DEFAULT_RADIO_TEXT "Radio"
86 #define DEFAULT_SWITCH_WIDTH 100
87 #define DEFAULT_SWITCH_HEIGHT 20
88 #define DEFAULT_SWITCH_ALIGNMENT WALeft
89 #define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
90 #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);
100 W_ViewProcedureTable _ButtonViewProcedures = {
101 NULL,
102 NULL,
103 NULL
107 static char *WMPushedRadioNotification="WMPushedRadioNotification";
110 #define NFONT(b) (b)->view->screen->normalFont
113 WMButton*
114 WMCreateCustomButton(WMWidget *parent, int behaviourMask)
116 Button *bPtr;
118 bPtr = wmalloc(sizeof(Button));
119 memset(bPtr, 0, sizeof(Button));
121 bPtr->widgetClass = WC_Button;
123 bPtr->view = W_CreateView(W_VIEW(parent));
124 if (!bPtr->view) {
125 free(bPtr);
126 return NULL;
128 bPtr->view->self = bPtr;
130 bPtr->flags.type = 0;
132 bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask)!=0;
133 bPtr->flags.pushIn = (behaviourMask & WBBPushInMask)!=0;
134 bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask)!=0;
135 bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask)!=0;
136 bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask)!=0;
137 bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask)!=0;
138 bPtr->flags.statePush = (behaviourMask & WBBStatePushMask)!=0;
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 bPtr->flags.enabled = 1;
147 WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask,
148 handleEvents, bPtr);
150 WMCreateEventHandler(bPtr->view, ButtonPressMask|ButtonReleaseMask
151 |EnterWindowMask|LeaveWindowMask,
152 handleActionEvents, bPtr);
154 W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
155 bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
156 bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
158 return bPtr;
163 WMButton*
164 WMCreateButton(WMWidget *parent, WMButtonType type)
166 W_Screen *scrPtr = W_VIEW(parent)->screen;
167 Button *bPtr;
169 switch (type) {
170 case WBTMomentaryPush:
171 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
172 |WBBPushInMask|WBBPushLightMask);
173 break;
175 case WBTMomentaryChange:
176 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
177 |WBBPushChangeMask);
178 break;
180 case WBTPushOnPushOff:
181 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStatePushMask
182 |WBBStateLightMask);
183 break;
185 case WBTToggle:
186 bPtr = WMCreateCustomButton(parent, WBBPushInMask|WBBStateChangeMask
187 |WBBStatePushMask);
188 break;
190 case WBTOnOff:
191 bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
192 break;
194 case WBTSwitch:
195 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
196 bPtr->flags.bordered = 0;
197 bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
198 bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
199 break;
201 case WBTRadio:
202 bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
203 bPtr->flags.bordered = 0;
204 bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
205 bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
206 break;
208 default:
209 case WBTMomentaryLight:
210 bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask
211 |WBBPushLightMask);
212 bPtr->flags.bordered = 1;
213 break;
216 bPtr->flags.type = type;
218 if (type==WBTRadio) {
219 W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
220 WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
221 bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
222 bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
223 } else if (type==WBTSwitch) {
224 W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
225 WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
226 bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
227 bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
230 return bPtr;
234 void
235 WMSetButtonImage(WMButton *bPtr, WMPixmap *image)
237 if (bPtr->image!=NULL)
238 WMReleasePixmap(bPtr->image);
239 bPtr->image = WMRetainPixmap(image);
242 if (bPtr->view->flags.realized) {
243 paintButton(bPtr);
248 void
249 WMSetButtonAltImage(WMButton *bPtr, WMPixmap *image)
251 if (bPtr->altImage!=NULL)
252 WMReleasePixmap(bPtr->altImage);
253 bPtr->altImage = WMRetainPixmap(image);
256 if (bPtr->view->flags.realized) {
257 paintButton(bPtr);
262 void
263 WMSetButtonImagePosition(WMButton *bPtr, WMImagePosition position)
265 bPtr->flags.imagePosition = position;
268 if (bPtr->view->flags.realized) {
269 paintButton(bPtr);
276 void
277 WMSetButtonTextAlignment(WMButton *bPtr, WMAlignment alignment)
279 bPtr->flags.alignment = alignment;
282 if (bPtr->view->flags.realized) {
283 paintButton(bPtr);
287 void
288 WMSetButtonText(WMButton *bPtr, char *text)
290 if (bPtr->caption)
291 free(bPtr->caption);
293 if (text!=NULL) {
294 bPtr->caption = wstrdup(text);
295 } else {
296 bPtr->caption = NULL;
300 if (bPtr->view->flags.realized) {
301 paintButton(bPtr);
306 void
307 WMSetButtonAltText(WMButton *bPtr, char *text)
309 if (bPtr->altCaption)
310 free(bPtr->altCaption);
312 if (text!=NULL) {
313 bPtr->altCaption = wstrdup(text);
314 } else {
315 bPtr->altCaption = NULL;
318 if (bPtr->view->flags.realized) {
319 paintButton(bPtr);
324 void
325 WMSetButtonSelected(WMButton *bPtr, int isSelected)
327 bPtr->flags.selected = isSelected;
329 if (bPtr->view->flags.realized) {
330 paintButton(bPtr);
336 WMGetButtonSelected(WMButton *bPtr)
338 CHECK_CLASS(bPtr, WC_Button);
340 return bPtr->flags.selected;
344 void
345 WMSetButtonBordered(WMButton *bPtr, int isBordered)
347 bPtr->flags.bordered = isBordered;
349 if (bPtr->view->flags.realized) {
350 paintButton(bPtr);
355 void
356 WMSetButtonFont(WMButton *bPtr, WMFont *font)
358 if (bPtr->font)
359 WMReleaseFont(bPtr->font);
361 bPtr->font = WMRetainFont(font);
365 void
366 WMSetButtonEnabled(WMButton *bPtr, Bool flag)
368 bPtr->flags.enabled = flag;
370 if (bPtr->view->flags.mapped) {
371 paintButton(bPtr);
376 void
377 WMSetButtonTag(WMButton *bPtr, int tag)
379 bPtr->tag = tag;
384 void
385 WMPerformButtonClick(WMButton *bPtr)
387 CHECK_CLASS(bPtr, WC_Button);
389 if (!bPtr->flags.enabled)
390 return;
392 bPtr->flags.pushed = 1;
393 bPtr->flags.selected = 1;
395 if (bPtr->view->flags.mapped) {
396 paintButton(bPtr);
397 XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
398 wusleep(20000);
401 if (bPtr->groupIndex>0) {
402 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
405 if (bPtr->action)
406 (*bPtr->action)(bPtr, bPtr->clientData);
408 bPtr->flags.pushed = 0;
410 if (bPtr->view->flags.mapped)
411 paintButton(bPtr);
416 void
417 WMSetButtonAction(WMButton *bPtr, WMAction *action, void *clientData)
419 CHECK_CLASS(bPtr, WC_Button);
421 bPtr->action = action;
423 bPtr->clientData = clientData;
429 static void
430 radioPushObserver(void *observerData, WMNotification *notification)
432 WMButton *bPtr = (WMButton*)observerData;
433 WMButton *pushedButton = (WMButton*)WMGetNotificationObject(notification);
435 if (bPtr!=pushedButton && pushedButton->groupIndex == bPtr->groupIndex
436 && bPtr->groupIndex!=0) {
437 if (bPtr->flags.selected) {
438 bPtr->flags.selected = 0;
439 paintButton(bPtr);
446 void
447 WMGroupButtons(WMButton *bPtr, WMButton *newMember)
449 static int tagIndex = 0;
451 CHECK_CLASS(bPtr, WC_Button);
452 CHECK_CLASS(newMember, WC_Button);
454 if (!bPtr->flags.addedObserver) {
455 WMAddNotificationObserver(radioPushObserver, bPtr,
456 WMPushedRadioNotification, NULL);
457 bPtr->flags.addedObserver = 1;
459 if (!newMember->flags.addedObserver) {
460 WMAddNotificationObserver(radioPushObserver, newMember,
461 WMPushedRadioNotification, NULL);
462 newMember->flags.addedObserver = 1;
465 if (bPtr->groupIndex==0) {
466 bPtr->groupIndex = ++tagIndex;
468 newMember->groupIndex = bPtr->groupIndex;
472 void
473 WMSetButtonContinuous(WMButton *bPtr, Bool flag)
475 bPtr->flags.continuous = flag;
476 if (bPtr->timer) {
477 WMDeleteTimerHandler(bPtr->timer);
478 bPtr->timer = NULL;
483 void
484 WMSetButtonPeriodicDelay(WMButton *bPtr, float delay, float interval)
486 bPtr->periodicInterval = interval;
487 bPtr->periodicDelay = delay;
492 static void
493 paintButton(Button *bPtr)
495 W_Screen *scrPtr = bPtr->view->screen;
496 GC gc;
497 WMReliefType relief;
498 int offset;
499 char *caption;
500 WMPixmap *image;
501 GC textGC;
503 gc = NULL;
504 caption = bPtr->caption;
505 image = bPtr->image;
506 offset = 0;
507 if (bPtr->flags.bordered)
508 relief = WRRaised;
509 else
510 relief = WRFlat;
512 if (bPtr->flags.selected) {
513 if (bPtr->flags.stateLight)
514 gc = W_GC(scrPtr->white);
516 if (bPtr->flags.stateChange) {
517 if (bPtr->altCaption) {
518 caption = bPtr->altCaption;
520 if (bPtr->altImage)
521 image = bPtr->altImage;
524 if (bPtr->flags.statePush && bPtr->flags.bordered) {
525 relief = WRSunken;
526 offset = 1;
530 if (bPtr->flags.pushed) {
531 if (bPtr->flags.pushIn) {
532 relief = WRPushed;
533 offset = 1;
535 if (bPtr->flags.pushLight)
536 gc = W_GC(scrPtr->white);
538 if (bPtr->flags.pushChange) {
539 if (bPtr->altCaption) {
540 caption = bPtr->altCaption;
542 if (bPtr->altImage)
543 image = bPtr->altImage;
548 if (bPtr->flags.enabled)
549 textGC = W_GC(scrPtr->black);
550 else
551 textGC = W_GC(scrPtr->darkGray);
553 W_PaintTextAndImage(bPtr->view, True, textGC,
554 (bPtr->font!=NULL ? bPtr->font : scrPtr->normalFont),
555 relief, caption, bPtr->flags.alignment, image,
556 bPtr->flags.imagePosition, gc, offset);
561 static void
562 handleEvents(XEvent *event, void *data)
564 Button *bPtr = (Button*)data;
566 CHECK_CLASS(data, WC_Button);
569 switch (event->type) {
570 case Expose:
571 if (event->xexpose.count!=0)
572 break;
573 paintButton(bPtr);
574 break;
576 case DestroyNotify:
577 destroyButton(bPtr);
578 break;
583 static void
584 autoRepeat(void *data)
586 Button *bPtr = (Button*)data;
588 if (bPtr->action && bPtr->flags.pushed)
589 (*bPtr->action)(bPtr, bPtr->clientData);
591 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval*1000),
592 autoRepeat, bPtr);
596 static void
597 handleActionEvents(XEvent *event, void *data)
599 Button *bPtr = (Button*)data;
600 int doclick = 0, dopaint=0;
602 CHECK_CLASS(data, WC_Button);
604 if (!bPtr->flags.enabled)
605 return;
607 switch (event->type) {
608 case EnterNotify:
609 if (bPtr->groupIndex == 0) {
610 bPtr->flags.pushed = bPtr->flags.wasPushed;
611 if (bPtr->flags.pushed) {
612 bPtr->flags.selected = !bPtr->flags.prevSelected;
613 dopaint = 1;
616 break;
618 case LeaveNotify:
619 if (bPtr->groupIndex == 0) {
620 bPtr->flags.wasPushed = bPtr->flags.pushed;
621 if (bPtr->flags.pushed) {
622 bPtr->flags.selected = bPtr->flags.prevSelected;
623 dopaint = 1;
625 bPtr->flags.pushed = 0;
627 break;
629 case ButtonPress:
630 if (event->xbutton.button == Button1) {
631 if (bPtr->groupIndex>0) {
632 if (!bPtr->flags.selected)
633 doclick = 1;
634 bPtr->flags.pushed = 1;
635 bPtr->flags.selected = 1;
636 dopaint = 1;
637 break;
639 bPtr->flags.wasPushed = 0;
640 bPtr->flags.pushed = 1;
641 bPtr->flags.prevSelected = bPtr->flags.selected;
642 bPtr->flags.selected = !bPtr->flags.selected;
643 dopaint = 1;
645 if (bPtr->flags.continuous && !bPtr->timer) {
646 bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay*1000),
647 autoRepeat, bPtr);
650 break;
652 case ButtonRelease:
653 if (event->xbutton.button == Button1) {
654 if (bPtr->flags.pushed) {
655 if (bPtr->groupIndex==0)
656 doclick = 1;
657 dopaint = 1;
658 if (bPtr->flags.springLoaded) {
659 bPtr->flags.selected = bPtr->flags.prevSelected;
662 bPtr->flags.pushed = 0;
664 if (bPtr->timer) {
665 WMDeleteTimerHandler(bPtr->timer);
666 bPtr->timer = NULL;
668 break;
671 if (dopaint)
672 paintButton(bPtr);
674 if (doclick) {
675 if (bPtr->flags.selected && bPtr->groupIndex>0) {
676 WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
679 if (bPtr->action)
680 (*bPtr->action)(bPtr, bPtr->clientData);
686 static void
687 destroyButton(Button *bPtr)
689 if (bPtr->flags.addedObserver) {
690 WMRemoveNotificationObserver(bPtr);
693 if (bPtr->timer)
694 WMDeleteTimerHandler(bPtr->timer);
696 if (bPtr->font)
697 WMReleaseFont(bPtr->font);
699 if (bPtr->caption)
700 free(bPtr->caption);
702 if (bPtr->altCaption)
703 free(bPtr->altCaption);
705 if (bPtr->image)
706 WMReleasePixmap(bPtr->image);
708 if (bPtr->altImage)
709 WMReleasePixmap(bPtr->altImage);
711 free(bPtr);