Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2004-2024 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 : FXbool
111 0 : MFXMenuCheckIcon::getCheck() const {
112 0 : return myCheck;
113 : }
114 :
115 :
116 : FXColor
117 0 : MFXMenuCheckIcon::getBoxColor() const {
118 0 : return myBoxColor;
119 : }
120 :
121 :
122 : long
123 0 : MFXMenuCheckIcon::onCheck(FXObject*, FXSelector, void*) {
124 0 : setCheck(TRUE);
125 0 : return 1;
126 : }
127 :
128 :
129 : long
130 0 : MFXMenuCheckIcon::onUncheck(FXObject*, FXSelector, void*) {
131 0 : setCheck(FALSE);
132 0 : return 1;
133 : }
134 :
135 :
136 : long
137 0 : MFXMenuCheckIcon::onUnknown(FXObject*, FXSelector, void*) {
138 0 : setCheck(MAYBE);
139 0 : return 1;
140 : }
141 :
142 :
143 : long
144 0 : MFXMenuCheckIcon::onCmdSetValue(FXObject*, FXSelector, void* ptr) {
145 0 : setCheck((FXbool)(FXuval)ptr);
146 0 : return 1;
147 : }
148 :
149 :
150 : long
151 0 : MFXMenuCheckIcon::onCmdSetIntValue(FXObject*, FXSelector, void* ptr) {
152 0 : setCheck((FXbool) * ((FXint*)ptr));
153 0 : return 1;
154 : }
155 :
156 :
157 : long
158 0 : MFXMenuCheckIcon::onCmdGetIntValue(FXObject*, FXSelector, void* ptr) {
159 0 : *((FXint*)ptr) = getCheck();
160 0 : return 1;
161 : }
162 :
163 :
164 : long
165 0 : MFXMenuCheckIcon::onButtonPress(FXObject*, FXSelector, void*) {
166 0 : if (!isEnabled()) {
167 0 : return 0;
168 : }
169 : return 1;
170 : }
171 :
172 :
173 : long
174 0 : MFXMenuCheckIcon::onButtonRelease(FXObject*, FXSelector, void*) {
175 0 : FXbool active = isActive();
176 0 : if (!isEnabled()) {
177 : return 0;
178 : }
179 : // keep menu open
180 : //getParent()->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
181 0 : if (active) {
182 0 : setCheck(!myCheck);
183 0 : if (target) {
184 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
185 : }
186 : }
187 : return 1;
188 : }
189 :
190 :
191 : long
192 0 : MFXMenuCheckIcon::onKeyPress(FXObject*, FXSelector, void* ptr) {
193 : FXEvent* event = (FXEvent*)ptr;
194 0 : if (isEnabled() && !(flags & FLAG_PRESSED)) {
195 : FXTRACE((200, "%s::onKeyPress %p keysym = 0x%04x state = %04x\n", getClassName(), (void*)this, event->code, event->state));
196 0 : if (event->code == FX::KEY_space || event->code == FX::KEY_KP_Space || event->code == FX::KEY_Return || event->code == FX::KEY_KP_Enter) {
197 0 : flags |= FLAG_PRESSED;
198 0 : return 1;
199 : }
200 : }
201 : return 0;
202 : }
203 :
204 :
205 : long
206 0 : MFXMenuCheckIcon::onKeyRelease(FXObject*, FXSelector, void* ptr) {
207 : FXEvent* event = (FXEvent*)ptr;
208 0 : if (isEnabled() && (flags & FLAG_PRESSED)) {
209 : FXTRACE((200, "%s::onKeyRelease %p keysym = 0x%04x state = %04x\n", getClassName(), (void*)this, event->code, event->state));
210 0 : if (event->code == FX::KEY_space || event->code == FX::KEY_KP_Space || event->code == FX::KEY_Return || event->code == FX::KEY_KP_Enter) {
211 0 : flags &= ~FLAG_PRESSED;
212 0 : setCheck(!myCheck);
213 0 : getParent()->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
214 0 : if (target) {
215 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
216 : }
217 0 : return 1;
218 : }
219 : }
220 : return 0;
221 : }
222 :
223 :
224 : long
225 0 : MFXMenuCheckIcon::onHotKeyPress(FXObject*, FXSelector, void* ptr) {
226 : FXTRACE((200, "%s::onHotKeyPress %p\n", getClassName(), (void*)this));
227 0 : handle(this, FXSEL(SEL_FOCUS_SELF, 0), ptr);
228 0 : if (isEnabled() && !(flags & FLAG_PRESSED)) {
229 0 : flags |= FLAG_PRESSED;
230 : }
231 0 : return 1;
232 : }
233 :
234 :
235 : long
236 0 : MFXMenuCheckIcon::onHotKeyRelease(FXObject*, FXSelector, void*) {
237 : FXTRACE((200, "%s::onHotKeyRelease %p\n", getClassName(), (void*)this));
238 0 : if (isEnabled() && (flags & FLAG_PRESSED)) {
239 0 : flags &= ~FLAG_PRESSED;
240 0 : setCheck(!myCheck);
241 0 : getParent()->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
242 0 : if (target) {
243 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
244 : }
245 : }
246 0 : return 1;
247 : }
248 :
249 :
250 : long
251 0 : MFXMenuCheckIcon::onCmdAccel(FXObject*, FXSelector, void*) {
252 0 : if (isEnabled()) {
253 0 : setCheck(!myCheck);
254 0 : if (target) {
255 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)(FXuval)myCheck);
256 : }
257 0 : return 1;
258 : }
259 : return 0;
260 : }
261 :
262 :
263 : long
264 0 : MFXMenuCheckIcon::onPaint(FXObject*, FXSelector, void* ptr) {
265 : FXEvent* ev = (FXEvent*)ptr;
266 0 : FXDCWindow dc(this, ev);
267 : FXint xx, yy;
268 : // set xx depending of myIcon
269 0 : if (myIcon) {
270 0 : xx = LEADSPACE + myIcon->getWidth() + 5;
271 : } else {
272 : xx = LEADSPACE;
273 : }
274 : // begin draw
275 0 : if (!isEnabled()) {
276 : // Grayed out
277 0 : dc.setForeground(backColor);
278 0 : dc.fillRectangle(0, 0, width, height);
279 0 : if (!label.empty()) {
280 0 : yy = font->getFontAscent() + (height - font->getFontHeight()) / 2;
281 0 : dc.setFont(font);
282 0 : dc.setForeground(hiliteColor);
283 0 : dc.drawText(xx + 1, yy + 1, label);
284 0 : if (!accel.empty()) {
285 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel) + 1, yy + 1, accel);
286 : }
287 0 : if (0 <= hotoff) {
288 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff) + 1, yy + 2, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
289 : }
290 0 : dc.setForeground(shadowColor);
291 0 : dc.drawText(xx, yy, label);
292 0 : if (!accel.empty()) {
293 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel), yy, accel);
294 : }
295 0 : if (0 <= hotoff) {
296 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff), yy + 1, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
297 : }
298 : }
299 0 : } else if (isActive()) {
300 : // Active
301 0 : dc.setForeground(selbackColor);
302 0 : dc.fillRectangle(0, 0, width, height);
303 0 : if (!label.empty()) {
304 0 : yy = font->getFontAscent() + (height - font->getFontHeight()) / 2;
305 0 : dc.setFont(font);
306 0 : dc.setForeground(isEnabled() ? seltextColor : shadowColor);
307 0 : dc.drawText(xx, yy, label);
308 0 : if (!accel.empty()) {
309 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel), yy, accel);
310 : }
311 0 : if (0 <= hotoff) {
312 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff), yy + 1, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
313 : }
314 : }
315 : } else {
316 : // Normal
317 0 : dc.setForeground(backColor);
318 0 : dc.fillRectangle(0, 0, width, height);
319 0 : if (!label.empty()) {
320 0 : yy = font->getFontAscent() + (height - font->getFontHeight()) / 2;
321 0 : dc.setFont(font);
322 0 : dc.setForeground(textColor);
323 0 : dc.drawText(xx, yy, label);
324 0 : if (!accel.empty()) {
325 0 : dc.drawText(width - TRAILSPACE - font->getTextWidth(accel), yy, accel);
326 : }
327 0 : if (0 <= hotoff) {
328 0 : dc.fillRectangle(xx + font->getTextWidth(&label[0], hotoff), yy + 1, font->getTextWidth(&label[hotoff], wclen(&label[hotoff])), 1);
329 : }
330 : }
331 : }
332 : // Draw the box
333 : xx = 5;
334 0 : yy = (height - 9) / 2;
335 0 : if (!isEnabled()) {
336 0 : dc.setForeground(backColor);
337 : } else {
338 0 : dc.setForeground(myBoxColor);
339 0 : dc.fillRectangle(xx + 1, yy + 1, 8, 8);
340 0 : dc.setForeground(shadowColor);
341 0 : dc.drawRectangle(xx, yy, 9, 9);
342 : }
343 : // Draw the check (tick)
344 0 : if (myCheck != FALSE) {
345 : FXSegment seg[6];
346 0 : seg[0].x1 = 2 + (FXshort)xx;
347 0 : seg[0].y1 = 4 + (FXshort)yy;
348 0 : seg[0].x2 = 4 + (FXshort)xx;
349 0 : seg[0].y2 = 6 + (FXshort)yy;
350 0 : seg[1].x1 = 2 + (FXshort)xx;
351 0 : seg[1].y1 = 5 + (FXshort)yy;
352 0 : seg[1].x2 = 4 + (FXshort)xx;
353 0 : seg[1].y2 = 7 + (FXshort)yy;
354 0 : seg[2].x1 = 2 + (FXshort)xx;
355 0 : seg[2].y1 = 6 + (FXshort)yy;
356 0 : seg[2].x2 = 4 + (FXshort)xx;
357 0 : seg[2].y2 = 8 + (FXshort)yy;
358 0 : seg[3].x1 = 4 + (FXshort)xx;
359 0 : seg[3].y1 = 6 + (FXshort)yy;
360 0 : seg[3].x2 = 8 + (FXshort)xx;
361 0 : seg[3].y2 = 2 + (FXshort)yy;
362 0 : seg[4].x1 = 4 + (FXshort)xx;
363 0 : seg[4].y1 = 7 + (FXshort)yy;
364 0 : seg[4].x2 = 8 + (FXshort)xx;
365 0 : seg[4].y2 = 3 + (FXshort)yy;
366 0 : seg[5].x1 = 4 + (FXshort)xx;
367 0 : seg[5].y1 = 8 + (FXshort)yy;
368 0 : seg[5].x2 = 8 + (FXshort)xx;
369 0 : seg[5].y2 = 4 + (FXshort)yy;
370 0 : if (isEnabled()) {
371 0 : if (myCheck == MAYBE) {
372 0 : dc.setForeground(shadowColor);
373 : } else {
374 0 : dc.setForeground(textColor);
375 : }
376 : } else {
377 0 : dc.setForeground(shadowColor);
378 : }
379 0 : dc.drawLineSegments(seg, 6);
380 : }
381 : // draw icon
382 0 : if (myIcon) {
383 0 : if (isEnabled()) {
384 0 : dc.drawIcon(myIcon, LEADSPACE, (height - myIcon->getHeight()) / 2);
385 : xx += 5 + myIcon->getWidth();
386 : } else {
387 0 : dc.drawIconSunken(myIcon, LEADSPACE, (height - myIcon->getHeight()) / 2);
388 : xx += 5 + myIcon->getWidth();
389 : }
390 : }
391 0 : return 1;
392 0 : }
393 :
394 :
395 : void
396 0 : MFXMenuCheckIcon::setBoxColor(FXColor clr) {
397 0 : if (clr != myBoxColor) {
398 0 : myBoxColor = clr;
399 0 : update();
400 : }
401 0 : }
402 :
403 :
404 : void
405 0 : MFXMenuCheckIcon::save(FXStream& store) const {
406 0 : FXMenuCommand::save(store);
407 0 : store << myCheck;
408 0 : store << myBoxColor;
409 0 : }
410 :
411 :
412 0 : void MFXMenuCheckIcon::load(FXStream& store) {
413 0 : FXMenuCommand::load(store);
414 0 : store >> myCheck;
415 0 : store >> myBoxColor;
416 0 : }
417 :
418 :
419 0 : MFXMenuCheckIcon::MFXMenuCheckIcon() :
420 0 : myIcon(nullptr),
421 0 : myCheck(FALSE),
422 0 : myBoxColor(0) {
423 0 : }
|