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