Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2006-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 MFXComboBoxIcon.cpp
15 : /// @author Jakob Erdmann
16 : /// @author Pablo Alvarez Lopez
17 : /// @date 2018-12-19
18 : ///
19 : //
20 : /****************************************************************************/
21 :
22 : #include <utils/common/MsgHandler.h>
23 : #include <utils/gui/div/GUIDesigns.h>
24 : #include <utils/gui/images/GUIIconSubSys.h>
25 : #include <utils/gui/windows/GUIAppEnum.h>
26 :
27 : #include "MFXComboBoxIcon.h"
28 : #include "MFXListIcon.h"
29 : #include "MFXListIconItem.h"
30 : #include "MFXTextFieldSearch.h"
31 :
32 : // ===========================================================================
33 : // FOX callback mapping
34 : // ===========================================================================
35 :
36 : FXDEFMAP(MFXComboBoxIcon) MFXComboBoxIconMap[] = {
37 : FXMAPFUNC(SEL_FOCUS_UP, 0, MFXComboBoxIcon::onFocusUp),
38 : FXMAPFUNC(SEL_FOCUS_DOWN, 0, MFXComboBoxIcon::onFocusDown),
39 : FXMAPFUNC(SEL_FOCUS_SELF, 0, MFXComboBoxIcon::onFocusSelf),
40 : FXMAPFUNC(SEL_UPDATE, MFXComboBoxIcon::ID_TEXT, MFXComboBoxIcon::onUpdFmText),
41 : FXMAPFUNC(SEL_CLICKED, MFXComboBoxIcon::ID_LIST, MFXComboBoxIcon::onListClicked),
42 : FXMAPFUNC(SEL_COMMAND, MFXComboBoxIcon::ID_LIST, MFXComboBoxIcon::onListClicked),
43 : FXMAPFUNC(SEL_LEFTBUTTONPRESS, MFXComboBoxIcon::ID_TEXT, MFXComboBoxIcon::onTextButton),
44 : FXMAPFUNC(SEL_MOUSEWHEEL, MFXComboBoxIcon::ID_TEXT, MFXComboBoxIcon::onMouseWheel),
45 : FXMAPFUNC(SEL_CHANGED, MFXComboBoxIcon::ID_TEXT, MFXComboBoxIcon::onTextChanged),
46 : FXMAPFUNC(SEL_COMMAND, MFXComboBoxIcon::ID_TEXT, MFXComboBoxIcon::onTextCommand),
47 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_SETVALUE, MFXComboBoxIcon::onFwdToText),
48 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_SETINTVALUE, MFXComboBoxIcon::onFwdToText),
49 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_SETREALVALUE, MFXComboBoxIcon::onFwdToText),
50 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_SETSTRINGVALUE, MFXComboBoxIcon::onFwdToText),
51 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_GETINTVALUE, MFXComboBoxIcon::onFwdToText),
52 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_GETREALVALUE, MFXComboBoxIcon::onFwdToText),
53 : FXMAPFUNC(SEL_COMMAND, FXWindow::ID_GETSTRINGVALUE, MFXComboBoxIcon::onFwdToText),
54 : FXMAPFUNC(SEL_COMMAND, MID_MTEXTFIELDSEARCH_UPDATED, MFXComboBoxIcon::onCmdFilter),
55 : };
56 :
57 : // Object implementation
58 1342797 : FXIMPLEMENT(MFXComboBoxIcon, FXPacker, MFXComboBoxIconMap, ARRAYNUMBER(MFXComboBoxIconMap))
59 :
60 : // ===========================================================================
61 : // member method definitions
62 : // ===========================================================================
63 :
64 7582 : MFXComboBoxIcon::MFXComboBoxIcon(FXComposite* p, MFXStaticToolTip* staticToolTip, const bool canSearch, const int visibleItems,
65 7582 : FXObject* tgt, FXSelector sel, FXuint opts, FXint x, FXint y, FXint w, FXint h, FXint pl, FXint pr, FXint pt, FXint pb):
66 7582 : FXPacker(p, opts, x, y, w, h, 0, 0, 0, 0, 0, 0) {
67 7582 : flags |= FLAG_ENABLED;
68 7582 : target = tgt;
69 7582 : message = sel;
70 : // create text field
71 7582 : myTextFieldIcon = new MFXTextFieldIcon(this, staticToolTip, GUIIcon::EMPTY, this, MFXComboBoxIcon::ID_TEXT, 0, 0, 0, 0, 0, pl, pr, pt, pb);
72 7582 : if (options & COMBOBOX_STATIC) {
73 7582 : myTextFieldIcon->setEditable(FALSE);
74 : }
75 : // create pane for list
76 7582 : myPane = new FXPopup(this, FRAME_LINE);
77 : // check if create search button
78 7582 : if (canSearch) {
79 0 : myTextFieldSearch = new MFXTextFieldSearch(myPane, staticToolTip, this, ID_SEARCH, FRAME_THICK | LAYOUT_FIX_WIDTH | LAYOUT_FIX_HEIGHT, 0, 0, 0, 0, 2, 2, 2, 2);
80 : // create label for empty icon
81 0 : myNoItemsLabel = new FXLabel(myPane, TL("No matches found"), nullptr, FRAME_THICK | LAYOUT_FIX_WIDTH | LAYOUT_FIX_HEIGHT, 0, 0, 0, 0, 2, 2, 2, 2);
82 0 : myNoItemsLabel->setTextColor(GUIDesignTextColorRed);
83 0 : myNoItemsLabel->hide();
84 : }
85 : // create list icon
86 7582 : myList = new MFXListIcon(myPane, this, MFXComboBoxIcon::ID_LIST, LIST_BROWSESELECT | LIST_AUTOSELECT | LAYOUT_FILL_X | LAYOUT_FILL_Y | SCROLLERS_TRACK | HSCROLLER_NEVER);
87 7582 : if (options & COMBOBOX_STATIC) {
88 7582 : myList->setScrollStyle(SCROLLERS_TRACK | HSCROLLING_OFF);
89 : }
90 7582 : myList->setNumVisible(visibleItems);
91 : // create button
92 7582 : myButton = new FXMenuButton(this, FXString::null, NULL, myPane, FRAME_RAISED | FRAME_THICK | MENUBUTTON_DOWN | MENUBUTTON_ATTACH_RIGHT, 0, 0, 0, 0, 0, 0, 0, 0);
93 7582 : myButton->setXOffset(border);
94 : myButton->setYOffset(border);
95 7582 : flags &= ~(FXuint)FLAG_UPDATE; // Never GUI update
96 :
97 7582 : }
98 :
99 :
100 15112 : MFXComboBoxIcon::~MFXComboBoxIcon() {
101 7556 : delete myPane;
102 7556 : myPane = (FXPopup*) - 1L;
103 7556 : myTextFieldIcon = (MFXTextFieldIcon*) - 1L;
104 7556 : myButton = (FXMenuButton*) - 1L;
105 7556 : if (myTextFieldSearch) {
106 0 : myTextFieldSearch = (MFXTextFieldSearch*) - 1L;
107 0 : myNoItemsLabel = (FXLabel*) - 1L;
108 : }
109 7556 : myList = (MFXListIcon*) - 1L;
110 15112 : }
111 :
112 :
113 : void
114 15163 : MFXComboBoxIcon::create() {
115 15163 : FXPacker::create();
116 15163 : myPane->create();
117 15163 : }
118 :
119 :
120 : void
121 0 : MFXComboBoxIcon::detach() {
122 0 : FXPacker::detach();
123 0 : myPane->detach();
124 0 : }
125 :
126 :
127 : void
128 0 : MFXComboBoxIcon::destroy() {
129 0 : myPane->destroy();
130 0 : FXPacker::destroy();
131 0 : }
132 :
133 :
134 : void
135 0 : MFXComboBoxIcon::enable() {
136 0 : if (!isEnabled()) {
137 0 : FXPacker::enable();
138 0 : myTextFieldIcon->enable();
139 0 : myButton->enable();
140 : }
141 0 : }
142 :
143 :
144 : void
145 0 : MFXComboBoxIcon::disable() {
146 0 : if (isEnabled()) {
147 0 : FXPacker::disable();
148 0 : myTextFieldIcon->disable();
149 0 : myButton->disable();
150 : }
151 0 : }
152 :
153 :
154 : FXint
155 14936 : MFXComboBoxIcon::getDefaultWidth() {
156 14936 : FXint ww = myTextFieldIcon->getDefaultWidth() + myButton->getDefaultWidth() + (border << 1);
157 14936 : FXint pw = myPane->getDefaultWidth();
158 14936 : return FXMAX(ww, pw);
159 : }
160 :
161 :
162 : FXint
163 0 : MFXComboBoxIcon::getDefaultHeight() {
164 : FXint th, bh;
165 0 : th = myTextFieldIcon->getDefaultHeight();
166 0 : bh = myButton->getDefaultHeight();
167 0 : return FXMAX(th, bh) + (border << 1);
168 : }
169 :
170 :
171 : void
172 7465 : MFXComboBoxIcon::layout() {
173 7465 : const FXint itemHeight = height - (border << 1);
174 7465 : const FXint buttonWidth = myButton->getDefaultWidth();
175 7465 : const FXint textWidth = width - buttonWidth - (border << 1);
176 7465 : myTextFieldIcon->position(border, border, textWidth, itemHeight);
177 7465 : myButton->position(border + textWidth, border, buttonWidth, itemHeight);
178 7465 : if (myTextFieldSearch) {
179 0 : myTextFieldSearch->resize(width, height);
180 0 : myNoItemsLabel->resize(width, height);
181 : }
182 7465 : myPane->resize(width, myPane->getDefaultHeight());
183 7465 : flags &= ~(FXuint)FLAG_DIRTY;
184 7465 : }
185 :
186 :
187 : FXString
188 0 : MFXComboBoxIcon::getText() const {
189 0 : return myTextFieldIcon->getText();
190 : }
191 :
192 :
193 : FXint
194 45528 : MFXComboBoxIcon::getNumItems() const {
195 45528 : return myList->getNumItems();
196 : }
197 :
198 :
199 : void
200 0 : MFXComboBoxIcon::setNumVisible(FXint nvis) {
201 0 : myList->setNumVisible(nvis);
202 0 : }
203 :
204 :
205 : void
206 0 : MFXComboBoxIcon::setText(const FXString& text, FXbool notify) {
207 0 : myTextFieldIcon->setText(text, notify);
208 0 : }
209 :
210 :
211 : FXbool
212 37946 : MFXComboBoxIcon::isItemCurrent(FXint index) const {
213 37946 : return myList->isItemCurrent(index);
214 : }
215 :
216 :
217 : long
218 7619 : MFXComboBoxIcon::setCurrentItem(const FXint index, FXbool notify) {
219 7619 : if (index >= 0 && index <= myList->getNumItems()) {
220 : // get item
221 7619 : MFXListIconItem* item = myList->getItem(index);
222 : // set it as current item and make it visible
223 7619 : myList->setCurrentItem(item);
224 7619 : myList->makeItemVisible(index);
225 : // update both text fields
226 7619 : myTextFieldIcon->setText(item->getText());
227 7619 : myTextFieldIcon->setIcon(item->getIcon());
228 7619 : myTextFieldIcon->setBackColor(item->getBackGroundColor());
229 : // check if notify
230 7619 : if (notify && target) {
231 0 : return target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)item);
232 : }
233 : } else {
234 0 : fxerror("%s::setItem: index out of range.\n", getClassName());
235 : }
236 : return 0;
237 : }
238 :
239 :
240 : long
241 0 : MFXComboBoxIcon::setCurrentItem(const FXString& text, FXbool notify) {
242 : // check if item exist
243 0 : for (int i = 0; i < myList->getNumItems(); i++) {
244 0 : const auto itemText = myList->tolowerString(myList->getItem(i)->getText());
245 0 : if (myList->tolowerString(text) == itemText) {
246 : // use "set curent item" function
247 0 : return setCurrentItem(i, notify);
248 : }
249 0 : }
250 : return 0;
251 : }
252 :
253 :
254 : FXint
255 0 : MFXComboBoxIcon::getCurrentItem() const {
256 0 : return myList->getCurrentItemIndex();
257 : }
258 :
259 :
260 : FXint
261 0 : MFXComboBoxIcon::updateIconItem(FXint index, const FXString& text, FXIcon* icon, FXColor bgColor, void* ptr) {
262 0 : if (index < 0 || myList->getNumItems() <= index) {
263 0 : fxerror("%s::setItem: index out of range.\n", getClassName());
264 : }
265 0 : myList->editItem(index, text, NULL, ptr);
266 0 : if (isItemCurrent(index)) {
267 0 : myTextFieldIcon->setText(text);
268 0 : myTextFieldIcon->setBackColor(bgColor);
269 0 : myTextFieldIcon->setIcon(icon);
270 : }
271 0 : recalc();
272 0 : return index;
273 : }
274 :
275 :
276 : FXint
277 0 : MFXComboBoxIcon::insertIconItem(FXint index, const FXString& text, FXIcon* icon, FXColor bgColor, void* ptr) {
278 0 : index = myList->insertItem(index, new MFXListIconItem(text, icon, bgColor, ptr));
279 0 : if (isItemCurrent(index)) {
280 0 : myTextFieldIcon->setText(text);
281 0 : myTextFieldIcon->setBackColor(bgColor);
282 0 : myTextFieldIcon->setIcon(icon);
283 : }
284 0 : recalc();
285 0 : return index;
286 : }
287 :
288 :
289 : FXint
290 37946 : MFXComboBoxIcon::appendIconItem(const FXString& text, FXIcon* icon, FXColor bgColor, void* ptr) {
291 37946 : FXint index = myList->appendItem(new MFXListIconItem(text, icon, bgColor, ptr));
292 37946 : if (isItemCurrent(getNumItems() - 1)) {
293 7582 : myTextFieldIcon->setText(text);
294 7582 : myTextFieldIcon->setBackColor(bgColor);
295 7582 : myTextFieldIcon->setIcon(icon);
296 : }
297 37946 : recalc();
298 37946 : return index;
299 : }
300 :
301 :
302 : void
303 0 : MFXComboBoxIcon::removeItem(FXint index) {
304 0 : const auto isCurrent = myList->isItemCurrent(index);
305 0 : if (isCurrent == TRUE) {
306 0 : if ((index > 0) && (index < (int)myList->getNumItems())) {
307 0 : setCurrentItem(index - 1);
308 0 : } else if (myList->getNumItems() > 0) {
309 0 : setCurrentItem(0);
310 : }
311 : }
312 0 : myList->removeItem(index);
313 0 : recalc();
314 0 : }
315 :
316 :
317 : void
318 0 : MFXComboBoxIcon::clearItems() {
319 0 : myTextFieldIcon->setText("");
320 0 : myList->clearItems();
321 0 : recalc();
322 0 : }
323 :
324 :
325 : FXint
326 40 : MFXComboBoxIcon::findItem(const FXString& text) const {
327 40 : return myList->findItem(text);
328 : }
329 :
330 :
331 : std::string
332 0 : MFXComboBoxIcon::getItemText(FXint index) const {
333 0 : return myList->getItem(index)->getText().text();
334 : }
335 :
336 :
337 : void
338 0 : MFXComboBoxIcon::setBackColor(FXColor clr) {
339 0 : myTextFieldIcon->setBackColor(clr);
340 0 : myList->setBackColor(clr);
341 0 : }
342 :
343 :
344 : void
345 0 : MFXComboBoxIcon::setTextColor(FXColor clr) {
346 0 : myTextFieldIcon->setTextColor(clr);
347 0 : myList->setTextColor(clr);
348 0 : }
349 :
350 :
351 : FXColor
352 0 : MFXComboBoxIcon::getTextColor() const {
353 0 : return myTextFieldIcon->getTextColor();
354 : }
355 :
356 :
357 : void
358 0 : MFXComboBoxIcon::setTipText(const FXString& txt) {
359 0 : myTextFieldIcon->setTipText(txt);
360 0 : }
361 :
362 :
363 : const FXString&
364 0 : MFXComboBoxIcon::getTipText() const {
365 0 : return myTextFieldIcon->getTipText();
366 : }
367 :
368 :
369 : long
370 427668 : MFXComboBoxIcon::onUpdFmText(FXObject*, FXSelector, void*) {
371 427668 : return (target && !myPane->shown()) ? target->tryHandle(this, FXSEL(SEL_UPDATE, message), NULL) : 0;
372 : }
373 :
374 :
375 : long
376 0 : MFXComboBoxIcon::onCmdFilter(FXObject*, FXSelector, void* ptr) {
377 0 : myList->setFilter(myTextFieldSearch->getText(), myNoItemsLabel);
378 0 : myPane->resize(width, myPane->getDefaultHeight());
379 0 : myPane->recalc();
380 0 : myPane->onPaint(0, 0, ptr);
381 0 : return 1;
382 : }
383 :
384 :
385 : long
386 0 : MFXComboBoxIcon::onFwdToText(FXObject* sender, FXSelector sel, void* ptr) {
387 0 : return myTextFieldIcon->handle(sender, sel, ptr);
388 : }
389 :
390 :
391 : long
392 0 : MFXComboBoxIcon::onListClicked(FXObject*, FXSelector sel, void* ptr) {
393 : // hide pane
394 0 : myButton->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
395 0 : if (FXSELTYPE(sel) == SEL_COMMAND) {
396 : // cast MFXListIconItem
397 : const MFXListIconItem* item = (MFXListIconItem*)ptr;
398 : // set icon and background color
399 0 : myTextFieldIcon->setText(item->getText());
400 0 : myTextFieldIcon->setIcon(item->getIcon());
401 0 : myTextFieldIcon->setBackColor(item->getBackGroundColor());
402 : // Select if editable
403 0 : if (!(options & COMBOBOX_STATIC)) {
404 0 : myTextFieldIcon->selectAll();
405 : }
406 0 : if (target) {
407 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)getText().text());
408 : }
409 : }
410 0 : return 1;
411 : }
412 :
413 :
414 : long
415 0 : MFXComboBoxIcon::onTextButton(FXObject*, FXSelector, void*) {
416 0 : if (options & COMBOBOX_STATIC) {
417 : // Post the myList
418 0 : myButton->handle(this, FXSEL(SEL_COMMAND, ID_POST), NULL);
419 0 : return 1;
420 : }
421 : return 0;
422 : }
423 :
424 :
425 : long
426 0 : MFXComboBoxIcon::onTextChanged(FXObject*, FXSelector, void* ptr) {
427 0 : return target ? target->tryHandle(this, FXSEL(SEL_CHANGED, message), ptr) : 0;
428 : }
429 :
430 :
431 : long
432 0 : MFXComboBoxIcon::onTextCommand(FXObject*, FXSelector, void* ptr) {
433 : // reset background colors
434 0 : myTextFieldIcon->setBackColor(GUIDesignBackgroundColorWhite);
435 : // check if item exist
436 0 : for (int i = 0; i < myList->getNumItems(); i++) {
437 0 : const auto itemText = myList->tolowerString(myList->getItem(i)->getText());
438 0 : if (myList->tolowerString(myTextFieldIcon->getText()) == itemText) {
439 : // use "set curent item" function
440 0 : return setCurrentItem(i, TRUE);
441 : }
442 0 : }
443 : // no item found, then reset icon label
444 0 : myTextFieldIcon->setIcon(GUIIconSubSys::getIcon(GUIIcon::CANCEL));
445 0 : return target ? target->tryHandle(this, FXSEL(SEL_COMMAND, message), ptr) : 0;
446 : }
447 :
448 :
449 : long
450 0 : MFXComboBoxIcon::onFocusSelf(FXObject* sender, FXSelector, void* ptr) {
451 0 : return myTextFieldIcon->handle(sender, FXSEL(SEL_FOCUS_SELF, 0), ptr);
452 : }
453 :
454 :
455 : long
456 0 : MFXComboBoxIcon::onFocusUp(FXObject*, FXSelector, void*) {
457 0 : if (isEnabled()) {
458 0 : FXint index = getCurrentItem();
459 0 : if (index < 0) {
460 0 : index = getNumItems() - 1;
461 0 : } else if (0 < index) {
462 0 : index--;
463 : }
464 0 : if (0 <= index && index < getNumItems()) {
465 0 : setCurrentItem(index, TRUE);
466 : }
467 0 : return 1;
468 : }
469 : return 0;
470 : }
471 :
472 :
473 : long
474 0 : MFXComboBoxIcon::onFocusDown(FXObject*, FXSelector, void*) {
475 0 : if (isEnabled()) {
476 0 : FXint index = getCurrentItem();
477 0 : if (index < 0) {
478 : index = 0;
479 0 : } else if (index < getNumItems() - 1) {
480 0 : index++;
481 : }
482 0 : if (0 <= index && index < getNumItems()) {
483 0 : setCurrentItem(index, TRUE);
484 : }
485 0 : return 1;
486 : }
487 : return 0;
488 : }
489 :
490 :
491 0 : long MFXComboBoxIcon::onMouseWheel(FXObject*, FXSelector, void* ptr) {
492 : FXEvent* event = (FXEvent*)ptr;
493 0 : if (isEnabled()) {
494 0 : FXint index = getCurrentItem();
495 0 : if (event->code < 0) {
496 0 : if (index < 0) {
497 : index = 0;
498 0 : } else if (index < getNumItems() - 1) {
499 0 : index++;
500 : }
501 0 : } else if (event->code > 0) {
502 0 : if (index < 0) {
503 0 : index = getNumItems() - 1;
504 0 : } else if (0 < index) {
505 0 : index--;
506 : }
507 : }
508 0 : if (0 <= index && index < getNumItems()) {
509 0 : setCurrentItem(index, TRUE);
510 : }
511 0 : return 1;
512 : }
513 : return 0;
514 : }
515 :
516 :
517 0 : MFXComboBoxIcon::MFXComboBoxIcon() {}
|