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