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