1 /********************************************************************************
2 * *
3 * M e n u C h e c k W i d g e t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 2002,2020 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (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 *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/> *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxkeys.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXMutex.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXAccelTable.h"
38 #include "FXFont.h"
39 #include "FXEvent.h"
40 #include "FXWindow.h"
41 #include "FXDCWindow.h"
42 #include "FXApp.h"
43 #include "FXIcon.h"
44 #include "FXMenuCommand.h"
45 #include "FXMenuCheck.h"
46
47 /*
48 Notes:
49 - FXMenuCheck should flip state when invoked, and send new state along
50 in ptr in callback.
51 */
52
53
54 #define LEADSPACE 22
55 #define TRAILSPACE 16
56
57 using namespace FX;
58
59 /*******************************************************************************/
60
61 namespace FX {
62
63 // Map
64 FXDEFMAP(FXMenuCheck) FXMenuCheckMap[]={
65 FXMAPFUNC(SEL_PAINT,0,FXMenuCheck::onPaint),
66 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXMenuCheck::onButtonPress),
67 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXMenuCheck::onButtonRelease),
68 FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXMenuCheck::onButtonPress),
69 FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXMenuCheck::onButtonRelease),
70 FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXMenuCheck::onButtonPress),
71 FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXMenuCheck::onButtonRelease),
72 FXMAPFUNC(SEL_KEYPRESS,0,FXMenuCheck::onKeyPress),
73 FXMAPFUNC(SEL_KEYRELEASE,0,FXMenuCheck::onKeyRelease),
74 FXMAPFUNC(SEL_KEYPRESS,FXWindow::ID_HOTKEY,FXMenuCheck::onHotKeyPress),
75 FXMAPFUNC(SEL_KEYRELEASE,FXWindow::ID_HOTKEY,FXMenuCheck::onHotKeyRelease),
76 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_CHECK,FXMenuCheck::onCheck),
77 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_UNCHECK,FXMenuCheck::onUncheck),
78 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_UNKNOWN,FXMenuCheck::onUnknown),
79 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETVALUE,FXMenuCheck::onCmdSetValue),
80 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETINTVALUE,FXMenuCheck::onCmdSetIntValue),
81 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_GETINTVALUE,FXMenuCheck::onCmdGetIntValue),
82 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_ACCEL,FXMenuCheck::onCmdAccel),
83 };
84
85
86 // Object implementation
FXIMPLEMENT(FXMenuCheck,FXMenuCommand,FXMenuCheckMap,ARRAYNUMBER (FXMenuCheckMap))87 FXIMPLEMENT(FXMenuCheck,FXMenuCommand,FXMenuCheckMap,ARRAYNUMBER(FXMenuCheckMap))
88
89
90 // Command menu item
91 FXMenuCheck::FXMenuCheck(){
92 boxColor=0;
93 check=false;
94 }
95
96
97 // Command menu item
FXMenuCheck(FXComposite * p,const FXString & text,FXObject * tgt,FXSelector sel,FXuint opts)98 FXMenuCheck::FXMenuCheck(FXComposite* p,const FXString& text,FXObject* tgt,FXSelector sel,FXuint opts):FXMenuCommand(p,text,NULL,tgt,sel,opts){
99 boxColor=getApp()->getBackColor();
100 check=false;
101 }
102
103
104 // Get default width
getDefaultWidth()105 FXint FXMenuCheck::getDefaultWidth(){
106 FXint tw,aw;
107 tw=aw=0;
108 if(!label.empty()) tw=font->getTextWidth(label.text(),label.length());
109 if(!accel.empty()) aw=font->getTextWidth(accel.text(),accel.length());
110 if(aw && tw) aw+=5;
111 return LEADSPACE+tw+aw+TRAILSPACE;
112 }
113
114
115 // Get default height
getDefaultHeight()116 FXint FXMenuCheck::getDefaultHeight(){
117 FXint th=0;
118 if(!label.empty() || !accel.empty()) th=font->getFontHeight()+5;
119 return FXMAX(th,20);
120 }
121
122
123 // Check button
setCheck(FXuchar s)124 void FXMenuCheck::setCheck(FXuchar s){
125 if(check!=s){
126 check=s;
127 update();
128 }
129 }
130
131
132 // Change state to checked
onCheck(FXObject *,FXSelector,void *)133 long FXMenuCheck::onCheck(FXObject*,FXSelector,void*){
134 setCheck(true);
135 return 1;
136 }
137
138
139 // Change state to unchecked
onUncheck(FXObject *,FXSelector,void *)140 long FXMenuCheck::onUncheck(FXObject*,FXSelector,void*){
141 setCheck(false);
142 return 1;
143 }
144
145
146 // Change state to indeterminate
onUnknown(FXObject *,FXSelector,void *)147 long FXMenuCheck::onUnknown(FXObject*,FXSelector,void*){
148 setCheck(maybe);
149 return 1;
150 }
151
152
153 // Update value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)154 long FXMenuCheck::onCmdSetValue(FXObject*,FXSelector,void* ptr){
155 setCheck((FXuchar)(FXuval)ptr);
156 return 1;
157 }
158
159
160 // Update value from a message
onCmdSetIntValue(FXObject *,FXSelector,void * ptr)161 long FXMenuCheck::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){
162 setCheck((FXuchar)*((FXint*)ptr));
163 return 1;
164 }
165
166
167 // Obtain value from text field
onCmdGetIntValue(FXObject *,FXSelector,void * ptr)168 long FXMenuCheck::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){
169 *((FXint*)ptr)=getCheck();
170 return 1;
171 }
172
173
174 // Pressed button
onButtonPress(FXObject *,FXSelector,void *)175 long FXMenuCheck::onButtonPress(FXObject*,FXSelector,void*){
176 if(!isEnabled()) return 0;
177 return 1;
178 }
179
180
181 // Released button
onButtonRelease(FXObject *,FXSelector,void *)182 long FXMenuCheck::onButtonRelease(FXObject*,FXSelector,void*){
183 FXbool active=isActive();
184 if(!isEnabled()) return 0;
185 getParent()->handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
186 if(active){
187 setCheck(!check);
188 if(target){ target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)check); }
189 }
190 return 1;
191 }
192
193
194 // Keyboard press
onKeyPress(FXObject *,FXSelector,void * ptr)195 long FXMenuCheck::onKeyPress(FXObject*,FXSelector,void* ptr){
196 FXEvent* event=(FXEvent*)ptr;
197 if(isEnabled() && !(flags&FLAG_PRESSED)){
198 FXTRACE((200,"%s::onKeyPress %p keysym=0x%04x state=%04x\n",getClassName(),this,event->code,event->state));
199 if(event->code==KEY_space || event->code==KEY_KP_Space || event->code==KEY_Return || event->code==KEY_KP_Enter){
200 flags|=FLAG_PRESSED;
201 return 1;
202 }
203 }
204 return 0;
205 }
206
207
208 // Keyboard release
onKeyRelease(FXObject *,FXSelector,void * ptr)209 long FXMenuCheck::onKeyRelease(FXObject*,FXSelector,void* ptr){
210 FXEvent* event=(FXEvent*)ptr;
211 if(isEnabled() && (flags&FLAG_PRESSED)){
212 FXTRACE((200,"%s::onKeyRelease %p keysym=0x%04x state=%04x\n",getClassName(),this,event->code,event->state));
213 if(event->code==KEY_space || event->code==KEY_KP_Space || event->code==KEY_Return || event->code==KEY_KP_Enter){
214 flags&=~FLAG_PRESSED;
215 setCheck(!check);
216 getParent()->handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
217 if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)check);
218 return 1;
219 }
220 }
221 return 0;
222 }
223
224
225 // Hot key combination pressed
onHotKeyPress(FXObject *,FXSelector,void * ptr)226 long FXMenuCheck::onHotKeyPress(FXObject*,FXSelector,void* ptr){
227 FXTRACE((200,"%s::onHotKeyPress %p\n",getClassName(),this));
228 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
229 if(isEnabled() && !(flags&FLAG_PRESSED)){
230 flags|=FLAG_PRESSED;
231 }
232 return 1;
233 }
234
235
236 // Hot key combination released
onHotKeyRelease(FXObject *,FXSelector,void *)237 long FXMenuCheck::onHotKeyRelease(FXObject*,FXSelector,void*){
238 FXTRACE((200,"%s::onHotKeyRelease %p\n",getClassName(),this));
239 if(isEnabled() && (flags&FLAG_PRESSED)){
240 flags&=~FLAG_PRESSED;
241 setCheck(!check);
242 getParent()->handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
243 if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)check);
244 }
245 return 1;
246 }
247
248
249 // Accelerator activated
onCmdAccel(FXObject *,FXSelector,void *)250 long FXMenuCheck::onCmdAccel(FXObject*,FXSelector,void*){
251 if(isEnabled()){
252 setCheck(!check);
253 if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)check);
254 return 1;
255 }
256 return 0;
257 }
258
259
260 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)261 long FXMenuCheck::onPaint(FXObject*,FXSelector,void* ptr){
262 FXEvent *ev=(FXEvent*)ptr;
263 FXDCWindow dc(this,ev);
264 FXint xx,yy;
265
266 xx=LEADSPACE;
267
268 // Grayed out
269 if(!isEnabled()){
270 dc.setForeground(backColor);
271 dc.fillRectangle(0,0,width,height);
272 if(!label.empty()){
273 yy=font->getFontAscent()+(height-font->getFontHeight())/2;
274 dc.setFont(font);
275 dc.setForeground(hiliteColor);
276 dc.drawText(xx+1,yy+1,label);
277 if(!accel.empty()) dc.drawText(width-TRAILSPACE-font->getTextWidth(accel)+1,yy+1,accel);
278 if(0<=hotoff) dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff)+1,yy+2,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1);
279 dc.setForeground(shadowColor);
280 dc.drawText(xx,yy,label);
281 if(!accel.empty()) dc.drawText(width-TRAILSPACE-font->getTextWidth(accel),yy,accel);
282 if(0<=hotoff) dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1);
283 }
284 }
285
286 // Active
287 else if(isActive()){
288 dc.setForeground(selbackColor);
289 dc.fillRectangle(0,0,width,height);
290 if(!label.empty()){
291 yy=font->getFontAscent()+(height-font->getFontHeight())/2;
292 dc.setFont(font);
293 dc.setForeground(isEnabled() ? seltextColor : shadowColor);
294 dc.drawText(xx,yy,label);
295 if(!accel.empty()) dc.drawText(width-TRAILSPACE-font->getTextWidth(accel),yy,accel);
296 if(0<=hotoff) dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1);
297 }
298 }
299
300 // Normal
301 else{
302 dc.setForeground(backColor);
303 dc.fillRectangle(0,0,width,height);
304 if(!label.empty()){
305 yy=font->getFontAscent()+(height-font->getFontHeight())/2;
306 dc.setFont(font);
307 dc.setForeground(textColor);
308 dc.drawText(xx,yy,label);
309 if(!accel.empty()) dc.drawText(width-TRAILSPACE-font->getTextWidth(accel),yy,accel);
310 if(0<=hotoff) dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1);
311 }
312 }
313
314 // Draw the box
315 xx=5;
316 yy=(height-9)/2;
317 if(!isEnabled())
318 dc.setForeground(backColor);
319 else
320 dc.setForeground(boxColor);
321 dc.fillRectangle(xx+1,yy+1,8,8);
322 dc.setForeground(shadowColor);
323 dc.drawRectangle(xx,yy,9,9);
324
325 // Draw the check
326 if(check!=false){
327 FXSegment seg[6];
328 seg[0].x1=2+xx; seg[0].y1=4+yy; seg[0].x2=4+xx; seg[0].y2=6+yy;
329 seg[1].x1=2+xx; seg[1].y1=5+yy; seg[1].x2=4+xx; seg[1].y2=7+yy;
330 seg[2].x1=2+xx; seg[2].y1=6+yy; seg[2].x2=4+xx; seg[2].y2=8+yy;
331 seg[3].x1=4+xx; seg[3].y1=6+yy; seg[3].x2=8+xx; seg[3].y2=2+yy;
332 seg[4].x1=4+xx; seg[4].y1=7+yy; seg[4].x2=8+xx; seg[4].y2=3+yy;
333 seg[5].x1=4+xx; seg[5].y1=8+yy; seg[5].x2=8+xx; seg[5].y2=4+yy;
334 if(isEnabled()){
335 if(check==maybe)
336 dc.setForeground(shadowColor);
337 else
338 dc.setForeground(textColor);
339 }
340 else{
341 dc.setForeground(shadowColor);
342 }
343 dc.drawLineSegments(seg,6);
344 }
345
346 return 1;
347 }
348
349
350 // Set box color
setBoxColor(FXColor clr)351 void FXMenuCheck::setBoxColor(FXColor clr){
352 if(clr!=boxColor){
353 boxColor=clr;
354 update();
355 }
356 }
357
358
359 // Save object to stream
save(FXStream & store) const360 void FXMenuCheck::save(FXStream& store) const {
361 FXMenuCommand::save(store);
362 store << check;
363 store << boxColor;
364 }
365
366
367 // Load object from stream
load(FXStream & store)368 void FXMenuCheck::load(FXStream& store){
369 FXMenuCommand::load(store);
370 store >> check;
371 store >> boxColor;
372 }
373
374
375 }
376