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 GUIMainWindow.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @date Tue, 29.05.2005
19 : ///
20 : //
21 : /****************************************************************************/
22 : #include <config.h>
23 :
24 : #include <string>
25 : #include <algorithm>
26 : #include <utils/foxtools/fxheader.h>
27 : // fx3d includes windows.h so we need to guard against macro pollution
28 : #ifdef WIN32
29 : #define NOMINMAX
30 : #endif
31 : #include <fx3d.h>
32 : #ifdef WIN32
33 : #undef NOMINMAX
34 : #endif
35 : #include <utils/common/MsgHandler.h>
36 : #include <utils/common/StringUtils.h>
37 : #include <utils/foxtools/MFXImageHelper.h>
38 : #include <utils/foxtools/MFXStaticToolTip.h>
39 : #include <utils/gui/images/GUITexturesHelper.h>
40 : #include <utils/gui/div/GUIDesigns.h>
41 : #include <utils/options/OptionsCont.h>
42 : #include "GUIMainWindow.h"
43 : #include "GUIGlChildWindow.h"
44 :
45 :
46 : // ===========================================================================
47 : // static member definitions
48 : // ===========================================================================
49 : GUIMainWindow* GUIMainWindow::myInstance = nullptr;
50 :
51 : // ===========================================================================
52 : // member method definitions
53 : // ===========================================================================
54 8030 : GUIMainWindow::GUIMainWindow(FXApp* app) :
55 : FXMainWindow(app, "sumo-gui main window", nullptr, nullptr, DECOR_ALL, 20, 20, 600, 400),
56 8030 : myAmFullScreen(false),
57 8030 : myTrackerLock(true),
58 8030 : myGLVisual(new FXGLVisual(app, VISUAL_DOUBLEBUFFER)),
59 8030 : myAmGaming(false),
60 8030 : myListInternal(false),
61 8030 : myListParking(true),
62 16060 : myListTeleporting(false) {
63 : // build static tooltips
64 8030 : myStaticTooltipMenu = new MFXStaticToolTip(app);
65 8030 : myStaticTooltipView = new MFXStaticToolTip(app);
66 : // build bold font
67 : FXFontDesc fdesc;
68 8030 : app->getNormalFont()->getFontDesc(fdesc);
69 8030 : fdesc.weight = FXFont::Bold;
70 8030 : GUIDesignHeight = (int)(fdesc.size / 90.0 * 18) + 5;
71 8030 : myBoldFont = new FXFont(app, fdesc);
72 : // https://en.wikipedia.org/wiki/Noto_fonts should be widely available
73 8030 : myFallbackFont = new FXFont(app, "Noto Sans CJK JP");
74 : // build docks
75 8030 : myTopDock = new FXDockSite(this, LAYOUT_SIDE_TOP | LAYOUT_FILL_X);
76 8030 : myBottomDock = new FXDockSite(this, LAYOUT_SIDE_BOTTOM | LAYOUT_FILL_X);
77 8030 : myLeftDock = new FXDockSite(this, LAYOUT_SIDE_LEFT | LAYOUT_FILL_Y);
78 8030 : myRightDock = new FXDockSite(this, LAYOUT_SIDE_RIGHT | LAYOUT_FILL_Y);
79 : // avoid instance Windows twice
80 8030 : if (myInstance != nullptr) {
81 0 : throw ProcessError("MainWindow initialized twice");
82 : }
83 8030 : myInstance = this;
84 : //myGLVisual->setStencilSize(8); // enable stencil buffer
85 8030 : }
86 :
87 :
88 8004 : GUIMainWindow::~GUIMainWindow() {
89 8004 : delete myStaticTooltipMenu;
90 8004 : delete myStaticTooltipView;
91 8004 : delete myBoldFont;
92 8004 : delete myFallbackFont;
93 8004 : delete myTopDock;
94 8004 : delete myBottomDock;
95 8004 : delete myLeftDock;
96 8004 : delete myRightDock;
97 8004 : myInstance = nullptr;
98 8004 : }
99 :
100 :
101 :
102 : void
103 7582 : GUIMainWindow::addGLChild(GUIGlChildWindow* child) {
104 7582 : myGLWindows.push_back(child);
105 7582 : }
106 :
107 :
108 : void
109 7559 : GUIMainWindow::removeGLChild(GUIGlChildWindow* child) {
110 7559 : std::vector<GUIGlChildWindow*>::iterator i = std::find(myGLWindows.begin(), myGLWindows.end(), child);
111 7559 : if (i != myGLWindows.end()) {
112 7556 : myGLWindows.erase(i);
113 : }
114 7559 : }
115 :
116 :
117 : void
118 0 : GUIMainWindow::addChild(FXMainWindow* child) {
119 0 : myTrackerLock.lock();
120 0 : myTrackerWindows.push_back(child);
121 0 : myTrackerLock.unlock();
122 0 : }
123 :
124 :
125 : void
126 0 : GUIMainWindow::removeChild(FXMainWindow* child) {
127 0 : myTrackerLock.lock();
128 0 : std::vector<FXMainWindow*>::iterator i = std::find(myTrackerWindows.begin(), myTrackerWindows.end(), child);
129 0 : myTrackerWindows.erase(i);
130 0 : myTrackerLock.unlock();
131 0 : }
132 :
133 :
134 : FXDockSite*
135 0 : GUIMainWindow::getTopDock() {
136 0 : return myTopDock;
137 : }
138 :
139 :
140 : std::vector<std::string>
141 675 : GUIMainWindow::getViewIDs() const {
142 : std::vector<std::string> ret;
143 1653 : for (GUIGlChildWindow* const window : myGLWindows) {
144 1956 : ret.push_back(window->getTitle().text());
145 : }
146 675 : return ret;
147 0 : }
148 :
149 :
150 : GUIGlChildWindow*
151 345 : GUIMainWindow::getViewByID(const std::string& id) const {
152 348 : for (GUIGlChildWindow* const window : myGLWindows) {
153 345 : if (std::string(window->getTitle().text()) == id) {
154 : return window;
155 : }
156 : }
157 : return nullptr;
158 : }
159 :
160 :
161 : void
162 3 : GUIMainWindow::removeViewByID(const std::string& id) {
163 6 : for (GUIGlChildWindow* const window : myGLWindows) {
164 6 : if (std::string(window->getTitle().text()) == id) {
165 3 : window->close();
166 3 : removeGLChild(window);
167 : return;
168 : }
169 : }
170 : }
171 :
172 :
173 : FXFont*
174 0 : GUIMainWindow::getBoldFont() {
175 0 : return myBoldFont;
176 : }
177 :
178 : FXFont*
179 0 : GUIMainWindow::getFallbackFont() {
180 0 : return myFallbackFont;
181 : }
182 :
183 : const std::vector<GUIGlChildWindow*>&
184 6490323 : GUIMainWindow::getViews() const {
185 6490323 : return myGLWindows;
186 : }
187 :
188 :
189 : void
190 6482389 : GUIMainWindow::updateChildren(int msg) {
191 : // inform views
192 6482389 : myMDIClient->forallWindows(this, FXSEL(SEL_COMMAND, msg), nullptr);
193 : // inform other windows
194 6482389 : myTrackerLock.lock();
195 6482389 : for (int i = 0; i < (int)myTrackerWindows.size(); i++) {
196 0 : myTrackerWindows[i]->handle(this, FXSEL(SEL_COMMAND, msg), nullptr);
197 : }
198 6482389 : myTrackerLock.unlock();
199 6482389 : }
200 :
201 :
202 : FXGLVisual*
203 7582 : GUIMainWindow::getGLVisual() const {
204 7582 : return myGLVisual;
205 : }
206 :
207 :
208 : MFXStaticToolTip*
209 132552 : GUIMainWindow::getStaticTooltipMenu() const {
210 132552 : return myStaticTooltipMenu;
211 : }
212 :
213 :
214 : MFXStaticToolTip*
215 1552542 : GUIMainWindow::getStaticTooltipView() const {
216 1552542 : return myStaticTooltipView;
217 : }
218 :
219 :
220 : FXLabel*
221 497 : GUIMainWindow::getCartesianLabel() {
222 497 : return myCartesianCoordinate;
223 : }
224 :
225 :
226 : FXLabel*
227 497 : GUIMainWindow::getGeoLabel() {
228 497 : return myGeoCoordinate;
229 : }
230 :
231 :
232 : FXLabel*
233 0 : GUIMainWindow::getTestLabel() {
234 0 : return myTestCoordinate;
235 : }
236 :
237 :
238 : FXHorizontalFrame*
239 497 : GUIMainWindow::getTestFrame() {
240 497 : return myTestFrame;
241 : }
242 :
243 :
244 : bool
245 15207 : GUIMainWindow::isGaming() const {
246 15207 : return myAmGaming;
247 : }
248 :
249 :
250 : bool
251 0 : GUIMainWindow::listInternal() const {
252 0 : return myListInternal;
253 : }
254 :
255 :
256 : bool
257 0 : GUIMainWindow::listParking() const {
258 0 : return myListParking;
259 : }
260 :
261 :
262 : bool
263 0 : GUIMainWindow::listTeleporting() const {
264 0 : return myListTeleporting;
265 : }
266 :
267 :
268 : GUIMainWindow*
269 6491513 : GUIMainWindow::getInstance() {
270 6491513 : if (myInstance != nullptr) {
271 6491465 : return myInstance;
272 : }
273 96 : throw ProcessError("A GUIMainWindow instance was not yet constructed.");
274 : }
275 :
276 :
277 : GUISUMOAbstractView*
278 51 : GUIMainWindow::getActiveView() const {
279 51 : GUIGlChildWindow* w = dynamic_cast<GUIGlChildWindow*>(myMDIClient->getActiveChild());
280 51 : if (w != nullptr) {
281 51 : return w->getView();
282 : }
283 : return nullptr;
284 : }
285 :
286 :
287 : void
288 15608 : GUIMainWindow::setWindowSizeAndPos() {
289 15608 : int windowWidth = getApp()->reg().readIntEntry("SETTINGS", "width", 600);
290 15608 : int windowHeight = getApp()->reg().readIntEntry("SETTINGS", "height", 400);
291 15608 : const OptionsCont& oc = OptionsCont::getOptions();
292 31216 : if (oc.isSet("window-size")) {
293 100 : std::vector<std::string> windowSize = oc.getStringVector("window-size");
294 50 : if (windowSize.size() != 2) {
295 0 : WRITE_ERROR(TL("option window-size requires INT,INT"));
296 : } else {
297 : try {
298 50 : windowWidth = StringUtils::toInt(windowSize[0]);
299 50 : windowHeight = StringUtils::toInt(windowSize[1]);
300 0 : } catch (NumberFormatException& e) {
301 0 : WRITE_ERROR("option window-size requires INT,INT " + toString(e.what()));
302 0 : }
303 : }
304 50 : }
305 31216 : if (oc.isSet("window-size") || getApp()->reg().readIntEntry("SETTINGS", "maximized", 0) == 0 || oc.isSet("window-pos")) {
306 : // when restoring previous pos, make sure the window fits fully onto the current screen
307 15608 : int x = MAX2(0, MIN2(getApp()->reg().readIntEntry("SETTINGS", "x", 150), getApp()->getRootWindow()->getWidth() - windowWidth));
308 15608 : int y = MAX2(50, MIN2(getApp()->reg().readIntEntry("SETTINGS", "y", 150), getApp()->getRootWindow()->getHeight() - windowHeight));
309 31216 : if (oc.isSet("window-pos")) {
310 100 : std::vector<std::string> windowPos = oc.getStringVector("window-pos");
311 50 : if (windowPos.size() != 2) {
312 0 : WRITE_ERROR(TL("option window-pos requires INT,INT"));
313 : } else {
314 : try {
315 50 : x = StringUtils::toInt(windowPos[0]);
316 50 : y = StringUtils::toInt(windowPos[1]);
317 0 : } catch (NumberFormatException& e) {
318 0 : WRITE_ERROR("option window-pos requires INT,INT " + toString(e.what()));
319 0 : }
320 : }
321 50 : }
322 15608 : move(x, y);
323 15608 : resize(windowWidth, windowHeight);
324 : }
325 15608 : }
326 :
327 : void
328 8030 : GUIMainWindow::storeWindowSizeAndPos() {
329 8030 : if (!myAmFullScreen) {
330 8030 : getApp()->reg().writeIntEntry("SETTINGS", "x", getX());
331 8030 : getApp()->reg().writeIntEntry("SETTINGS", "y", getY());
332 8030 : getApp()->reg().writeIntEntry("SETTINGS", "width", getWidth());
333 8030 : getApp()->reg().writeIntEntry("SETTINGS", "height", getHeight());
334 : }
335 8030 : }
336 :
337 :
338 : void
339 8030 : GUIMainWindow::buildLanguageMenu(FXMenuBar* menuBar) {
340 8030 : myLanguageMenu = new FXMenuPane(this);
341 8030 : GUIDesigns::buildFXMenuTitle(menuBar, TL("Langua&ge"), nullptr, myLanguageMenu);
342 :
343 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "English", "", TL("Change language to english. (en)"),
344 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_EN), this, MID_LANGUAGE_EN);
345 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Deutsch", "", TL("Change language to german. (de)"),
346 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_DE), this, MID_LANGUAGE_DE);
347 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Español", "", TL("Change language to spanish. (es)"),
348 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ES), this, MID_LANGUAGE_ES);
349 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Português", "", TL("Change language to portuguese. (pt)"),
350 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_PT), this, MID_LANGUAGE_PT);
351 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Français", "", TL("Change language to french. (fr)"),
352 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_FR), this, MID_LANGUAGE_FR);
353 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Italiano", "", TL("Change language to italian. (it)"),
354 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_IT), this, MID_LANGUAGE_IT);
355 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "简体中文", "", TL("简体中文 (zh)"),
356 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZH), this, MID_LANGUAGE_ZH);
357 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "繁體中文", "", TL("繁體中文 (zh-Hant)"),
358 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZHT), this, MID_LANGUAGE_ZHT);
359 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Türkçe", "", TL("Change language to turkish. (tr)"),
360 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_TR), this, MID_LANGUAGE_TR);
361 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Magyar", "", TL("Change language to hungarian. (hu)"),
362 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_HU), this, MID_LANGUAGE_HU);
363 16060 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "日本語", "", TL("Change language to japanese. (ja)"),
364 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_JA), this, MID_LANGUAGE_JA);
365 8030 : }
366 :
367 :
368 : long
369 0 : GUIMainWindow::onCmdChangeLanguage(FXObject*, FXSelector sel, void*) {
370 : // set language
371 : std::string langID;
372 : std::string lang;
373 : // continue depending of called button
374 0 : switch (FXSELID(sel)) {
375 : case MID_LANGUAGE_DE:
376 : langID = "de";
377 0 : lang = TL("german");
378 : break;
379 : case MID_LANGUAGE_ES:
380 : langID = "es";
381 0 : lang = TL("spanish");
382 : break;
383 : case MID_LANGUAGE_PT:
384 : langID = "pt";
385 0 : lang = TL("portuguese");
386 : break;
387 : case MID_LANGUAGE_FR:
388 : langID = "fr";
389 0 : lang = TL("french");
390 : break;
391 : case MID_LANGUAGE_IT:
392 : langID = "it";
393 0 : lang = TL("italian");
394 : break;
395 : case MID_LANGUAGE_ZH:
396 : langID = "zh";
397 0 : lang = TL("simplified chinese");
398 : break;
399 : case MID_LANGUAGE_ZHT:
400 : langID = "zh-Hant";
401 0 : lang = TL("traditional chinese");
402 : break;
403 : case MID_LANGUAGE_TR:
404 : langID = "tr";
405 0 : lang = TL("turkish");
406 : break;
407 : case MID_LANGUAGE_HU:
408 : langID = "hu";
409 0 : lang = TL("hungarian");
410 : break;
411 : case MID_LANGUAGE_JA:
412 : langID = "ja";
413 0 : lang = TL("japanese");
414 : break;
415 : default:
416 : langID = "C";
417 0 : lang = TL("english");
418 : break;
419 : }
420 : // check if change language
421 0 : if (langID != gLanguage) {
422 : // update language
423 : gLanguage = langID;
424 : // show info
425 0 : WRITE_MESSAGE(TL("Language changed to ") + lang);
426 : // show dialog
427 0 : const std::string header = TL("Restart needed");
428 0 : const std::string body = TL("Changing display language needs restart to take effect.") + std::string("\n") +
429 : #ifdef DEBUG
430 : #ifdef WIN32
431 : TL("For the Debug build you might also need to set the LANG environment variable.") + std::string("\n") +
432 : #endif
433 : #endif
434 0 : TL("Under development. You can help to improve the translation at:") + std::string("\n") +
435 : "https://hosted.weblate.org/projects/eclipse-sumo/";
436 0 : FXMessageBox::information(getApp(), MBOX_OK, header.c_str(), "%s", body.c_str());
437 : // update language in registry (common for sumo and netedit)
438 0 : std::string appKey = getApp()->reg().getAppKey().text();
439 0 : if (appKey == "SUMO GUI") {
440 : // registry is written again later so we have to modify the "live" version
441 0 : getApp()->reg().writeStringEntry("gui", "language", langID.c_str());
442 : } else {
443 0 : FXRegistry reg("SUMO GUI", "sumo-gui");
444 0 : reg.read();
445 0 : reg.writeStringEntry("gui", "language", langID.c_str());
446 0 : reg.write();
447 0 : }
448 : }
449 0 : return 1;
450 : }
451 :
452 :
453 : long
454 4314897 : GUIMainWindow::onUpdChangeLanguage(FXObject* obj, FXSelector, void*) {
455 : // get language menu command
456 4314897 : FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(obj);
457 4314897 : if (menuCommand) {
458 : // check if change color
459 4314897 : if ((gLanguage == "C") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_EN))) {
460 431508 : menuCommand->setTextColor(GUIDesignTextColorBlue);
461 3883389 : } else if ((gLanguage == "de") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_DE))) {
462 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
463 3883389 : } else if ((gLanguage == "es") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ES))) {
464 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
465 3883389 : } else if ((gLanguage == "pt") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_PT))) {
466 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
467 3883389 : } else if ((gLanguage == "fr") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_FR))) {
468 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
469 3883389 : } else if ((gLanguage == "it") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_IT))) {
470 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
471 3883389 : } else if ((gLanguage == "zh") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZH))) {
472 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
473 3883389 : } else if ((gLanguage == "zh-Hant") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZHT))) {
474 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
475 3883389 : } else if ((gLanguage == "tr") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_TR))) {
476 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
477 3883389 : } else if ((gLanguage == "hu") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_HU))) {
478 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
479 : } else {
480 3883389 : menuCommand->setTextColor(GUIDesignTextColorBlack);
481 : }
482 : }
483 4314897 : return 1;
484 : }
485 :
486 :
487 : /****************************************************************************/
|