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