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