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