1 
2 #include "WINGsP.h"
3 
4 typedef struct W_Button {
5 	W_Class widgetClass;
6 	WMView *view;
7 
8 	char *caption;
9 
10 	char *altCaption;
11 
12 	WMFont *font;
13 
14 	WMColor *textColor;
15 	WMColor *altTextColor;
16 	WMColor *disTextColor;
17 
18 	W_Pixmap *image;
19 	W_Pixmap *altImage;
20 	W_Pixmap *tsImage;
21 
22 	W_Pixmap *dimage;
23 
24 	void *clientData;
25 	WMAction *action;
26 
27 	int tag;
28 
29 	int groupIndex;
30 
31 	float periodicDelay;
32 	float periodicInterval;
33 
34 	WMHandlerID *timer;	/* for continuous mode */
35 
36 	struct {
37 		WMButtonType type:4;
38 		WMImagePosition imagePosition:4;
39 		WMAlignment alignment:2;
40 
41 		unsigned int selected:2;
42 
43 		unsigned int enabled:1;
44 
45 		unsigned int dimsWhenDisabled:1;
46 
47 		unsigned int bordered:1;
48 
49 		unsigned int springLoaded:1;
50 
51 		unsigned int pushIn:1;	/* change relief while pushed */
52 
53 		unsigned int pushLight:1;	/* highlight while pushed */
54 
55 		unsigned int pushChange:1;	/* change caption while pushed */
56 
57 		unsigned int stateLight:1;	/* state indicated by highlight */
58 
59 		unsigned int stateChange:1;	/* state indicated by caption change */
60 
61 		unsigned int statePush:1;	/* state indicated by relief */
62 
63 		unsigned int continuous:1;	/* continually perform action */
64 
65 		unsigned int prevSelected:1;
66 
67 		unsigned int pushed:1;
68 
69 		unsigned int wasPushed:1;
70 
71 		unsigned int redrawPending:1;
72 
73 		unsigned int addedObserver:1;
74 	} flags;
75 } Button;
76 
77 #define DEFAULT_BUTTON_WIDTH	60
78 #define DEFAULT_BUTTON_HEIGHT	24
79 #define DEFAULT_BUTTON_ALIGNMENT	WACenter
80 #define DEFAULT_BUTTON_IS_BORDERED	True
81 
82 #define DEFAULT_RADIO_WIDTH	100
83 #define DEFAULT_RADIO_HEIGHT	20
84 #define DEFAULT_RADIO_ALIGNMENT	WALeft
85 #define DEFAULT_RADIO_IMAGE_POSITION	WIPLeft
86 #define DEFAULT_RADIO_TEXT	"Radio"
87 
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"
93 
94 static void destroyButton(Button * bPtr);
95 static void paintButton(Button * bPtr);
96 
97 static void handleEvents(XEvent * event, void *data);
98 static void handleActionEvents(XEvent * event, void *data);
99 
100 static char *WMPushedRadioNotification = "WMPushedRadioNotification";
101 
102 
WMCreateCustomButton(WMWidget * parent,int behaviourMask)103 WMButton *WMCreateCustomButton(WMWidget * parent, int behaviourMask)
104 {
105 	Button *bPtr;
106 
107 	bPtr = wmalloc(sizeof(Button));
108 
109 	bPtr->widgetClass = WC_Button;
110 
111 	bPtr->view = W_CreateView(W_VIEW(parent));
112 	if (!bPtr->view) {
113 		wfree(bPtr);
114 		return NULL;
115 	}
116 	bPtr->view->self = bPtr;
117 
118 	bPtr->flags.type = 0;
119 
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;
127 
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;
131 
132 	bPtr->flags.enabled = 1;
133 	bPtr->flags.dimsWhenDisabled = 1;
134 
135 	WMCreateEventHandler(bPtr->view, ExposureMask | StructureNotifyMask, handleEvents, bPtr);
136 
137 	WMCreateEventHandler(bPtr->view, ButtonPressMask | ButtonReleaseMask
138 			     | EnterWindowMask | LeaveWindowMask, handleActionEvents, bPtr);
139 
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;
143 
144 	return bPtr;
145 }
146 
WMCreateButton(WMWidget * parent,WMButtonType type)147 WMButton *WMCreateButton(WMWidget * parent, WMButtonType type)
148 {
149 	W_Screen *scrPtr = W_VIEW(parent)->screen;
150 	Button *bPtr;
151 
152 	switch (type) {
153 	case WBTMomentaryPush:
154 		bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushInMask | WBBPushLightMask);
155 		break;
156 
157 	case WBTMomentaryChange:
158 		bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushChangeMask);
159 		break;
160 
161 	case WBTPushOnPushOff:
162 		bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStatePushMask | WBBStateLightMask);
163 		break;
164 
165 	case WBTToggle:
166 		bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStateChangeMask | WBBStatePushMask);
167 		break;
168 
169 	case WBTOnOff:
170 		bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
171 		break;
172 
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;
179 
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;
186 
187 	case WBTTriState:
188 		bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
189 		bPtr->flags.bordered = 0;
190 		bPtr->image = WMRetainPixmap(scrPtr->tristateButtonImageOff);
191 		bPtr->altImage = WMRetainPixmap(scrPtr->tristateButtonImageOn);
192 		bPtr->tsImage = WMRetainPixmap(scrPtr->tristateButtonImageTri);
193 		break;
194 
195 	default:
196 	case WBTMomentaryLight:
197 		bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushLightMask);
198 		bPtr->flags.bordered = 1;
199 		break;
200 	}
201 
202 	bPtr->flags.type = type;
203 
204 	if (type == WBTRadio) {
205 		W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
206 		WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
207 		bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
208 		bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
209 	} else if (type == WBTSwitch || type == WBTTriState) {
210 		W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
211 		WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
212 		bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
213 		bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
214 	}
215 
216 	return bPtr;
217 }
218 
updateDisabledMask(WMButton * bPtr)219 static void updateDisabledMask(WMButton * bPtr)
220 {
221 	WMScreen *scr = WMWidgetScreen(bPtr);
222 	Display *dpy = scr->display;
223 
224 	if (bPtr->image) {
225 		XGCValues gcv;
226 
227 		if (bPtr->dimage->mask) {
228 			XFreePixmap(dpy, bPtr->dimage->mask);
229 			bPtr->dimage->mask = None;
230 		}
231 
232 		if (bPtr->flags.dimsWhenDisabled) {
233 			bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
234 							   bPtr->dimage->width, bPtr->dimage->height, 1);
235 
236 			XSetForeground(dpy, scr->monoGC, 0);
237 			XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
238 				       bPtr->dimage->width, bPtr->dimage->height);
239 
240 			gcv.foreground = 1;
241 			gcv.background = 0;
242 			gcv.stipple = scr->stipple;
243 			gcv.fill_style = FillStippled;
244 			gcv.clip_mask = bPtr->image->mask;
245 			gcv.clip_x_origin = 0;
246 			gcv.clip_y_origin = 0;
247 
248 			XChangeGC(dpy, scr->monoGC, GCForeground | GCBackground | GCStipple
249 				  | GCFillStyle | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
250 
251 			XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
252 				       bPtr->dimage->width, bPtr->dimage->height);
253 
254 			gcv.fill_style = FillSolid;
255 			gcv.clip_mask = None;
256 			XChangeGC(dpy, scr->monoGC, GCFillStyle | GCClipMask, &gcv);
257 		}
258 	}
259 }
260 
WMSetButtonImageDefault(WMButton * bPtr)261 void WMSetButtonImageDefault(WMButton * bPtr)
262 {
263 	WMSetButtonImage(bPtr, WMWidgetScreen(bPtr)->buttonArrow);
264 	WMSetButtonAltImage(bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
265 }
266 
WMSetButtonImage(WMButton * bPtr,WMPixmap * image)267 void WMSetButtonImage(WMButton * bPtr, WMPixmap * image)
268 {
269 	if (bPtr->image != NULL)
270 		WMReleasePixmap(bPtr->image);
271 	bPtr->image = WMRetainPixmap(image);
272 
273 	if (bPtr->dimage) {
274 		bPtr->dimage->pixmap = None;
275 		WMReleasePixmap(bPtr->dimage);
276 		bPtr->dimage = NULL;
277 	}
278 
279 	if (image) {
280 		bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
281 							  image->pixmap, None,
282 							  image->width, image->height, image->depth);
283 		updateDisabledMask(bPtr);
284 	}
285 
286 	if (bPtr->view->flags.realized) {
287 		paintButton(bPtr);
288 	}
289 }
290 
WMSetButtonAltImage(WMButton * bPtr,WMPixmap * image)291 void WMSetButtonAltImage(WMButton * bPtr, WMPixmap * image)
292 {
293 	if (bPtr->altImage != NULL)
294 		WMReleasePixmap(bPtr->altImage);
295 	bPtr->altImage = WMRetainPixmap(image);
296 
297 	if (bPtr->view->flags.realized) {
298 		paintButton(bPtr);
299 	}
300 }
301 
WMSetButtonImagePosition(WMButton * bPtr,WMImagePosition position)302 void WMSetButtonImagePosition(WMButton * bPtr, WMImagePosition position)
303 {
304 	bPtr->flags.imagePosition = position;
305 
306 	if (bPtr->view->flags.realized) {
307 		paintButton(bPtr);
308 	}
309 }
310 
WMSetButtonTextAlignment(WMButton * bPtr,WMAlignment alignment)311 void WMSetButtonTextAlignment(WMButton * bPtr, WMAlignment alignment)
312 {
313 	bPtr->flags.alignment = alignment;
314 
315 	if (bPtr->view->flags.realized) {
316 		paintButton(bPtr);
317 	}
318 }
319 
WMSetButtonText(WMButton * bPtr,const char * text)320 void WMSetButtonText(WMButton * bPtr, const char *text)
321 {
322 	if (bPtr->caption)
323 		wfree(bPtr->caption);
324 
325 	if (text != NULL) {
326 		bPtr->caption = wstrdup(text);
327 	} else {
328 		bPtr->caption = NULL;
329 	}
330 
331 	if (bPtr->view->flags.realized) {
332 		paintButton(bPtr);
333 	}
334 }
335 
WMGetButtonText(WMButton * bPtr)336 const char *WMGetButtonText(WMButton *bPtr)
337 {
338 	return bPtr->caption;
339 }
340 
WMSetButtonAltText(WMButton * bPtr,const char * text)341 void WMSetButtonAltText(WMButton * bPtr, const char *text)
342 {
343 	if (bPtr->altCaption)
344 		wfree(bPtr->altCaption);
345 
346 	if (text != NULL) {
347 		bPtr->altCaption = wstrdup(text);
348 	} else {
349 		bPtr->altCaption = NULL;
350 	}
351 
352 	if (bPtr->view->flags.realized) {
353 		paintButton(bPtr);
354 	}
355 }
356 
WMSetButtonTextColor(WMButton * bPtr,WMColor * color)357 void WMSetButtonTextColor(WMButton * bPtr, WMColor * color)
358 {
359 	if (bPtr->textColor)
360 		WMReleaseColor(bPtr->textColor);
361 
362 	bPtr->textColor = WMRetainColor(color);
363 }
364 
WMSetButtonAltTextColor(WMButton * bPtr,WMColor * color)365 void WMSetButtonAltTextColor(WMButton * bPtr, WMColor * color)
366 {
367 	if (bPtr->altTextColor)
368 		WMReleaseColor(bPtr->altTextColor);
369 
370 	bPtr->altTextColor = WMRetainColor(color);
371 }
372 
WMSetButtonDisabledTextColor(WMButton * bPtr,WMColor * color)373 void WMSetButtonDisabledTextColor(WMButton * bPtr, WMColor * color)
374 {
375 	if (bPtr->disTextColor)
376 		WMReleaseColor(bPtr->disTextColor);
377 
378 	bPtr->disTextColor = WMRetainColor(color);
379 }
380 
WMSetButtonSelected(WMButton * bPtr,int isSelected)381 void WMSetButtonSelected(WMButton * bPtr, int isSelected)
382 {
383 	if ((bPtr->flags.type == WBTTriState) && (isSelected < 0))
384 		bPtr->flags.selected = 2;
385 	else
386 		bPtr->flags.selected = isSelected ? 1 : 0;
387 
388 	if (bPtr->view->flags.realized) {
389 		paintButton(bPtr);
390 	}
391 	if (bPtr->groupIndex > 0)
392 		WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
393 }
394 
WMGetButtonSelected(WMButton * bPtr)395 int WMGetButtonSelected(WMButton * bPtr)
396 {
397 	CHECK_CLASS(bPtr, WC_Button);
398 
399 	if ((bPtr->flags.type == WBTTriState) && (bPtr->flags.selected == 2))
400 		return -1;
401 
402 	return bPtr->flags.selected;
403 }
404 
WMSetButtonBordered(WMButton * bPtr,int isBordered)405 void WMSetButtonBordered(WMButton * bPtr, int isBordered)
406 {
407 	bPtr->flags.bordered = isBordered;
408 
409 	if (bPtr->view->flags.realized) {
410 		paintButton(bPtr);
411 	}
412 }
413 
WMSetButtonFont(WMButton * bPtr,WMFont * font)414 void WMSetButtonFont(WMButton * bPtr, WMFont * font)
415 {
416 	if (bPtr->font)
417 		WMReleaseFont(bPtr->font);
418 
419 	bPtr->font = WMRetainFont(font);
420 }
421 
WMSetButtonEnabled(WMButton * bPtr,Bool flag)422 void WMSetButtonEnabled(WMButton * bPtr, Bool flag)
423 {
424 	bPtr->flags.enabled = ((flag == 0) ? 0 : 1);
425 
426 	if (bPtr->view->flags.mapped) {
427 		paintButton(bPtr);
428 	}
429 }
430 
WMGetButtonEnabled(WMButton * bPtr)431 int WMGetButtonEnabled(WMButton * bPtr)
432 {
433 	CHECK_CLASS(bPtr, WC_Button);
434 
435 	return bPtr->flags.enabled;
436 }
437 
WMSetButtonImageDimsWhenDisabled(WMButton * bPtr,Bool flag)438 void WMSetButtonImageDimsWhenDisabled(WMButton * bPtr, Bool flag)
439 {
440 	bPtr->flags.dimsWhenDisabled = ((flag == 0) ? 0 : 1);
441 
442 	updateDisabledMask(bPtr);
443 }
444 
WMSetButtonTag(WMButton * bPtr,int tag)445 void WMSetButtonTag(WMButton * bPtr, int tag)
446 {
447 	bPtr->tag = tag;
448 }
449 
WMPerformButtonClick(WMButton * bPtr)450 void WMPerformButtonClick(WMButton * bPtr)
451 {
452 	CHECK_CLASS(bPtr, WC_Button);
453 
454 	if (!bPtr->flags.enabled)
455 		return;
456 
457 	bPtr->flags.pushed = 1;
458 	bPtr->flags.selected = 1;
459 
460 	if (bPtr->view->flags.mapped) {
461 		paintButton(bPtr);
462 		XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
463 		wusleep(20000);
464 	}
465 
466 	bPtr->flags.pushed = 0;
467 
468 	if (bPtr->groupIndex > 0) {
469 		WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
470 	}
471 
472 	if (bPtr->action)
473 		(*bPtr->action) (bPtr, bPtr->clientData);
474 
475 	if (bPtr->view->flags.mapped)
476 		paintButton(bPtr);
477 }
478 
WMSetButtonAction(WMButton * bPtr,WMAction * action,void * clientData)479 void WMSetButtonAction(WMButton * bPtr, WMAction * action, void *clientData)
480 {
481 	CHECK_CLASS(bPtr, WC_Button);
482 
483 	bPtr->action = action;
484 
485 	bPtr->clientData = clientData;
486 }
487 
radioPushObserver(void * observerData,WMNotification * notification)488 static void radioPushObserver(void *observerData, WMNotification * notification)
489 {
490 	WMButton *bPtr = (WMButton *) observerData;
491 	WMButton *pushedButton = (WMButton *) WMGetNotificationObject(notification);
492 
493 	if (bPtr != pushedButton && pushedButton->groupIndex == bPtr->groupIndex && bPtr->groupIndex != 0) {
494 		if (bPtr->flags.selected) {
495 			bPtr->flags.selected = 0;
496 			paintButton(bPtr);
497 		}
498 	}
499 }
500 
WMGroupButtons(WMButton * bPtr,WMButton * newMember)501 void WMGroupButtons(WMButton * bPtr, WMButton * newMember)
502 {
503 	static int tagIndex = 0;
504 
505 	CHECK_CLASS(bPtr, WC_Button);
506 	CHECK_CLASS(newMember, WC_Button);
507 
508 	if (!bPtr->flags.addedObserver) {
509 		WMAddNotificationObserver(radioPushObserver, bPtr, WMPushedRadioNotification, NULL);
510 		bPtr->flags.addedObserver = 1;
511 	}
512 	if (!newMember->flags.addedObserver) {
513 		WMAddNotificationObserver(radioPushObserver, newMember, WMPushedRadioNotification, NULL);
514 		newMember->flags.addedObserver = 1;
515 	}
516 
517 	if (bPtr->groupIndex == 0) {
518 		bPtr->groupIndex = ++tagIndex;
519 	}
520 	newMember->groupIndex = bPtr->groupIndex;
521 }
522 
WMSetButtonContinuous(WMButton * bPtr,Bool flag)523 void WMSetButtonContinuous(WMButton * bPtr, Bool flag)
524 {
525 	bPtr->flags.continuous = ((flag == 0) ? 0 : 1);
526 	if (bPtr->timer) {
527 		WMDeleteTimerHandler(bPtr->timer);
528 		bPtr->timer = NULL;
529 	}
530 }
531 
WMSetButtonPeriodicDelay(WMButton * bPtr,float delay,float interval)532 void WMSetButtonPeriodicDelay(WMButton * bPtr, float delay, float interval)
533 {
534 	bPtr->periodicInterval = interval;
535 	bPtr->periodicDelay = delay;
536 }
537 
paintButton(Button * bPtr)538 static void paintButton(Button * bPtr)
539 {
540 	W_Screen *scrPtr = bPtr->view->screen;
541 	WMReliefType relief;
542 	int offset;
543 	char *caption;
544 	WMPixmap *image;
545 	WMColor *textColor;
546 	WMColor *backColor;
547 
548 	backColor = NULL;
549 	caption = bPtr->caption;
550 
551 	if (bPtr->flags.enabled) {
552 		textColor = (bPtr->textColor != NULL ? bPtr->textColor : scrPtr->black);
553 	} else {
554 		textColor = (bPtr->disTextColor != NULL ? bPtr->disTextColor : scrPtr->darkGray);
555 	}
556 
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;
566 
567 	if (bPtr->flags.selected) {
568 		if (bPtr->flags.stateLight) {
569 			backColor = scrPtr->white;
570 			textColor = scrPtr->black;
571 		}
572 
573 		if (bPtr->flags.stateChange) {
574 			if (bPtr->altCaption)
575 				caption = bPtr->altCaption;
576 			if (bPtr->flags.selected == 2)
577 				image = bPtr->tsImage;
578 			else if (bPtr->altImage)
579 				image = bPtr->altImage;
580 			if (bPtr->altTextColor)
581 				textColor = bPtr->altTextColor;
582 		}
583 
584 		if (bPtr->flags.statePush && bPtr->flags.bordered) {
585 			relief = WRSunken;
586 			offset = 1;
587 		}
588 	}
589 
590 	if (bPtr->flags.pushed) {
591 		if (bPtr->flags.pushIn) {
592 			relief = WRPushed;
593 			offset = 1;
594 		}
595 		if (bPtr->flags.pushLight) {
596 			backColor = scrPtr->white;
597 			textColor = scrPtr->black;
598 		}
599 
600 		if (bPtr->flags.pushChange) {
601 			if (bPtr->altCaption)
602 				caption = bPtr->altCaption;
603 			if (bPtr->altImage)
604 				image = bPtr->altImage;
605 			if (bPtr->altTextColor)
606 				textColor = bPtr->altTextColor;
607 		}
608 	}
609 
610 	W_PaintTextAndImage(bPtr->view, True, textColor,
611 			    (bPtr->font != NULL ? bPtr->font : scrPtr->normalFont),
612 			    relief, caption, bPtr->flags.alignment, image,
613 			    bPtr->flags.imagePosition, backColor, offset);
614 }
615 
handleEvents(XEvent * event,void * data)616 static void handleEvents(XEvent * event, void *data)
617 {
618 	Button *bPtr = (Button *) data;
619 
620 	CHECK_CLASS(data, WC_Button);
621 
622 	switch (event->type) {
623 	case Expose:
624 		if (event->xexpose.count != 0)
625 			break;
626 		paintButton(bPtr);
627 		break;
628 
629 	case DestroyNotify:
630 		destroyButton(bPtr);
631 		break;
632 	}
633 }
634 
autoRepeat(void * data)635 static void autoRepeat(void *data)
636 {
637 	Button *bPtr = (Button *) data;
638 
639 	if (bPtr->action && bPtr->flags.pushed)
640 		(*bPtr->action) (bPtr, bPtr->clientData);
641 
642 	bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval * 1000), autoRepeat, bPtr);
643 }
644 
handleActionEvents(XEvent * event,void * data)645 static void handleActionEvents(XEvent * event, void *data)
646 {
647 	Button *bPtr = (Button *) data;
648 	int doclick = 0, dopaint = 0;
649 
650 	CHECK_CLASS(data, WC_Button);
651 
652 	if (!bPtr->flags.enabled)
653 		return;
654 
655 	switch (event->type) {
656 	case EnterNotify:
657 		if (bPtr->groupIndex == 0) {
658 			bPtr->flags.pushed = bPtr->flags.wasPushed;
659 			if (bPtr->flags.pushed) {
660 				bPtr->flags.selected = !bPtr->flags.prevSelected;
661 				dopaint = 1;
662 			}
663 		}
664 		break;
665 
666 	case LeaveNotify:
667 		if (bPtr->groupIndex == 0) {
668 			bPtr->flags.wasPushed = bPtr->flags.pushed;
669 			if (bPtr->flags.pushed) {
670 				bPtr->flags.selected = bPtr->flags.prevSelected;
671 				dopaint = 1;
672 			}
673 			bPtr->flags.pushed = 0;
674 		}
675 		break;
676 
677 	case ButtonPress:
678 		if (event->xbutton.button == Button1) {
679 			static const unsigned int next_state[4] = { [0] = 1, [1] = 2, [2] = 0 };
680 
681 			bPtr->flags.prevSelected = bPtr->flags.selected;
682 			bPtr->flags.wasPushed = 0;
683 			bPtr->flags.pushed = 1;
684 			if (bPtr->groupIndex > 0) {
685 				bPtr->flags.selected = 1;
686 				dopaint = 1;
687 				break;
688 			}
689 			if (bPtr->flags.type == WBTTriState)
690 				bPtr->flags.selected = next_state[bPtr->flags.selected];
691 			else
692 				bPtr->flags.selected = !bPtr->flags.selected;
693 			dopaint = 1;
694 
695 			if (bPtr->flags.continuous && !bPtr->timer) {
696 				bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay * 1000),
697 								autoRepeat, bPtr);
698 			}
699 		}
700 		break;
701 
702 	case ButtonRelease:
703 		if (event->xbutton.button == Button1) {
704 			if (bPtr->flags.pushed) {
705 				if (bPtr->groupIndex == 0 || (bPtr->flags.selected && bPtr->groupIndex > 0))
706 					doclick = 1;
707 				dopaint = 1;
708 				if (bPtr->flags.springLoaded) {
709 					bPtr->flags.selected = bPtr->flags.prevSelected;
710 				}
711 			}
712 			bPtr->flags.pushed = 0;
713 		}
714 		if (bPtr->timer) {
715 			WMDeleteTimerHandler(bPtr->timer);
716 			bPtr->timer = NULL;
717 		}
718 		break;
719 	}
720 
721 	if (dopaint)
722 		paintButton(bPtr);
723 
724 	if (doclick) {
725 		if (bPtr->flags.selected && bPtr->groupIndex > 0) {
726 			WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
727 		}
728 
729 		if (bPtr->action)
730 			(*bPtr->action) (bPtr, bPtr->clientData);
731 	}
732 }
733 
destroyButton(Button * bPtr)734 static void destroyButton(Button * bPtr)
735 {
736 	if (bPtr->flags.addedObserver) {
737 		WMRemoveNotificationObserver(bPtr);
738 	}
739 
740 	if (bPtr->timer)
741 		WMDeleteTimerHandler(bPtr->timer);
742 
743 	if (bPtr->font)
744 		WMReleaseFont(bPtr->font);
745 
746 	if (bPtr->caption)
747 		wfree(bPtr->caption);
748 
749 	if (bPtr->altCaption)
750 		wfree(bPtr->altCaption);
751 
752 	if (bPtr->textColor)
753 		WMReleaseColor(bPtr->textColor);
754 
755 	if (bPtr->altTextColor)
756 		WMReleaseColor(bPtr->altTextColor);
757 
758 	if (bPtr->disTextColor)
759 		WMReleaseColor(bPtr->disTextColor);
760 
761 	if (bPtr->image)
762 		WMReleasePixmap(bPtr->image);
763 
764 	if (bPtr->dimage) {
765 		/* yuck.. kluge */
766 		bPtr->dimage->pixmap = None;
767 
768 		WMReleasePixmap(bPtr->dimage);
769 	}
770 	if (bPtr->altImage)
771 		WMReleasePixmap(bPtr->altImage);
772 
773 	if (bPtr->tsImage)
774 		WMReleasePixmap(bPtr->tsImage);
775 
776 	wfree(bPtr);
777 }
778