Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2004-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MFXMenuCheckIcon.cpp
15 : /// @author Pablo Alvarez Lopez
16 : /// @date Jan 2021
17 : ///
18 : //
19 : /****************************************************************************/
20 :
21 : #include <fxkeys.h>
22 :
23 : #include "MFXMenuCheckIcon.h"
24 :
25 :
26 : #define LEADSPACE 22
27 : #define TRAILSPACE 16
28 :
29 : // ===========================================================================
30 : // FOX callback mapping
31 : // ===========================================================================
32 :
33 : FXDEFMAP(MFXMenuCheckIcon) MFXMenuCheckIconMap[] = {
34 : FXMAPFUNC(SEL_PAINT, 0, MFXMenuCheckIcon::onPaint),
35 : FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, MFXMenuCheckIcon::onButtonPress),
36 : FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, MFXMenuCheckIcon::onButtonRelease),
37 : FXMAPFUNC(SEL_MIDDLEBUTTONPRESS, 0, MFXMenuCheckIcon::onButtonPress),
38 : FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE, 0, MFXMenuCheckIcon::onButtonRelease),
39 : FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, MFXMenuCheckIcon::onButtonPress),
40 : FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, MFXMenuCheckIcon::onButtonRelease),
41 : FXMAPFUNC(SEL_KEYPRESS, 0, MFXMenuCheckIcon::onKeyPress),
42 : FXMAPFUNC(SEL_KEYRELEASE, 0, MFXMenuCheckIcon::onKeyRelease),
43 : FXMAPFUNC(SEL_KEYPRESS, FXWindow::ID_HOTKEY, MFXMenuCheckIcon::onHotKeyPress),
44 : FXMAPFUNC(SEL_KEYRELEASE, FXWindow::ID_HOTKEY, MFXMenuCheckIcon::onHotKeyRelease),
45 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_CHECK, MFXMenuCheckIcon::onCheck),
46 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_UNCHECK, MFXMenuCheckIcon::onUncheck),
47 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_UNKNOWN, MFXMenuCheckIcon::onUnknown),
48 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_SETVALUE, MFXMenuCheckIcon::onCmdSetValue),
49 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_SETINTVALUE, MFXMenuCheckIcon::onCmdSetIntValue),
50 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_GETINTVALUE, MFXMenuCheckIcon::onCmdGetIntValue),
51 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_ACCEL, MFXMenuCheckIcon::onCmdAccel),
52 : };
53 :
54 : // Object implementation
55 0 : FXIMPLEMENT(MFXMenuCheckIcon, FXMenuCommand, MFXMenuCheckIconMap, ARRAYNUMBER(MFXMenuCheckIconMap))
56 :
57 : // ===========================================================================
58 : // member method definitions
59 : // ===========================================================================
60 :
61 0 : MFXMenuCheckIcon::MFXMenuCheckIcon(FXComposite* p, const std::string& text, const std::string& shortcut, const std::string& info, const FXIcon* icon, FXObject* tgt, FXSelector sel, FXuint opts) :
62 0 : FXMenuCommand(p, (text + "\t" + shortcut + "\t" + info).c_str(), NULL, tgt, sel, opts),
63 0 : myIcon(icon),
64 0 : myCheck(FALSE),
65 0 : myBoxColor(getApp()->getBackColor()) {
66 0 : }
67 :
68 :
69 : FXint
70 0 : MFXMenuCheckIcon::getDefaultWidth() {
71 : FXint tw, aw;
72 : tw = aw = 0;
73 0 : if (!label.empty()) {
74 0 : tw = font->getTextWidth(label.text(), label.length());
75 : }
76 0 : if (!accel.empty()) {
77 0 : aw = font->getTextWidth(accel.text(), accel.length());
78 : }
79 0 : if (aw && tw) {
80 0 : aw += 5;
81 : }
82 : // return width depending of icon
83 0 : if (myIcon) {
84 0 : return LEADSPACE + (myIcon->getWidth() + 5) + tw + aw + TRAILSPACE;
85 : } else {
86 0 : return LEADSPACE + tw + aw + TRAILSPACE;
87 : }
88 : }
89 :
90 :
91 : FXint
92 0 : MFXMenuCheckIcon::getDefaultHeight() {
93 : FXint th = 0;
94 0 : if (!label.empty() || !accel.empty()) {
95 0 : th = font->getFontHeight() + 5;
96 : }
97 0 : return FXMAX(th, 20);
98 : }
99 :
100 :
101 : void
102 0 : MFXMenuCheckIcon::setCheck(FXbool s) {
103 0 : if (myCheck != s) {
104 0 : myCheck = s;
105 0 : update();
106 : }
107 0 : }
108 :
109 :
110 : void
111 0 : MFXMenuCheckIcon::toggleCheck() {
112 0 : if (myCheck == TRUE) {
113 0 : setCheck(FALSE);
114 0 : } else if (myCheck == FALSE) {
115 0 : setCheck(TRUE);
116 : } else {
117 0 : setCheck(MAYBE);
118 : }
119 0 : }
120 :
121 :
122 : FXbool
123 0 : MFXMenuCheckIcon::getCheck() const {
124 0 : return myCheck;
125 : }
126 :
127 :
128 : FXColor
129 0 : MFXMenuCheckIcon::getBoxColor() const {
130 0 : return myBoxColor;
131 : }
132 :
133 :
134 : long
135 0 : MFXMenuCheckIcon::onCheck(FXObject*, FXSelector, void*) {
136 0 : setCheck(TRUE);
137 0 : return 1;
138 : }
139 :
140 :
141 : long
142 0 : MFXMenuCheckIcon::onUncheck(FXObject*, FXSelector, void*) {
143 0 : setCheck(FALSE);
144 0 : return 1;
145 : }
146 :
147 :
148 : long
149 0 : MFXMenuCheckIcon::onUnknown(FXObject*, FXSelector, void*) {
150 0 : setCheck(MAYBE);
151 0 : return 1;
152 : }
153 :
154 :
155 : long
156 0 : MFXMenuCheckIcon::onCmdSetValue(FXObject*, FXSelector, void* ptr) {
157 0 : setCheck((FXbool)(FXuval)ptr);
158 0 : return 1;
159 : }
160 :
161 :
162 : long
163 0 : MFXMenuCheckIcon::onCmdSetIntValue(FXObject*, FXSelector, void* ptr) {
164 0 : setCheck((FXbool) * ((FXint*)ptr));
165 0 : return 1;
166 : }
167 :
168 :
169 : long
170 0 : MFXMenuCheckIcon::onCmdGetIntValue(FXObject*, FXSelector, void* ptr) {
171 0 : *((FXint*)ptr) = getCheck();
172 0 : return 1;
173 : }
174 :
175 :
176 : long
177 0 : MFXMenuCheckIcon::onButtonPress(FXObject*, FXSelector, void*) {
178 0 : if (!isEnabled()) {
179 0 : return 0;
180 : }
181 : return 1;
182 : }
183 :
184 :
185 : long
186 0 : MFXMenuCheckIcon::onButtonRelease(FXObject*, FXSelector, void*) {
187 0 : FXbool active = isActive();
188 0 : if (!isEnabled()) {
189 : return 0;
190 : }
191 : // keep menu open
192 : //getParent()->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
193 0 : if (active) {
194 0 : setCheck(!myCheck);
195 0 : if (target) {
196 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
197 : }
198 : }
199 : return 1;
200 : }
201 :
202 :
203 : long
204 0 : MFXMenuCheckIcon::onKeyPress(FXObject*, FXSelector, void* ptr) {
205 : FXEvent* event = (FXEvent*)ptr;
206 0 : if (isEnabled() && !(flags & FLAG_PRESSED)) {
207 : FXTRACE((200, "%s::onKeyPress %p keysym = 0x%04x state = %04x\n", getClassName(), (void*)this, event->code, event->state));
208 0 : if (event->code == FX::KEY_space || event->code == FX::KEY_KP_Space || event->code == FX::KEY_Return || event->code == FX::KEY_KP_Enter) {
209 0 : flags |= FLAG_PRESSED;
210 0 : return 1;
211 : }
212 : }
213 : return 0;
214 : }
215 :
216 :
217 : long
218 0 : MFXMenuCheckIcon::onKeyRelease(FXObject*, FXSelector, void* ptr) {
219 : FXEvent* event = (FXEvent*)ptr;
220 0 : if (isEnabled() && (flags & FLAG_PRESSED)) {
221 : FXTRACE((200, "%s::onKeyRelease %p keysym = 0x%04x state = %04x\n", getClassName(), (void*)this, event->code, event->state));
222 0 : if (event->code == FX::KEY_space || event->code == FX::KEY_KP_Space || event->code == FX::KEY_Return || event->code == FX::KEY_KP_Enter) {
223 0 : flags &= ~FLAG_PRESSED;
224 0 : setCheck(!myCheck);
225 0 : getParent()->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
226 0 : if (target) {
227 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
228 : }
229 0 : return 1;
230 : }
231 : }
232 : return 0;
233 : }
234 :
235 :
236 : long
237 0 : MFXMenuCheckIcon::onHotKeyPress(FXObject*, FXSelector, void* ptr) {
238 : FXTRACE((200, "%s::onHotKeyPress %p\n", getClassName(), (void*)this));
239 0 : handle(this, FXSEL(SEL_FOCUS_SELF, 0), ptr);
240 0 : if (isEnabled() && !(flags & FLAG_PRESSED)) {
241 0 : flags |= FLAG_PRESSED;
242 : }
243 0 : return 1;
244 : }
245 :
246 :
247 : long
248 0 : MFXMenuCheckIcon::onHotKeyRelease(FXObject*, FXSelector, void*) {
249 : FXTRACE((200, "%s::onHotKeyRelease %p\n", getClassName(), (void*)this));
250 0 : if (isEnabled() && (flags & FLAG_PRESSED)) {
251 0 : flags &= ~FLAG_PRESSED;
252 0 : setCheck(!myCheck);
253 0 : getParent()->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
254 0 : if (target) {
255 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
256 : }
257 : }
258 0 : return 1;
259 : }
260 :
261 :
262 : long
263 0 : MFXMenuCheckIcon::onCmdAccel(FXObject*, FXSelector, void*) {
264 0 : if (isEnabled()) {
265 0 : setCheck(!myCheck);
266 0 : if (target) {
267 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
268 : }
269 0 : return 1;
270 : }
271 : return 0;
272 : }
273 :
274 :
275 : long
276 0 : MFXMenuCheckIcon::onPaint(FXObject*, FXSelector, void* ptr) {
277 : FXEvent* ev = (FXEvent*)ptr;
278 0 : FXDCWindow dc(this, ev);
279 : FXint xx, yy;
280 : // set xx depending of myIcon
281 0 : if (myIcon) {
282 0 : xx = LEADSPACE + myIcon->getWidth() + 5;
283 : } else {
284 : xx = LEADSPACE;
285 : }
286 : // begin draw
287 0 : if (!isEnabled()) {
288 : // Grayed out
289 0 : dc.setForeground(backColor);
290 0 : dc.fillRectangle(0, 0, width, height);
291 0 : if (!label.empty()) {
292 0 : yy = font->getFontAscent() + (height - font->getFontHeight()) / 2;
293 0 : dc.setFont(font);
294 0 : dc.setForeground(hiliteColor);
295 0 : dc.drawText(xx + 1, yy + 1, label);
296 0 : if (!accel.empty()) {
297 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel) + 1, yy + 1, accel);
298 : }
299 0 : if (0 <= hotoff) {
300 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff) + 1, yy + 2, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
301 : }
302 0 : dc.setForeground(shadowColor);
303 0 : dc.drawText(xx, yy, label);
304 0 : if (!accel.empty()) {
305 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel), yy, accel);
306 : }
307 0 : if (0 <= hotoff) {
308 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff), yy + 1, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
309 : }
310 : }
311 0 : } else if (isActive()) {
312 : // Active
313 0 : dc.setForeground(selbackColor);
314 0 : dc.fillRectangle(0, 0, width, height);
315 0 : if (!label.empty()) {
316 0 : yy = font->getFontAscent() + (height - font->getFontHeight()) / 2;
317 0 : dc.setFont(font);
318 0 : dc.setForeground(isEnabled() ? seltextColor : shadowColor);
319 0 : dc.drawText(xx, yy, label);
320 0 : if (!accel.empty()) {
321 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel), yy, accel);
322 : }
323 0 : if (0 <= hotoff) {
324 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff), yy + 1, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
325 : }
326 : }
327 : } else {
328 : // Normal
329 0 : dc.setForeground(backColor);
330 0 : dc.fillRectangle(0, 0, width, height);
331 0 : if (!label.empty()) {
332 0 : yy = font->getFontAscent() + (height - font->getFontHeight()) / 2;
333 0 : dc.setFont(font);
334 0 : dc.setForeground(textColor);
335 0 : dc.drawText(xx, yy, label);
336 0 : if (!accel.empty()) {
337 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel), yy, accel);
338 : }
339 0 : if (0 <= hotoff) {
340 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff), yy + 1, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
341 : }
342 : }
343 : }
344 : // Draw the box
345 : xx = 5;
346 0 : yy = (height - 9) / 2;
347 0 : if (!isEnabled()) {
348 0 : dc.setForeground(backColor);
349 : } else {
350 0 : dc.setForeground(myBoxColor);
351 0 : dc.fillRectangle(xx + 1, yy + 1, 8, 8);
352 0 : dc.setForeground(shadowColor);
353 0 : dc.drawRectangle(xx, yy, 9, 9);
354 : }
355 : // Draw the check (tick)
356 0 : if (myCheck != FALSE) {
357 : FXSegment seg[6];
358 0 : seg[0].x1 = 2 + (FXshort)xx;
359 0 : seg[0].y1 = 4 + (FXshort)yy;
360 0 : seg[0].x2 = 4 + (FXshort)xx;
361 0 : seg[0].y2 = 6 + (FXshort)yy;
362 0 : seg[1].x1 = 2 + (FXshort)xx;
363 0 : seg[1].y1 = 5 + (FXshort)yy;
364 0 : seg[1].x2 = 4 + (FXshort)xx;
365 0 : seg[1].y2 = 7 + (FXshort)yy;
366 0 : seg[2].x1 = 2 + (FXshort)xx;
367 0 : seg[2].y1 = 6 + (FXshort)yy;
368 0 : seg[2].x2 = 4 + (FXshort)xx;
369 0 : seg[2].y2 = 8 + (FXshort)yy;
370 0 : seg[3].x1 = 4 + (FXshort)xx;
371 0 : seg[3].y1 = 6 + (FXshort)yy;
372 0 : seg[3].x2 = 8 + (FXshort)xx;
373 0 : seg[3].y2 = 2 + (FXshort)yy;
374 0 : seg[4].x1 = 4 + (FXshort)xx;
375 0 : seg[4].y1 = 7 + (FXshort)yy;
376 0 : seg[4].x2 = 8 + (FXshort)xx;
377 0 : seg[4].y2 = 3 + (FXshort)yy;
378 0 : seg[5].x1 = 4 + (FXshort)xx;
379 0 : seg[5].y1 = 8 + (FXshort)yy;
380 0 : seg[5].x2 = 8 + (FXshort)xx;
381 0 : seg[5].y2 = 4 + (FXshort)yy;
382 0 : if (isEnabled()) {
383 0 : if (myCheck == MAYBE) {
384 0 : dc.setForeground(shadowColor);
385 : } else {
386 0 : dc.setForeground(textColor);
387 : }
388 : } else {
389 0 : dc.setForeground(shadowColor);
390 : }
391 0 : dc.drawLineSegments(seg, 6);
392 : }
393 : // draw icon
394 0 : if (myIcon) {
395 0 : if (isEnabled()) {
396 0 : dc.drawIcon(myIcon, LEADSPACE, (height - myIcon->getHeight()) / 2);
397 : xx += 5 + myIcon->getWidth();
398 : } else {
399 0 : dc.drawIconSunken(myIcon, LEADSPACE, (height - myIcon->getHeight()) / 2);
400 : xx += 5 + myIcon->getWidth();
401 : }
402 : }
403 0 : return 1;
404 0 : }
405 :
406 :
407 : void
408 0 : MFXMenuCheckIcon::setBoxColor(FXColor clr) {
409 0 : if (clr != myBoxColor) {
410 0 : myBoxColor = clr;
411 0 : update();
412 : }
413 0 : }
414 :
415 :
416 : void
417 0 : MFXMenuCheckIcon::save(FXStream& store) const {
418 0 : FXMenuCommand::save(store);
419 0 : store << myCheck;
420 0 : store << myBoxColor;
421 0 : }
422 :
423 :
424 0 : void MFXMenuCheckIcon::load(FXStream& store) {
425 0 : FXMenuCommand::load(store);
426 0 : store >> myCheck;
427 0 : store >> myBoxColor;
428 0 : }
429 :
430 :
431 0 : MFXMenuCheckIcon::MFXMenuCheckIcon() :
432 0 : myIcon(nullptr),
433 0 : myCheck(FALSE),
434 0 : myBoxColor(0) {
435 0 : }
|