Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 GUIDialog_ChooserAbstract.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @date Sept 2002
19 : ///
20 : // Class for the window that allows to choose a street, junction or vehicle
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include <string>
25 : #include <vector>
26 : #include <fxkeys.h>
27 : #include <utils/common/MsgHandler.h>
28 : #include <utils/gui/windows/GUIAppEnum.h>
29 : #include <utils/gui/windows/GUIGlChildWindow.h>
30 : #include <utils/gui/windows/GUIMainWindow.h>
31 : #include <utils/gui/globjects/GUIGlObject.h>
32 : #include <utils/gui/globjects/GUIGlObjectStorage.h>
33 : #include <utils/gui/images/GUIIconSubSys.h>
34 : #include <utils/gui/div/GUIGlobalSelection.h>
35 : #include <utils/gui/div/GUIDesigns.h>
36 : #include <utils/gui/globjects/GUIGlObject_AbstractAdd.h>
37 :
38 : #include "GUIDialog_ChooserAbstract.h"
39 :
40 :
41 : // ===========================================================================
42 : // FOX callback mapping
43 : // ===========================================================================
44 : FXDEFMAP(GUIDialog_ChooserAbstract) GUIDialog_ChooserAbstractMap[] = {
45 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSER_CENTER, GUIDialog_ChooserAbstract::onCmdCenter),
46 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSER_TRACK, GUIDialog_ChooserAbstract::onCmdTrack),
47 : FXMAPFUNC(SEL_COMMAND, MID_CANCEL, GUIDialog_ChooserAbstract::onCmdClose),
48 : FXMAPFUNC(SEL_CHANGED, MID_CHOOSER_TEXT, GUIDialog_ChooserAbstract::onChgText),
49 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSER_TEXT, GUIDialog_ChooserAbstract::onCmdText),
50 : FXMAPFUNC(SEL_KEYPRESS, 0, GUIDialog_ChooserAbstract::onKeyPress),
51 : FXMAPFUNC(SEL_CHANGED, MID_CHOOSER_LIST, GUIDialog_ChooserAbstract::onChgList),
52 : FXMAPFUNC(SEL_DESELECTED, MID_CHOOSER_LIST, GUIDialog_ChooserAbstract::onChgListSel),
53 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSER_FILTER, GUIDialog_ChooserAbstract::onCmdFilter),
54 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSER_FILTER_SUBSTR, GUIDialog_ChooserAbstract::onCmdFilterSubstr),
55 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_INVERT, GUIDialog_ChooserAbstract::onCmdToggleSelection),
56 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_SELECT, GUIDialog_ChooserAbstract::onCmdAddListSelection),
57 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_CLEAR, GUIDialog_ChooserAbstract::onCmdClearListSelection),
58 : FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_NAME, GUIDialog_ChooserAbstract::onCmdLocateByName),
59 : FXMAPFUNC(SEL_COMMAND, MID_UPDATE, GUIDialog_ChooserAbstract::onCmdUpdate),
60 : };
61 :
62 0 : FXIMPLEMENT(GUIDialog_ChooserAbstract, FXMainWindow, GUIDialog_ChooserAbstractMap, ARRAYNUMBER(GUIDialog_ChooserAbstractMap))
63 :
64 :
65 : // ===========================================================================
66 : // method definitions
67 : // ===========================================================================
68 : #ifdef _MSC_VER
69 : #pragma warning(push)
70 : #pragma warning(disable: 4355) // mask warning about "this" in initializers
71 : #endif
72 0 : GUIDialog_ChooserAbstract::GUIDialog_ChooserAbstract(GUIGlChildWindow* windowsParent, int messageId,
73 0 : FXIcon* icon, const FXString& title, const std::vector<GUIGlID>& ids, GUIGlObjectStorage& /*glStorage*/) :
74 : FXMainWindow(windowsParent->getApp(), title, icon, nullptr, GUIDesignChooserDialog),
75 : GUIPersistentWindowPos(this, "LOCATOR", true, 20, 40, 300, 350),
76 0 : myWindowsParent(windowsParent),
77 0 : myMessageId(messageId),
78 0 : myLocateByName(false),
79 0 : myHaveFilteredSubstring(false) {
80 0 : FXHorizontalFrame* hbox = new FXHorizontalFrame(this, GUIDesignAuxiliarFrame);
81 : // build the list
82 0 : FXVerticalFrame* layoutLeft = new FXVerticalFrame(hbox, GUIDesignChooserLayoutLeft);
83 0 : myTextEntry = new FXTextField(layoutLeft, 0, this, MID_CHOOSER_TEXT, TEXTFIELD_ENTER_ONLY | GUIDesignChooserTextField);
84 0 : FXVerticalFrame* layoutList = new FXVerticalFrame(layoutLeft, GUIDesignChooserLayoutList);
85 0 : myList = new FXList(layoutList, this, MID_CHOOSER_LIST, GUIDesignChooserListSingle);
86 : // build the buttons
87 0 : FXVerticalFrame* layoutRight = new FXVerticalFrame(hbox, GUIDesignChooserLayoutRight);
88 0 : myCenterButton = GUIDesigns::buildFXButton(layoutRight, TL("Center"), "", "", GUIIconSubSys::getIcon(GUIIcon::RECENTERVIEW), this, MID_CHOOSER_CENTER, GUIDesignChooserButtons);
89 0 : myTrackButton = GUIDesigns::buildFXButton(layoutRight, TL("Track"), "", "", GUIIconSubSys::getIcon(GUIIcon::RECENTERVIEW), this, MID_CHOOSER_TRACK, GUIDesignChooserButtons);
90 : // only enable Track Button if we're locating vehicles
91 0 : if (title.text() != std::string(TL("Vehicle Chooser"))) {
92 0 : myTrackButton->disable();
93 0 : myTrackButton->hide();
94 : }
95 0 : new FXHorizontalSeparator(layoutRight, GUIDesignHorizontalSeparator);
96 0 : GUIDesigns::buildFXButton(layoutRight, TL("&Hide Unselected"), "", "", GUIIconSubSys::getIcon(GUIIcon::FLAG), this, MID_CHOOSER_FILTER, GUIDesignChooserButtons);
97 0 : GUIDesigns::buildFXButton(layoutRight, TL("By &Name"), TL("Locate item by name"), "", nullptr, this, MID_CHOOSEN_NAME, GUIDesignChooserButtons);
98 0 : GUIDesigns::buildFXButton(layoutRight, TL("&Select/deselect"), "", TL("Select/deselect current object"), GUIIconSubSys::getIcon(GUIIcon::FLAG), this, MID_CHOOSEN_INVERT, GUIDesignChooserButtons);
99 0 : GUIDesigns::buildFXButton(layoutRight, TL("&Filter substring"), "", "", nullptr, this, MID_CHOOSER_FILTER_SUBSTR, GUIDesignChooserButtons);
100 0 : GUIDesigns::buildFXButton(layoutRight, TL("Select &all"), "", TL("Select all items in list"), GUIIconSubSys::getIcon(GUIIcon::FLAG), this, MID_CHOOSEN_SELECT, GUIDesignChooserButtons);
101 0 : GUIDesigns::buildFXButton(layoutRight, TL("&Deselect all"), "", TL("Deselect all items in list"), GUIIconSubSys::getIcon(GUIIcon::FLAG), this, MID_CHOOSEN_CLEAR, GUIDesignChooserButtons);
102 0 : GUIDesigns::buildFXButton(layoutRight, TL("&Update"), "", TL("Reload all ids"), GUIIconSubSys::getIcon(GUIIcon::RELOAD), this, MID_UPDATE, GUIDesignChooserButtons);
103 0 : new FXHorizontalSeparator(layoutRight, GUIDesignHorizontalSeparator);
104 0 : GUIDesigns::buildFXButton(layoutRight, TL("&Close"), "", "", GUIIconSubSys::getIcon(GUIIcon::NO), this, MID_CANCEL, GUIDesignChooserButtons);
105 0 : myCountLabel = new FXLabel(layoutRight, "placeholder", nullptr, LAYOUT_BOTTOM | LAYOUT_FILL_X | JUSTIFY_LEFT);
106 0 : myCaseSensitive = new FXCheckButton(layoutRight, TL("case-sensitive search"));
107 0 : myCaseSensitive->setCheck(getApp()->reg().readIntEntry("LOCATOR", "caseSensitive", 0) == 1);
108 0 : myInstantCenter = new FXCheckButton(layoutRight, TL("auto-center"));
109 0 : myInstantCenter->setCheck(getApp()->reg().readIntEntry("LOCATOR", "autoCenter", 0) == 1);
110 0 : refreshList(ids);
111 : // add child in windowsParent
112 0 : myWindowsParent->getGUIMainWindowParent()->addChild(this);
113 0 : loadWindowPos();
114 : // create and show dialog
115 0 : create();
116 0 : show();
117 0 : }
118 : #ifdef _MSC_VER
119 : #pragma warning(pop)
120 : #endif
121 :
122 :
123 0 : GUIDialog_ChooserAbstract::~GUIDialog_ChooserAbstract() {
124 : // remove child from windowsParent
125 0 : myWindowsParent->getGUIMainWindowParent()->removeChild(this);
126 0 : getApp()->reg().writeIntEntry("LOCATOR", "autoCenter", myInstantCenter->getCheck());
127 0 : getApp()->reg().writeIntEntry("LOCATOR", "caseSensitive", myCaseSensitive->getCheck());
128 0 : }
129 :
130 :
131 : GUIGlObject*
132 0 : GUIDialog_ChooserAbstract::getObject() const {
133 0 : return static_cast<GUIGlObject*>(mySelected);
134 : }
135 :
136 :
137 : void
138 0 : GUIDialog_ChooserAbstract::show() {
139 0 : FXMainWindow::show();
140 0 : myTextEntry->setFocus();
141 0 : }
142 :
143 :
144 : long
145 0 : GUIDialog_ChooserAbstract::onCmdCenter(FXObject*, FXSelector, void*) {
146 0 : int selected = myList->getCurrentItem();
147 0 : if (selected >= 0) {
148 0 : myWindowsParent->getView()->stopTrack();
149 0 : myWindowsParent->setView(*static_cast<GUIGlID*>(myList->getItemData(selected)));
150 : }
151 0 : return 1;
152 : }
153 :
154 :
155 : long
156 0 : GUIDialog_ChooserAbstract::onCmdTrack(FXObject*, FXSelector, void*) {
157 0 : int selected = myList->getCurrentItem();
158 0 : if (selected >= 0) {
159 0 : myWindowsParent->setView(*static_cast<GUIGlID*>(myList->getItemData(selected)));
160 0 : GUIGlID id = *static_cast<GUIGlID*>(myList->getItemData(selected));
161 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(id);
162 0 : if (o->getType() == GLO_VEHICLE) {
163 0 : myWindowsParent->getView()->startTrack(o->getGlID());
164 : }
165 0 : GUIGlObjectStorage::gIDStorage.unblockObject(id);
166 : }
167 0 : return 1;
168 : }
169 :
170 :
171 : long
172 0 : GUIDialog_ChooserAbstract::onCmdClose(FXObject*, FXSelector, void*) {
173 0 : close(true);
174 0 : return 1;
175 : }
176 :
177 : long
178 0 : GUIDialog_ChooserAbstract::onChgList(FXObject*, FXSelector, void*) {
179 : // mouse-click toggles item selection but changked current item with
180 : // keyboard does not affect select
181 : // Enabling the line blow toggles the behavior (which must be fixed via onChgListSel)
182 0 : myList->selectItem(myList->getCurrentItem());
183 0 : if (myInstantCenter->getCheck()) {
184 0 : onCmdCenter(nullptr, 0, nullptr);
185 : }
186 0 : return 1;
187 : }
188 :
189 : long
190 0 : GUIDialog_ChooserAbstract::onChgListSel(FXObject*, FXSelector, void*) {
191 0 : myList->selectItem(myList->getCurrentItem());
192 0 : return 1;
193 : }
194 :
195 : long
196 0 : GUIDialog_ChooserAbstract::onChgText(FXObject*, FXSelector, void*) {
197 0 : const bool caseSensitive = myCaseSensitive->getCheck() == TRUE;
198 : int id = -1;
199 0 : if (myLocateByName || myHaveFilteredSubstring) {
200 : // findItem does not support substring search
201 0 : const int numItems = myList->getNumItems();
202 0 : FXString t = myTextEntry->getText();
203 0 : if (!caseSensitive) {
204 0 : t = t.lower();
205 : }
206 0 : for (int i = 0; i < numItems; i++) {
207 0 : FXString t2 = myList->getItemText(i);
208 0 : if (!caseSensitive) {
209 0 : t2 = t2.lower();
210 : }
211 0 : if (t2.find(t) >= 0) {
212 : id = i;
213 : break;
214 : }
215 0 : }
216 0 : } else {
217 0 : const int caseOpt = caseSensitive ? 0 : SEARCH_IGNORECASE;
218 0 : id = myList->findItem(myTextEntry->getText(), -1, SEARCH_PREFIX | caseOpt);
219 : }
220 0 : if (id < 0) {
221 0 : if (myList->getNumItems() > 0) {
222 0 : myList->deselectItem(myList->getCurrentItem());
223 : }
224 0 : myCenterButton->disable();
225 0 : myTrackButton->disable();
226 0 : return 1;
227 : }
228 0 : myList->deselectItem(myList->getCurrentItem());
229 0 : myList->makeItemVisible(id);
230 0 : myList->selectItem(id);
231 0 : myList->setCurrentItem(id, true);
232 0 : myCenterButton->enable();
233 0 : myTrackButton->enable();
234 0 : return 1;
235 : }
236 :
237 :
238 : long
239 0 : GUIDialog_ChooserAbstract::onCmdText(FXObject*, FXSelector, void*) {
240 0 : int current = myList->getCurrentItem();
241 0 : if (current >= 0 && myList->isItemSelected(current)) {
242 0 : myWindowsParent->setView(*static_cast<GUIGlID*>(myList->getItemData(current)));
243 : }
244 0 : return 1;
245 : }
246 :
247 :
248 :
249 : long
250 0 : GUIDialog_ChooserAbstract::onKeyPress(FXObject* o, FXSelector sel, void* ptr) {
251 : FXEvent* event = (FXEvent*)ptr;
252 0 : if (event->code == KEY_Return) {
253 0 : onCmdText(nullptr, 0, nullptr);
254 0 : if ((event->state & CONTROLMASK) != 0) {
255 0 : close(true);
256 : }
257 0 : return 1;
258 0 : } else if (event->code == KEY_Left || (event->code == KEY_Up && myList->getCurrentItem() == 0)) {
259 0 : myTextEntry->setFocus();
260 0 : return 1;
261 0 : } else if (event->code == KEY_Escape) {
262 0 : close(true);
263 0 : return 1;
264 : }
265 0 : return FXMainWindow::onKeyPress(o, sel, ptr);
266 : }
267 :
268 :
269 : long
270 0 : GUIDialog_ChooserAbstract::onCmdFilter(FXObject*, FXSelector, void*) {
271 0 : FXIcon* flag = GUIIconSubSys::getIcon(GUIIcon::FLAG);
272 : std::vector<GUIGlID> selectedGlIDs;
273 0 : const int numItems = myList->getNumItems();
274 0 : for (int i = 0; i < numItems; i++) {
275 0 : const GUIGlID glID = *static_cast<GUIGlID*>(myList->getItemData(i));
276 0 : if (myList->getItemIcon(i) == flag) {
277 0 : selectedGlIDs.push_back(glID);
278 : }
279 : }
280 0 : refreshList(selectedGlIDs);
281 0 : return 1;
282 0 : }
283 :
284 :
285 : long
286 0 : GUIDialog_ChooserAbstract::onCmdFilterSubstr(FXObject*, FXSelector, void*) {
287 0 : const bool caseSensitive = myCaseSensitive->getCheck() == TRUE;
288 : std::vector<GUIGlID> selectedGlIDs;
289 0 : const int numItems = myList->getNumItems();
290 0 : FXString t = myTextEntry->getText();
291 0 : if (!caseSensitive) {
292 0 : t = t.lower();
293 : }
294 0 : for (int i = 0; i < numItems; i++) {
295 0 : FXString t2 = myList->getItemText(i);
296 0 : if (!caseSensitive) {
297 0 : t2 = t2.lower();
298 : }
299 0 : if (t2.find(t) >= 0) {
300 0 : const GUIGlID glID = *static_cast<GUIGlID*>(myList->getItemData(i));
301 0 : selectedGlIDs.push_back(glID);
302 : }
303 0 : }
304 0 : refreshList(selectedGlIDs);
305 : // filter ACs in netedit
306 0 : filterACs(selectedGlIDs);
307 0 : myHaveFilteredSubstring = true;
308 0 : onChgText(nullptr, 0, nullptr);
309 0 : return 1;
310 0 : }
311 :
312 :
313 : std::string
314 0 : GUIDialog_ChooserAbstract::getObjectName(GUIGlObject* o) const {
315 0 : if (myLocateByName) {
316 0 : return o->getOptionalName();
317 : } else {
318 : return o->getMicrosimID();
319 : }
320 : }
321 :
322 : void
323 0 : GUIDialog_ChooserAbstract::refreshList(const std::vector<GUIGlID>& ids) {
324 0 : myList->clearItems();
325 0 : for (auto i : ids) {
326 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(i);
327 0 : if (o == nullptr) {
328 0 : continue;
329 : }
330 0 : const std::string& name = getObjectName(o);
331 0 : const bool selected = myWindowsParent->isSelected(o);
332 0 : FXIcon* const ico = selected ? GUIIconSubSys::getIcon(GUIIcon::FLAG) : nullptr;
333 0 : myIDs.insert(o->getGlID());
334 0 : myList->appendItem(name.c_str(), ico, (void*) & (*myIDs.find(o->getGlID())));
335 0 : GUIGlObjectStorage::gIDStorage.unblockObject(i);
336 : }
337 0 : myList->update();
338 0 : myCountLabel->setText(TLF("% objects", toString(ids.size())).c_str());
339 0 : }
340 :
341 :
342 : long
343 0 : GUIDialog_ChooserAbstract::onCmdToggleSelection(FXObject*, FXSelector, void*) {
344 0 : FXIcon* flag = GUIIconSubSys::getIcon(GUIIcon::FLAG);
345 0 : int i = myList->getCurrentItem();
346 0 : if (i >= 0) {
347 0 : toggleSelection(i);
348 0 : if (myList->getItemIcon(i) == flag) {
349 0 : myList->setItemIcon(i, nullptr);
350 : } else {
351 0 : myList->setItemIcon(i, flag);
352 : }
353 : }
354 0 : myList->update();
355 0 : myWindowsParent->getView()->update();
356 0 : return 1;
357 : }
358 :
359 :
360 : long
361 0 : GUIDialog_ChooserAbstract::onCmdAddListSelection(FXObject*, FXSelector, void*) {
362 0 : FXIcon* flag = GUIIconSubSys::getIcon(GUIIcon::FLAG);
363 0 : const int numItems = myList->getNumItems();
364 0 : for (int i = 0; i < numItems; i++) {
365 0 : select(i);
366 0 : myList->setItemIcon(i, flag);
367 : }
368 0 : myList->update();
369 0 : myWindowsParent->getView()->update();
370 0 : return 1;
371 : }
372 :
373 :
374 : long
375 0 : GUIDialog_ChooserAbstract::onCmdClearListSelection(FXObject*, FXSelector, void*) {
376 0 : const int numItems = myList->getNumItems();
377 0 : for (int i = 0; i < numItems; i++) {
378 0 : deselect(i);
379 0 : myList->setItemIcon(i, nullptr);
380 : }
381 0 : myList->update();
382 0 : myWindowsParent->getView()->update();
383 0 : return 1;
384 : }
385 :
386 :
387 : long
388 0 : GUIDialog_ChooserAbstract::onCmdLocateByName(FXObject*, FXSelector, void*) {
389 : std::vector<std::pair<std::string, GUIGlID> > namesAndIDs;
390 0 : myLocateByName = true;
391 0 : const int numItems = myList->getNumItems();
392 0 : for (int i = 0; i < numItems; i++) {
393 0 : GUIGlID glID = *static_cast<GUIGlID*>(myList->getItemData(i));
394 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(glID);
395 0 : if (o != 0) {
396 0 : const std::string& name = getObjectName(o);
397 0 : if (name != "") {
398 0 : namesAndIDs.push_back(std::make_pair(name, glID));
399 : }
400 : }
401 0 : GUIGlObjectStorage::gIDStorage.unblockObject(glID);
402 : }
403 0 : std::sort(namesAndIDs.begin(), namesAndIDs.end());
404 : std::vector<GUIGlID> selectedGlIDs;
405 0 : for (const auto& item : namesAndIDs) {
406 0 : selectedGlIDs.push_back(item.second);
407 : }
408 0 : refreshList(selectedGlIDs);
409 0 : myTextEntry->setFocus();
410 0 : return 1;
411 0 : }
412 :
413 : long
414 0 : GUIDialog_ChooserAbstract::onCmdUpdate(FXObject*, FXSelector, void*) {
415 0 : refreshList(myWindowsParent->getObjectIDs(myMessageId));
416 0 : return 1;
417 : }
418 :
419 : void
420 0 : GUIDialog_ChooserAbstract::toggleSelection(int listIndex) {
421 0 : GUIGlID* glID = static_cast<GUIGlID*>(myList->getItemData(listIndex));
422 0 : gSelected.toggleSelection(*glID);
423 0 : }
424 :
425 : void
426 0 : GUIDialog_ChooserAbstract::select(int listIndex) {
427 0 : GUIGlID* glID = static_cast<GUIGlID*>(myList->getItemData(listIndex));
428 0 : gSelected.select(*glID);
429 0 : }
430 :
431 : void
432 0 : GUIDialog_ChooserAbstract::deselect(int listIndex) {
433 0 : GUIGlID* glID = static_cast<GUIGlID*>(myList->getItemData(listIndex));
434 0 : gSelected.deselect(*glID);
435 0 : }
436 :
437 :
438 : void
439 0 : GUIDialog_ChooserAbstract::filterACs(const std::vector<GUIGlID>& /*GLIDs*/) {
440 : // overrided in GNEACChooserDialog
441 0 : }
442 :
443 : /****************************************************************************/
|