Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2006-2026 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 1508632 : FXIMPLEMENT(MFXComboBoxIcon, FXPacker, MFXComboBoxIconMap, ARRAYNUMBER(MFXComboBoxIconMap))
59 :
60 : // ===========================================================================
61 : // member method definitions
62 : // ===========================================================================
63 :
64 8507 : MFXComboBoxIcon::MFXComboBoxIcon(FXComposite* p, MFXStaticToolTip* staticToolTip, const bool canSearch, const int visibleItems,
65 8507 : FXObject* tgt, FXSelector sel, FXuint opts, FXint x, FXint y, FXint w, FXint h, FXint pl, FXint pr, FXint pt, FXint pb):
66 8507 : FXPacker(p, opts, x, y, w, h, 0, 0, 0, 0, 0, 0) {
67 8507 : flags |= FLAG_ENABLED;
68 8507 : target = tgt;
69 8507 : message = sel;
70 : // create text field
71 8507 : myTextFieldIcon = new MFXTextFieldIcon(this, staticToolTip, GUIIcon::EMPTY, this, MFXComboBoxIcon::ID_TEXT, 0, 0, 0, 0, 0, pl, pr, pt, pb);
72 8507 : if (options & COMBOBOX_STATIC) {
73 8507 : myTextFieldIcon->setEditable(FALSE);
74 : }
75 : // create pane for list
76 8507 : myPane = new FXPopup(this, FRAME_LINE);
77 : // check if create search button
78 8507 : 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 8507 : myList = new MFXListIcon(myPane, this, MFXComboBoxIcon::ID_LIST, LIST_BROWSESELECT | LIST_AUTOSELECT | LAYOUT_FILL_X | LAYOUT_FILL_Y | SCROLLERS_TRACK | HSCROLLER_NEVER);
87 8507 : if (options & COMBOBOX_STATIC) {
88 8507 : myList->setScrollStyle(SCROLLERS_TRACK | HSCROLLING_OFF);
89 : }
90 8507 : myList->setNumVisible(visibleItems);
91 : // create button
92 8507 : 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 8507 : myButton->setXOffset(border);
94 : myButton->setYOffset(border);
95 8507 : flags &= ~(FXuint)FLAG_UPDATE; // Never GUI update
96 :
97 8507 : }
98 :
99 :
100 16960 : MFXComboBoxIcon::~MFXComboBoxIcon() {
101 8480 : delete myPane;
102 8480 : myPane = (FXPopup*) - 1L;
103 8480 : myTextFieldIcon = (MFXTextFieldIcon*) - 1L;
104 8480 : myButton = (FXMenuButton*) - 1L;
105 8480 : if (myTextFieldSearch) {
106 0 : myTextFieldSearch = (MFXTextFieldSearch*) - 1L;
107 0 : myNoItemsLabel = (FXLabel*) - 1L;
108 : }
109 8480 : myList = (MFXListIcon*) - 1L;
110 16960 : }
111 :
112 :
113 : void
114 17014 : MFXComboBoxIcon::create() {
115 17014 : FXPacker::create();
116 17014 : myPane->create();
117 17014 : }
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 16834 : MFXComboBoxIcon::getDefaultWidth() {
156 16834 : FXint ww = myTextFieldIcon->getDefaultWidth() + myButton->getDefaultWidth() + (border << 1);
157 16834 : FXint pw = myPane->getDefaultWidth();
158 16834 : 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 8414 : MFXComboBoxIcon::layout() {
173 8414 : const FXint itemHeight = height - (border << 1);
174 8414 : const FXint buttonWidth = myButton->getDefaultWidth();
175 8414 : const FXint textWidth = width - buttonWidth - (border << 1);
176 8414 : myTextFieldIcon->position(border, border, textWidth, itemHeight);
177 8414 : myButton->position(border + textWidth, border, buttonWidth, itemHeight);
178 8414 : if (myTextFieldSearch) {
179 0 : myTextFieldSearch->resize(width, height);
180 0 : myNoItemsLabel->resize(width, height);
181 : }
182 8414 : myPane->resize(width, myPane->getDefaultHeight());
183 8414 : flags &= ~(FXuint)FLAG_DIRTY;
184 8414 : }
185 :
186 :
187 : FXString
188 0 : MFXComboBoxIcon::getText() const {
189 0 : if (myTextFieldIcon) {
190 0 : return myTextFieldIcon->getText();
191 : } else {
192 0 : return "";
193 : }
194 : }
195 :
196 :
197 : FXIcon*
198 0 : MFXComboBoxIcon::getIcon() const {
199 0 : if (myTextFieldIcon) {
200 0 : return myTextFieldIcon->getIcon();
201 : } else {
202 : return nullptr;
203 : }
204 : }
205 :
206 :
207 : FXint
208 51078 : MFXComboBoxIcon::getNumItems() const {
209 51078 : return myList->getNumItems();
210 : }
211 :
212 :
213 : void
214 0 : MFXComboBoxIcon::setNumVisible(FXint nvis) {
215 0 : myList->setNumVisible(nvis);
216 0 : }
217 :
218 :
219 : void
220 0 : MFXComboBoxIcon::setText(const FXString& text, FXbool notify) {
221 0 : myTextFieldIcon->setText(text, notify);
222 0 : }
223 :
224 :
225 : FXbool
226 42571 : MFXComboBoxIcon::isItemCurrent(FXint index) const {
227 42571 : return myList->isItemCurrent(index);
228 : }
229 :
230 :
231 : long
232 8545 : MFXComboBoxIcon::setCurrentItem(const FXint index, FXbool notify) {
233 8545 : if (index >= 0 && index <= myList->getNumItems()) {
234 : // get item
235 8545 : MFXListIconItem* item = myList->getItem(index);
236 : // set it as current item and make it visible
237 8545 : myList->setCurrentItem(item);
238 8545 : myList->makeItemVisible(index);
239 : // update both text fields
240 8545 : myTextFieldIcon->setText(item->getText());
241 8545 : myTextFieldIcon->setIcon(item->getIcon());
242 8545 : myTextFieldIcon->setBackColor(item->getBackGroundColor());
243 : // check if notify
244 8545 : if (notify && target) {
245 0 : return target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)item);
246 : }
247 : } else {
248 0 : fxerror("%s::setItem: index out of range.\n", getClassName());
249 : }
250 : return 0;
251 : }
252 :
253 :
254 : long
255 0 : MFXComboBoxIcon::setCurrentItem(const FXString& text, FXbool notify) {
256 : // check if item exist
257 0 : for (int i = 0; i < myList->getNumItems(); i++) {
258 0 : const auto itemText = myList->tolowerString(myList->getItem(i)->getText());
259 0 : if (myList->tolowerString(text) == itemText) {
260 : // use "set curent item" function
261 0 : return setCurrentItem(i, notify);
262 : }
263 0 : }
264 : return 0;
265 : }
266 :
267 :
268 : FXint
269 0 : MFXComboBoxIcon::getCurrentItem() const {
270 0 : return myList->getCurrentItemIndex();
271 : }
272 :
273 :
274 : FXint
275 0 : MFXComboBoxIcon::updateIconItem(FXint index, const FXString& text, FXIcon* icon, FXColor bgColor, void* ptr) {
276 0 : if (index < 0 || myList->getNumItems() <= index) {
277 0 : fxerror("%s::setItem: index out of range.\n", getClassName());
278 : }
279 0 : myList->editItem(index, text, NULL, ptr);
280 0 : if (isItemCurrent(index)) {
281 0 : myTextFieldIcon->setText(text);
282 0 : myTextFieldIcon->setBackColor(bgColor);
283 0 : myTextFieldIcon->setIcon(icon);
284 : }
285 0 : recalc();
286 0 : return index;
287 : }
288 :
289 :
290 : FXint
291 0 : MFXComboBoxIcon::insertIconItem(FXint index, const FXString& text, FXIcon* icon, FXColor bgColor, void* ptr) {
292 0 : index = myList->insertItem(index, new MFXListIconItem(text, icon, bgColor, ptr));
293 0 : if (isItemCurrent(index)) {
294 0 : myTextFieldIcon->setText(text);
295 0 : myTextFieldIcon->setBackColor(bgColor);
296 0 : myTextFieldIcon->setIcon(icon);
297 : }
298 0 : recalc();
299 0 : return index;
300 : }
301 :
302 :
303 : FXint
304 42571 : MFXComboBoxIcon::appendIconItem(const FXString& text, FXIcon* icon, FXColor bgColor, void* ptr) {
305 42571 : FXint index = myList->appendItem(new MFXListIconItem(text, icon, bgColor, ptr));
306 42571 : if (isItemCurrent(getNumItems() - 1)) {
307 8507 : myTextFieldIcon->setText(text);
308 8507 : myTextFieldIcon->setBackColor(bgColor);
309 8507 : myTextFieldIcon->setIcon(icon);
310 : }
311 42571 : recalc();
312 42571 : return index;
313 : }
314 :
315 :
316 : void
317 0 : MFXComboBoxIcon::removeItem(FXint index) {
318 0 : const auto isCurrent = myList->isItemCurrent(index);
319 0 : if (isCurrent == TRUE) {
320 0 : if ((index > 0) && (index < (int)myList->getNumItems())) {
321 0 : setCurrentItem(index - 1);
322 0 : } else if (myList->getNumItems() > 0) {
323 0 : setCurrentItem(0);
324 : }
325 : }
326 0 : myList->removeItem(index);
327 0 : recalc();
328 0 : }
329 :
330 :
331 : void
332 0 : MFXComboBoxIcon::clearItems() {
333 0 : myTextFieldIcon->setText("");
334 0 : myList->clearItems();
335 0 : recalc();
336 0 : }
337 :
338 :
339 : FXint
340 41 : MFXComboBoxIcon::findItem(const FXString& text) const {
341 41 : return myList->findItem(text);
342 : }
343 :
344 :
345 : std::string
346 0 : MFXComboBoxIcon::getItemText(FXint index) const {
347 0 : return myList->getItem(index)->getText().text();
348 : }
349 :
350 :
351 : void
352 0 : MFXComboBoxIcon::setBackColor(FXColor clr) {
353 0 : myTextFieldIcon->setBackColor(clr);
354 0 : myList->setBackColor(clr);
355 0 : }
356 :
357 :
358 : void
359 0 : MFXComboBoxIcon::setTextColor(FXColor clr) {
360 0 : myTextFieldIcon->setTextColor(clr);
361 0 : myList->setTextColor(clr);
362 0 : }
363 :
364 :
365 : FXColor
366 0 : MFXComboBoxIcon::getTextColor() const {
367 0 : return myTextFieldIcon->getTextColor();
368 : }
369 :
370 :
371 : void
372 0 : MFXComboBoxIcon::setTipText(const FXString& txt) {
373 0 : myTextFieldIcon->setTipText(txt);
374 0 : }
375 :
376 :
377 : const FXString&
378 0 : MFXComboBoxIcon::getTipText() const {
379 0 : return myTextFieldIcon->getTipText();
380 : }
381 :
382 :
383 : long
384 480441 : MFXComboBoxIcon::onUpdFmText(FXObject*, FXSelector, void*) {
385 480441 : return (target && !myPane->shown()) ? target->tryHandle(this, FXSEL(SEL_UPDATE, message), NULL) : 0;
386 : }
387 :
388 :
389 : long
390 0 : MFXComboBoxIcon::onCmdFilter(FXObject*, FXSelector, void* ptr) {
391 0 : myList->setFilter(myTextFieldSearch->getText(), myNoItemsLabel);
392 0 : myPane->resize(width, myPane->getDefaultHeight());
393 0 : myPane->recalc();
394 0 : myPane->onPaint(0, 0, ptr);
395 0 : return 1;
396 : }
397 :
398 :
399 : long
400 0 : MFXComboBoxIcon::onFwdToText(FXObject* sender, FXSelector sel, void* ptr) {
401 0 : return myTextFieldIcon->handle(sender, sel, ptr);
402 : }
403 :
404 :
405 : long
406 0 : MFXComboBoxIcon::onListClicked(FXObject*, FXSelector sel, void* ptr) {
407 : // hide pane
408 0 : myButton->handle(this, FXSEL(SEL_COMMAND, ID_UNPOST), NULL);
409 0 : if (FXSELTYPE(sel) == SEL_COMMAND) {
410 : // cast MFXListIconItem
411 : const MFXListIconItem* item = (MFXListIconItem*)ptr;
412 : // set icon and background color
413 0 : myTextFieldIcon->setText(item->getText());
414 0 : myTextFieldIcon->setIcon(item->getIcon());
415 0 : myTextFieldIcon->setBackColor(item->getBackGroundColor());
416 : // Select if editable
417 0 : if (!(options & COMBOBOX_STATIC)) {
418 0 : myTextFieldIcon->selectAll();
419 : }
420 0 : if (target) {
421 0 : target->tryHandle(this, FXSEL(SEL_COMMAND, message), (void*)getText().text());
422 : }
423 : }
424 0 : return 1;
425 : }
426 :
427 :
428 : long
429 0 : MFXComboBoxIcon::onTextButton(FXObject*, FXSelector, void*) {
430 0 : if (options & COMBOBOX_STATIC) {
431 : // Post the myList
432 0 : myButton->handle(this, FXSEL(SEL_COMMAND, ID_POST), NULL);
433 0 : return 1;
434 : }
435 : return 0;
436 : }
437 :
438 :
439 : long
440 0 : MFXComboBoxIcon::onTextChanged(FXObject*, FXSelector, void* ptr) {
441 0 : return target ? target->tryHandle(this, FXSEL(SEL_CHANGED, message), ptr) : 0;
442 : }
443 :
444 :
445 : long
446 0 : MFXComboBoxIcon::onTextCommand(FXObject*, FXSelector, void* ptr) {
447 : // reset background colors
448 0 : myTextFieldIcon->setBackColor(GUIDesignBackgroundColorWhite);
449 : // check if item exist
450 0 : for (int i = 0; i < myList->getNumItems(); i++) {
451 0 : const auto itemText = myList->tolowerString(myList->getItem(i)->getText());
452 0 : if (myList->tolowerString(myTextFieldIcon->getText()) == itemText) {
453 : // use "set curent item" function
454 0 : return setCurrentItem(i, TRUE);
455 : }
456 0 : }
457 : // no item found, then reset icon label
458 0 : myTextFieldIcon->setIcon(GUIIconSubSys::getIcon(GUIIcon::CANCEL));
459 0 : return target ? target->tryHandle(this, FXSEL(SEL_COMMAND, message), ptr) : 0;
460 : }
461 :
462 :
463 : long
464 0 : MFXComboBoxIcon::onFocusSelf(FXObject* sender, FXSelector, void* ptr) {
465 0 : return myTextFieldIcon->handle(sender, FXSEL(SEL_FOCUS_SELF, 0), ptr);
466 : }
467 :
468 :
469 : long
470 0 : MFXComboBoxIcon::onFocusUp(FXObject*, FXSelector, void*) {
471 0 : if (isEnabled()) {
472 0 : FXint index = getCurrentItem();
473 0 : if (index < 0) {
474 0 : index = getNumItems() - 1;
475 0 : } else if (0 < index) {
476 0 : index--;
477 : }
478 0 : if (0 <= index && index < getNumItems()) {
479 0 : setCurrentItem(index, TRUE);
480 : }
481 0 : return 1;
482 : }
483 : return 0;
484 : }
485 :
486 :
487 : long
488 0 : MFXComboBoxIcon::onFocusDown(FXObject*, FXSelector, void*) {
489 0 : if (isEnabled()) {
490 0 : FXint index = getCurrentItem();
491 0 : if (index < 0) {
492 : index = 0;
493 0 : } else if (index < getNumItems() - 1) {
494 0 : index++;
495 : }
496 0 : if (0 <= index && index < getNumItems()) {
497 0 : setCurrentItem(index, TRUE);
498 : }
499 0 : return 1;
500 : }
501 : return 0;
502 : }
503 :
504 :
505 0 : long MFXComboBoxIcon::onMouseWheel(FXObject*, FXSelector, void* ptr) {
506 : FXEvent* event = (FXEvent*)ptr;
507 0 : if (isEnabled()) {
508 0 : FXint index = getCurrentItem();
509 0 : if (event->code < 0) {
510 0 : if (index < 0) {
511 : index = 0;
512 0 : } else if (index < getNumItems() - 1) {
513 0 : index++;
514 : }
515 0 : } else if (event->code > 0) {
516 0 : if (index < 0) {
517 0 : index = getNumItems() - 1;
518 0 : } else if (0 < index) {
519 0 : index--;
520 : }
521 : }
522 0 : if (0 <= index && index < getNumItems()) {
523 0 : setCurrentItem(index, TRUE);
524 : }
525 0 : return 1;
526 : }
527 : return 0;
528 : }
529 :
530 :
531 0 : MFXComboBoxIcon::MFXComboBoxIcon() {}
|