1 /********************************************************************************
2 * *
3 * R a d i o B u t t o n O b j e c t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1998,2006 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; either *
11 * version 2.1 of the License, or (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 *********************************************************************************
22 * $Id: FXRadioButton.cpp,v 1.64 2006/01/22 17:58:38 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "FXHash.h"
29 #include "FXThread.h"
30 #include "FXStream.h"
31 #include "FXString.h"
32 #include "FXSize.h"
33 #include "FXPoint.h"
34 #include "FXRectangle.h"
35 #include "FXRegistry.h"
36 #include "FXApp.h"
37 #include "FXDCWindow.h"
38 #include "FXRadioButton.h"
39
40 /*
41 To do:
42 - Need check-style also (stay in when pressed, pop out when unpressed).
43 - Who owns the icon(s)?
44 - Arrow buttons should auto-repeat with a timer of some kind
45 - "&Label\tTooltip\tHelptext\thttp://server/application/helponitem.html"
46 - CheckButton should send SEL_COMMAND.
47 - Default button mode:- should somehow get focus.
48 - Weird state change still possible using both keyboard and mouse if two
49 radio buttons are involved.
50 */
51
52
53 #define RADIOBUTTON_MASK (RADIOBUTTON_AUTOGRAY|RADIOBUTTON_AUTOHIDE)
54
55 using namespace FX;
56
57 /*******************************************************************************/
58
59 namespace FX {
60
61 // Map
62 FXDEFMAP(FXRadioButton) FXRadioButtonMap[]={
63 FXMAPFUNC(SEL_PAINT,0,FXRadioButton::onPaint),
64 FXMAPFUNC(SEL_UPDATE,0,FXRadioButton::onUpdate),
65 FXMAPFUNC(SEL_ENTER,0,FXRadioButton::onEnter),
66 FXMAPFUNC(SEL_LEAVE,0,FXRadioButton::onLeave),
67 FXMAPFUNC(SEL_FOCUSIN,0,FXRadioButton::onFocusIn),
68 FXMAPFUNC(SEL_FOCUSOUT,0,FXRadioButton::onFocusOut),
69 FXMAPFUNC(SEL_UNGRABBED,0,FXRadioButton::onUngrabbed),
70 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXRadioButton::onLeftBtnPress),
71 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXRadioButton::onLeftBtnRelease),
72 FXMAPFUNC(SEL_KEYPRESS,0,FXRadioButton::onKeyPress),
73 FXMAPFUNC(SEL_KEYRELEASE,0,FXRadioButton::onKeyRelease),
74 FXMAPFUNC(SEL_KEYPRESS,FXWindow::ID_HOTKEY,FXRadioButton::onHotKeyPress),
75 FXMAPFUNC(SEL_KEYRELEASE,FXWindow::ID_HOTKEY,FXRadioButton::onHotKeyRelease),
76 FXMAPFUNC(SEL_COMMAND,FXRadioButton::ID_CHECK,FXRadioButton::onCheck),
77 FXMAPFUNC(SEL_COMMAND,FXRadioButton::ID_UNCHECK,FXRadioButton::onUncheck),
78 FXMAPFUNC(SEL_COMMAND,FXRadioButton::ID_UNKNOWN,FXRadioButton::onUnknown),
79 FXMAPFUNC(SEL_COMMAND,FXRadioButton::ID_SETVALUE,FXRadioButton::onCmdSetValue),
80 FXMAPFUNC(SEL_COMMAND,FXRadioButton::ID_SETINTVALUE,FXRadioButton::onCmdSetIntValue),
81 FXMAPFUNC(SEL_COMMAND,FXRadioButton::ID_GETINTVALUE,FXRadioButton::onCmdGetIntValue),
82 };
83
84
85 // Object implementation
FXIMPLEMENT(FXRadioButton,FXLabel,FXRadioButtonMap,ARRAYNUMBER (FXRadioButtonMap))86 FXIMPLEMENT(FXRadioButton,FXLabel,FXRadioButtonMap,ARRAYNUMBER(FXRadioButtonMap))
87
88
89 // Deserialization
90 FXRadioButton::FXRadioButton(){
91 radioColor=0;
92 diskColor=0;
93 check=FALSE;
94 oldcheck=FALSE;
95 }
96
97
98 // Make a check button
FXRadioButton(FXComposite * p,const FXString & text,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb)99 FXRadioButton::FXRadioButton(FXComposite* p,const FXString& text,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):
100 FXLabel(p,text,NULL,opts,x,y,w,h,pl,pr,pt,pb){
101 radioColor=getApp()->getForeColor();
102 diskColor=getApp()->getBackColor();
103 target=tgt;
104 message=sel;
105 check=FALSE;
106 oldcheck=FALSE;
107 }
108
109
110 // If window can have focus
canFocus() const111 bool FXRadioButton::canFocus() const { return true; }
112
113
114 // Get default width
getDefaultWidth()115 FXint FXRadioButton::getDefaultWidth(){
116 FXint tw=0,s=0,w;
117 if(!label.empty()){
118 tw=labelWidth(label);
119 s=4;
120 }
121 if(!(options&(ICON_AFTER_TEXT|ICON_BEFORE_TEXT))) w=FXMAX(tw,13); else w=tw+13+s;
122 return padleft+padright+w+(border<<1);
123 }
124
125
126 // Get default height
getDefaultHeight()127 FXint FXRadioButton::getDefaultHeight(){
128 FXint th=0,h;
129 if(!label.empty()){
130 th=labelHeight(label);
131 }
132 if(!(options&(ICON_ABOVE_TEXT|ICON_BELOW_TEXT))) h=FXMAX(th,13); else h=th+13;
133 return padtop+padbottom+h+(border<<1);
134 }
135
136
137 // Check button
setCheck(FXbool s,FXbool notify)138 void FXRadioButton::setCheck(FXbool s,FXbool notify){
139 if(check!=s){
140 check=s;
141 update();
142 if(notify && target){target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)check);}
143 }
144 }
145
146
147 // Change state to checked
onCheck(FXObject *,FXSelector,void *)148 long FXRadioButton::onCheck(FXObject*,FXSelector,void*){
149 setCheck(TRUE);
150 return 1;
151 }
152
153
154 // Change state to unchecked
onUncheck(FXObject *,FXSelector,void *)155 long FXRadioButton::onUncheck(FXObject*,FXSelector,void*){
156 setCheck(FALSE);
157 return 1;
158 }
159
160
161 // Change state to indeterminate
onUnknown(FXObject *,FXSelector,void *)162 long FXRadioButton::onUnknown(FXObject*,FXSelector,void*){
163 setCheck(MAYBE);
164 return 1;
165 }
166
167
168 // Update value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)169 long FXRadioButton::onCmdSetValue(FXObject*,FXSelector,void* ptr){
170 setCheck((FXbool)(FXuval)ptr);
171 return 1;
172 }
173
174
175 // Update value from a message
onCmdSetIntValue(FXObject *,FXSelector,void * ptr)176 long FXRadioButton::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){
177 setCheck((FXbool)*((FXint*)ptr));
178 return 1;
179 }
180
181
182 // Obtain value from text field
onCmdGetIntValue(FXObject *,FXSelector,void * ptr)183 long FXRadioButton::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){
184 *((FXint*)ptr)=getCheck();
185 return 1;
186 }
187
188
189 // Implement auto-hide or auto-gray modes
onUpdate(FXObject * sender,FXSelector sel,void * ptr)190 long FXRadioButton::onUpdate(FXObject* sender,FXSelector sel,void* ptr){
191 if(!FXLabel::onUpdate(sender,sel,ptr)){
192 if(options&RADIOBUTTON_AUTOHIDE){if(shown()){hide();recalc();}}
193 if(options&RADIOBUTTON_AUTOGRAY){disable();}
194 }
195 return 1;
196 }
197
198
199 // Gained focus
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)200 long FXRadioButton::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
201 FXLabel::onFocusIn(sender,sel,ptr);
202 update(border,border,width-(border<<1),height-(border<<1));
203 return 1;
204 }
205
206
207 // Lost focus
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)208 long FXRadioButton::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
209 FXLabel::onFocusOut(sender,sel,ptr);
210 update(border,border,width-(border<<1),height-(border<<1));
211 return 1;
212 }
213
214
215 // Entered button
onEnter(FXObject * sender,FXSelector sel,void * ptr)216 long FXRadioButton::onEnter(FXObject* sender,FXSelector sel,void* ptr){
217 FXLabel::onEnter(sender,sel,ptr);
218 if(isEnabled() && (flags&FLAG_PRESSED)) setCheck(TRUE);
219 return 1;
220 }
221
222
223 // Left button
onLeave(FXObject * sender,FXSelector sel,void * ptr)224 long FXRadioButton::onLeave(FXObject* sender,FXSelector sel,void* ptr){
225 FXLabel::onLeave(sender,sel,ptr);
226 if(isEnabled() && (flags&FLAG_PRESSED)) setCheck(oldcheck);
227 return 1;
228 }
229
230
231 // Pressed mouse button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)232 long FXRadioButton::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
233 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
234 flags&=~FLAG_TIP;
235 if(isEnabled() && !(flags&FLAG_PRESSED)){
236 grab();
237 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
238 oldcheck=check;
239 setCheck(TRUE);
240 flags|=FLAG_PRESSED;
241 flags&=~FLAG_UPDATE;
242 return 1;
243 }
244 return 0;
245 }
246
247
248 // Released mouse button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)249 long FXRadioButton::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
250 if(isEnabled() && (flags&FLAG_PRESSED)){
251 ungrab();
252 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
253 flags|=FLAG_UPDATE;
254 flags&=~FLAG_PRESSED;
255 if(check!=oldcheck && target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)TRUE);
256 return 1;
257 }
258 return 0;
259 }
260
261
262 // Lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)263 long FXRadioButton::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
264 FXLabel::onUngrabbed(sender,sel,ptr);
265 setCheck(oldcheck);
266 flags&=~FLAG_PRESSED;
267 flags|=FLAG_UPDATE;
268 return 1;
269 }
270
271
272 // Key Press
onKeyPress(FXObject *,FXSelector,void * ptr)273 long FXRadioButton::onKeyPress(FXObject*,FXSelector,void* ptr){
274 FXEvent* event=(FXEvent*)ptr;
275 flags&=~FLAG_TIP;
276 if(isEnabled() && !(flags&FLAG_PRESSED)){
277 if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
278 if(event->code==KEY_space || event->code==KEY_KP_Space){
279 oldcheck=check;
280 setCheck(TRUE);
281 flags|=FLAG_PRESSED;
282 flags&=~FLAG_UPDATE;
283 return 1;
284 }
285 }
286 return 0;
287 }
288
289
290 // Key Release
onKeyRelease(FXObject *,FXSelector,void * ptr)291 long FXRadioButton::onKeyRelease(FXObject*,FXSelector,void* ptr){
292 FXEvent* event=(FXEvent*)ptr;
293 if(isEnabled() && (flags&FLAG_PRESSED)){
294 if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
295 if(event->code==KEY_space || event->code==KEY_KP_Space){
296 flags|=FLAG_UPDATE;
297 flags&=~FLAG_PRESSED;
298 if(check!=oldcheck && target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)TRUE);
299 return 1;
300 }
301 }
302 return 0;
303 }
304
305
306 // Hot key combination pressed
onHotKeyPress(FXObject *,FXSelector,void * ptr)307 long FXRadioButton::onHotKeyPress(FXObject*,FXSelector,void* ptr){
308 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
309 flags&=~FLAG_TIP;
310 if(isEnabled() && !(flags&FLAG_PRESSED)){
311 oldcheck=check;
312 setCheck(TRUE);
313 flags|=FLAG_PRESSED;
314 flags&=~FLAG_UPDATE;
315 }
316 return 1;
317 }
318
319
320 // Hot key combination released
onHotKeyRelease(FXObject *,FXSelector,void *)321 long FXRadioButton::onHotKeyRelease(FXObject*,FXSelector,void*){
322 flags&=~FLAG_TIP;
323 if(isEnabled() && (flags&FLAG_PRESSED)){
324 flags|=FLAG_UPDATE;
325 flags&=~FLAG_PRESSED;
326 if(check!=oldcheck && target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)TRUE);
327 }
328 return 1;
329 }
330
331
332 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)333 long FXRadioButton::onPaint(FXObject*,FXSelector,void* ptr){
334 FXEvent *ev=(FXEvent*)ptr;
335 FXint tw=0,th=0,tx,ty,ix,iy;
336 FXRectangle recs[6];
337 FXDCWindow dc(this,ev);
338
339 dc.setForeground(backColor);
340 dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h);
341
342 if(!label.empty()){
343 tw=labelWidth(label);
344 th=labelHeight(label);
345 }
346
347 just_x(tx,ix,tw,13);
348 just_y(ty,iy,th,13);
349
350
351 /*
352 012345678901
353
354 0 SSSS 0
355 1 SSBBBBSS 1
356 2 SBB BBW 2
357 3 SB BB OW 3
358 4 SB BBBB OW 4
359 5 SB BBBBBB OW 5
360 6 SB BBBBBB OW 6
361 7 SB BBBB OW 7
362 8 SB BB OW 8
363 9 SBO OOW 9
364 0 WWOOOOWW 0
365 1 WWWW 1
366
367 012345678901
368 */
369
370 // Inside
371 recs[0].x=ix+4; recs[0].y=iy+2; recs[0].w=4; recs[0].h=1;
372 recs[1].x=ix+3; recs[1].y=iy+3; recs[1].w=6; recs[1].h=1;
373 recs[2].x=ix+2; recs[2].y=iy+4; recs[2].w=8; recs[2].h=4;
374 recs[3].x=ix+3; recs[3].y=iy+8; recs[3].w=6; recs[3].h=1;
375 recs[4].x=ix+4; recs[4].y=iy+9; recs[4].w=4; recs[4].h=1;
376 if(!isEnabled()) // fix by Daniel Gehriger (gehriger@linkcad.com)
377 dc.setForeground(baseColor);
378 else
379 dc.setForeground(diskColor);
380 dc.fillRectangles(recs,5);
381
382 // Top left outside
383 recs[0].x=ix+4; recs[0].y=iy+0; recs[0].w=4; recs[0].h=1;
384 recs[1].x=ix+2; recs[1].y=iy+1; recs[1].w=2; recs[1].h=1;
385 recs[2].x=ix+8; recs[2].y=iy+1; recs[2].w=2; recs[2].h=1;
386 recs[3].x=ix+1; recs[3].y=iy+2; recs[3].w=1; recs[3].h=2;
387 recs[4].x=ix+0; recs[4].y=iy+4; recs[4].w=1; recs[4].h=4;
388 recs[5].x=ix+1; recs[5].y=iy+8; recs[5].w=1; recs[5].h=2;
389 dc.setForeground(shadowColor);
390 dc.fillRectangles(recs,6);
391
392 // Top left inside
393 recs[0].x=ix+4; recs[0].y=iy+1; recs[0].w=4; recs[0].h=1;
394 recs[1].x=ix+2; recs[1].y=iy+2; recs[1].w=2; recs[1].h=1;
395 recs[2].x=ix+8; recs[2].y=iy+2; recs[2].w=2; recs[2].h=1;
396 recs[3].x=ix+2; recs[3].y=iy+3; recs[3].w=1; recs[3].h=1;
397 recs[4].x=ix+1; recs[4].y=iy+4; recs[4].w=1; recs[4].h=4;
398 recs[5].x=ix+2; recs[5].y=iy+8; recs[5].w=1; recs[5].h=2;
399 dc.setForeground(borderColor);
400 dc.fillRectangles(recs,6);
401
402 // Bottom right outside
403 recs[0].x=ix+10;recs[0].y=iy+2; recs[0].w=1; recs[0].h=2;
404 recs[1].x=ix+11;recs[1].y=iy+4; recs[1].w=1; recs[1].h=4;
405 recs[2].x=ix+10;recs[2].y=iy+8; recs[2].w=1; recs[2].h=2;
406 recs[3].x=ix+8; recs[3].y=iy+10;recs[3].w=2; recs[3].h=1;
407 recs[4].x=ix+2; recs[4].y=iy+10;recs[4].w=2; recs[4].h=1;
408 recs[5].x=ix+4; recs[5].y=iy+11;recs[5].w=4; recs[5].h=1;
409 dc.setForeground(hiliteColor);
410 dc.fillRectangles(recs,6);
411
412 // Bottom right inside
413 recs[0].x=ix+9; recs[0].y=iy+3; recs[0].w=1; recs[0].h=1;
414 recs[1].x=ix+10;recs[1].y=iy+4; recs[1].w=1; recs[1].h=4;
415 recs[2].x=ix+9; recs[2].y=iy+8; recs[2].w=1; recs[2].h=1;
416 recs[3].x=ix+8; recs[3].y=iy+9; recs[3].w=2; recs[3].h=1;
417 recs[4].x=ix+3; recs[4].y=iy+9; recs[4].w=1; recs[4].h=1;
418 recs[5].x=ix+4; recs[5].y=iy+10;recs[5].w=4; recs[5].h=1;
419 dc.setForeground(baseColor);
420 dc.fillRectangles(recs,6);
421
422 // Ball inside
423 if(check!=FALSE){
424 recs[0].x=ix+5; recs[0].y=iy+4; recs[0].w=2; recs[0].h=1;
425 recs[1].x=ix+4; recs[1].y=iy+5; recs[1].w=4; recs[1].h=2;
426 recs[2].x=ix+5; recs[2].y=iy+7; recs[2].w=2; recs[2].h=1;
427 if(isEnabled())
428 dc.setForeground(radioColor);
429 else
430 dc.setForeground(shadowColor);
431 dc.fillRectangles(recs,3);
432 }
433
434 // Label
435 if(!label.empty()){
436 dc.setFont(font);
437 if(isEnabled()){
438 dc.setForeground(textColor);
439 drawLabel(dc,label,hotoff,tx,ty,tw,th);
440 if(hasFocus()){
441 dc.drawFocusRectangle(tx-1,ty-1,tw+2,th+2);
442 }
443 }
444 else{
445 dc.setForeground(hiliteColor);
446 drawLabel(dc,label,hotoff,tx+1,ty+1,tw,th);
447 dc.setForeground(shadowColor);
448 drawLabel(dc,label,hotoff,tx,ty,tw,th);
449 }
450 }
451 drawFrame(dc,0,0,width,height);
452 return 1;
453 }
454
455
456 // Set radio color
setRadioColor(FXColor clr)457 void FXRadioButton::setRadioColor(FXColor clr){
458 if(radioColor!=clr){
459 radioColor=clr;
460 update();
461 }
462 }
463
464
465 // Set disk color
setDiskColor(FXColor clr)466 void FXRadioButton::setDiskColor(FXColor clr){
467 if(clr!=diskColor){
468 diskColor=clr;
469 update();
470 }
471 }
472
473
474 // Change radio button style
setRadioButtonStyle(FXuint style)475 void FXRadioButton::setRadioButtonStyle(FXuint style){
476 FXuint opts=(options&~RADIOBUTTON_MASK) | (style&RADIOBUTTON_MASK);
477 if(options!=opts){
478 options=opts;
479 update();
480 }
481 }
482
483
484 // Return current radio button style
getRadioButtonStyle() const485 FXuint FXRadioButton::getRadioButtonStyle() const {
486 return (options&RADIOBUTTON_MASK);
487 }
488
489
490 // Save object to stream
save(FXStream & store) const491 void FXRadioButton::save(FXStream& store) const {
492 FXLabel::save(store);
493 store << radioColor;
494 store << diskColor;
495 }
496
497
498 // Load object from stream
load(FXStream & store)499 void FXRadioButton::load(FXStream& store){
500 FXLabel::load(store);
501 store >> radioColor;
502 store >> diskColor;
503 }
504
505 }
506
507