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