Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 8789 : GUIMainWindow::GUIMainWindow(FXApp* app) :
55 : FXMainWindow(app, "sumo-gui main window", nullptr, nullptr, DECOR_ALL, 20, 20, 600, 400),
56 8789 : myAmFullScreen(false),
57 8789 : myTrackerLock(true),
58 8789 : myGLVisual(new FXGLVisual(app, VISUAL_DOUBLEBUFFER)),
59 8789 : myAmGaming(false),
60 8789 : myListInternal(false),
61 8789 : myListParking(true),
62 17578 : myListTeleporting(false) {
63 : // build static tooltips
64 8789 : myStaticTooltipMenu = new MFXStaticToolTip(app);
65 8789 : myStaticTooltipView = new MFXStaticToolTip(app);
66 : // build bold font
67 : FXFontDesc fdesc;
68 8789 : app->getNormalFont()->getFontDesc(fdesc);
69 8789 : const double uiScale = app->reg().readRealEntry("SETTINGS", "uiscale", 1);
70 8789 : fdesc.size = (FXushort)(fdesc.size * uiScale);
71 8789 : myNormalFont = new FXFont(app, fdesc);
72 8789 : app->setNormalFont(myNormalFont);
73 8789 : fdesc.weight = FXFont::Bold;
74 8789 : GUIDesignHeight = (int)(fdesc.size / 90.0 * 18) + 5;
75 8789 : myBoldFont = new FXFont(app, fdesc);
76 : // https://en.wikipedia.org/wiki/Noto_fonts should be widely available
77 8789 : myFallbackFont = new FXFont(app, "Noto Sans CJK JP");
78 : // build docks
79 8789 : myTopDock = new FXDockSite(this, LAYOUT_SIDE_TOP | LAYOUT_FILL_X);
80 8789 : myBottomDock = new FXDockSite(this, LAYOUT_SIDE_BOTTOM | LAYOUT_FILL_X);
81 8789 : myLeftDock = new FXDockSite(this, LAYOUT_SIDE_LEFT | LAYOUT_FILL_Y);
82 8789 : myRightDock = new FXDockSite(this, LAYOUT_SIDE_RIGHT | LAYOUT_FILL_Y);
83 : // avoid instance Windows twice
84 8789 : if (myInstance != nullptr) {
85 0 : throw ProcessError("MainWindow initialized twice");
86 : }
87 8789 : myInstance = this;
88 : //myGLVisual->setStencilSize(8); // enable stencil buffer
89 8789 : }
90 :
91 :
92 8761 : GUIMainWindow::~GUIMainWindow() {
93 8761 : delete myStaticTooltipMenu;
94 8761 : delete myStaticTooltipView;
95 8761 : delete myNormalFont;
96 8761 : delete myBoldFont;
97 8761 : delete myFallbackFont;
98 8761 : delete myTopDock;
99 8761 : delete myBottomDock;
100 8761 : delete myLeftDock;
101 8761 : delete myRightDock;
102 8761 : myInstance = nullptr;
103 8761 : }
104 :
105 :
106 :
107 : void
108 8309 : GUIMainWindow::addGLChild(GUIGlChildWindow* child) {
109 8309 : myGLWindows.push_back(child);
110 8309 : }
111 :
112 :
113 : void
114 8284 : GUIMainWindow::removeGLChild(GUIGlChildWindow* child) {
115 8284 : std::vector<GUIGlChildWindow*>::iterator i = std::find(myGLWindows.begin(), myGLWindows.end(), child);
116 8284 : if (i != myGLWindows.end()) {
117 8281 : myGLWindows.erase(i);
118 : }
119 8284 : }
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 12332101 : GUIMainWindow::getViews() const {
190 12332101 : return myGLWindows;
191 : }
192 :
193 :
194 : void
195 12323791 : GUIMainWindow::updateChildren(int msg) {
196 : // inform views
197 12323791 : myMDIClient->forallWindows(this, FXSEL(SEL_COMMAND, msg), nullptr);
198 : // inform other windows
199 12323791 : myTrackerLock.lock();
200 12323791 : for (int i = 0; i < (int)myTrackerWindows.size(); i++) {
201 0 : myTrackerWindows[i]->handle(this, FXSEL(SEL_COMMAND, msg), nullptr);
202 : }
203 12323791 : myTrackerLock.unlock();
204 12323791 : }
205 :
206 :
207 : FXGLVisual*
208 8309 : GUIMainWindow::getGLVisual() const {
209 8309 : return myGLVisual;
210 : }
211 :
212 :
213 : MFXStaticToolTip*
214 145548 : GUIMainWindow::getStaticTooltipMenu() const {
215 145548 : return myStaticTooltipMenu;
216 : }
217 :
218 :
219 : MFXStaticToolTip*
220 1636560 : GUIMainWindow::getStaticTooltipView() const {
221 1636560 : return myStaticTooltipView;
222 : }
223 :
224 :
225 : FXLabel*
226 473 : GUIMainWindow::getCartesianLabel() {
227 473 : return myCartesianCoordinate;
228 : }
229 :
230 :
231 : FXLabel*
232 473 : GUIMainWindow::getGeoLabel() {
233 473 : return myGeoCoordinate;
234 : }
235 :
236 :
237 : FXLabel*
238 0 : GUIMainWindow::getTestLabel() {
239 0 : return myTestCoordinate;
240 : }
241 :
242 :
243 : FXHorizontalFrame*
244 473 : GUIMainWindow::getTestFrame() {
245 473 : return myTestFrame;
246 : }
247 :
248 :
249 : bool
250 16662 : GUIMainWindow::isGaming() const {
251 16662 : 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 12333390 : GUIMainWindow::getInstance() {
275 12333390 : if (myInstance != nullptr) {
276 12333342 : 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 17094 : GUIMainWindow::setWindowSizeAndPos() {
294 17094 : int windowWidth = getApp()->reg().readIntEntry("SETTINGS", "width", 600);
295 17094 : int windowHeight = getApp()->reg().readIntEntry("SETTINGS", "height", 400);
296 17094 : const OptionsCont& oc = OptionsCont::getOptions();
297 34188 : if (oc.isSet("window-size")) {
298 104 : std::vector<std::string> windowSize = oc.getStringVector("window-size");
299 52 : if (windowSize.size() != 2) {
300 0 : WRITE_ERROR(TL("option window-size requires INT,INT"));
301 : } else {
302 : try {
303 52 : windowWidth = StringUtils::toInt(windowSize[0]);
304 52 : 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 52 : }
310 34188 : 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 17094 : int x = MAX2(0, MIN2(getApp()->reg().readIntEntry("SETTINGS", "x", 150), getApp()->getRootWindow()->getWidth() - windowWidth));
313 17094 : int y = MAX2(50, MIN2(getApp()->reg().readIntEntry("SETTINGS", "y", 150), getApp()->getRootWindow()->getHeight() - windowHeight));
314 34188 : if (oc.isSet("window-pos")) {
315 104 : std::vector<std::string> windowPos = oc.getStringVector("window-pos");
316 52 : if (windowPos.size() != 2) {
317 0 : WRITE_ERROR(TL("option window-pos requires INT,INT"));
318 : } else {
319 : try {
320 52 : x = StringUtils::toInt(windowPos[0]);
321 52 : 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 52 : }
327 17094 : move(x, y);
328 17094 : resize(windowWidth, windowHeight);
329 : }
330 17094 : }
331 :
332 : void
333 8789 : GUIMainWindow::storeWindowSizeAndPos() {
334 8789 : if (!myAmFullScreen) {
335 8789 : getApp()->reg().writeIntEntry("SETTINGS", "x", getX());
336 8789 : getApp()->reg().writeIntEntry("SETTINGS", "y", getY());
337 8789 : getApp()->reg().writeIntEntry("SETTINGS", "width", getWidth());
338 8789 : getApp()->reg().writeIntEntry("SETTINGS", "height", getHeight());
339 : }
340 8789 : }
341 :
342 :
343 : void
344 8789 : GUIMainWindow::buildLanguageMenu(FXMenuBar* menuBar) {
345 8789 : myLanguageMenu = new FXMenuPane(this);
346 8789 : GUIDesigns::buildFXMenuTitle(menuBar, TL("Langua&ge"), nullptr, myLanguageMenu);
347 :
348 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "English", "", TL("Change language to english. (en)"),
349 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_EN), this, MID_LANGUAGE_EN);
350 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Deutsch", "", TL("Change language to german. (de)"),
351 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_DE), this, MID_LANGUAGE_DE);
352 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Español", "", TL("Change language to spanish. (es)"),
353 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ES), this, MID_LANGUAGE_ES);
354 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Português", "", TL("Change language to portuguese. (pt)"),
355 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_PT), this, MID_LANGUAGE_PT);
356 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Français", "", TL("Change language to french. (fr)"),
357 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_FR), this, MID_LANGUAGE_FR);
358 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Italiano", "", TL("Change language to italian. (it)"),
359 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_IT), this, MID_LANGUAGE_IT);
360 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "简体中文", "", TL("简体中文 (zh)"),
361 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZH), this, MID_LANGUAGE_ZH);
362 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "繁體中文", "", TL("繁體中文 (zh-Hant)"),
363 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZHT), this, MID_LANGUAGE_ZHT);
364 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Türkçe", "", TL("Change language to turkish. (tr)"),
365 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_TR), this, MID_LANGUAGE_TR);
366 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "日本語", "", TL("Change language to japanese. (ja)"),
367 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_JA), this, MID_LANGUAGE_JA);
368 17578 : GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "한국어", "", TL("Change language to korean. (ko)"),
369 : GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_KO), this, MID_LANGUAGE_KO);
370 8789 : }
371 :
372 :
373 : long
374 0 : GUIMainWindow::onCmdChangeLanguage(FXObject*, FXSelector sel, void*) {
375 : // set language
376 : std::string langID;
377 : std::string lang;
378 : // continue depending of called button
379 0 : switch (FXSELID(sel)) {
380 : case MID_LANGUAGE_DE:
381 : langID = "de";
382 0 : lang = TL("german");
383 : break;
384 : case MID_LANGUAGE_ES:
385 : langID = "es";
386 0 : lang = TL("spanish");
387 : break;
388 : case MID_LANGUAGE_PT:
389 : langID = "pt";
390 0 : lang = TL("portuguese");
391 : break;
392 : case MID_LANGUAGE_FR:
393 : langID = "fr";
394 0 : lang = TL("french");
395 : break;
396 : case MID_LANGUAGE_IT:
397 : langID = "it";
398 0 : lang = TL("italian");
399 : break;
400 : case MID_LANGUAGE_ZH:
401 : langID = "zh";
402 0 : lang = TL("simplified chinese");
403 : break;
404 : case MID_LANGUAGE_ZHT:
405 : langID = "zh-Hant";
406 0 : lang = TL("traditional chinese");
407 : break;
408 : case MID_LANGUAGE_TR:
409 : langID = "tr";
410 0 : lang = TL("turkish");
411 : break;
412 : case MID_LANGUAGE_JA:
413 : langID = "ja";
414 0 : lang = TL("japanese");
415 : break;
416 : case MID_LANGUAGE_KO:
417 : langID = "ko";
418 0 : lang = TL("korean");
419 : break;
420 : default:
421 : langID = "C";
422 0 : lang = TL("english");
423 : break;
424 : }
425 : // check if change language
426 0 : if (langID != gLanguage) {
427 : // update language
428 : gLanguage = langID;
429 : // show info
430 0 : WRITE_MESSAGE(TL("Language changed to ") + lang);
431 : // show dialog
432 0 : const std::string header = TL("Restart needed");
433 0 : const std::string body = TL("Changing display language needs restart to take effect.") + std::string("\n") +
434 : #ifdef DEBUG
435 : #ifdef WIN32
436 : TL("For the Debug build you might also need to set the LANG environment variable.") + std::string("\n") +
437 : #endif
438 : #endif
439 0 : TL("Under development. You can help to improve the translation at:") + std::string("\n") +
440 : "https://hosted.weblate.org/projects/eclipse-sumo/";
441 0 : FXMessageBox::information(getApp(), MBOX_OK, header.c_str(), "%s", body.c_str());
442 : // update language in registry (common for sumo and netedit)
443 0 : std::string appKey = getApp()->reg().getAppKey().text();
444 0 : if (appKey == "SUMO GUI") {
445 : // registry is written again later so we have to modify the "live" version
446 0 : getApp()->reg().writeStringEntry("gui", "language", langID.c_str());
447 : } else {
448 0 : FXRegistry reg("SUMO GUI", "sumo-gui");
449 0 : reg.read();
450 0 : reg.writeStringEntry("gui", "language", langID.c_str());
451 0 : reg.write();
452 0 : }
453 : }
454 0 : return 1;
455 : }
456 :
457 :
458 : long
459 8827574 : GUIMainWindow::onUpdChangeLanguage(FXObject* obj, FXSelector, void*) {
460 : // get language menu command
461 8827574 : FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(obj);
462 8827574 : if (menuCommand) {
463 : // check if change color
464 8827574 : if ((gLanguage == "C") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_EN))) {
465 882782 : menuCommand->setTextColor(GUIDesignTextColorBlue);
466 7944792 : } else if ((gLanguage == "de") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_DE))) {
467 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
468 7944792 : } else if ((gLanguage == "es") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ES))) {
469 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
470 7944792 : } else if ((gLanguage == "pt") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_PT))) {
471 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
472 7944792 : } else if ((gLanguage == "fr") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_FR))) {
473 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
474 7944792 : } else if ((gLanguage == "it") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_IT))) {
475 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
476 7944792 : } else if ((gLanguage == "zh") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZH))) {
477 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
478 7944792 : } else if ((gLanguage == "zh-Hant") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZHT))) {
479 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
480 7944792 : } else if ((gLanguage == "tr") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_TR))) {
481 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
482 7944792 : } else if ((gLanguage == "ja") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_JA))) {
483 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
484 7944792 : } else if ((gLanguage == "ko") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_KO))) {
485 0 : menuCommand->setTextColor(GUIDesignTextColorBlue);
486 : } else {
487 7944792 : menuCommand->setTextColor(GUIDesignTextColorBlack);
488 : }
489 : }
490 8827574 : return 1;
491 : }
492 :
493 :
494 : /****************************************************************************/
|