#include <config.h>
#include <string>
#include <algorithm>
#include <utils/foxtools/fxheader.h>
#ifdef WIN32
#define NOMINMAX
#endif
#include <fx3d.h>
#ifdef WIN32
#undef NOMINMAX
#endif
#include <utils/common/MsgHandler.h>
#include <utils/common/StringUtils.h>
#include <utils/foxtools/MFXImageHelper.h>
#include <utils/foxtools/MFXStaticToolTip.h>
#include <utils/gui/images/GUITexturesHelper.h>
#include <utils/gui/div/GUIDesigns.h>
#include <utils/options/OptionsCont.h>
#include "GUIMainWindow.h"
#include "GUIGlChildWindow.h"
GUIMainWindow* GUIMainWindow::myInstance = nullptr;
GUIMainWindow::GUIMainWindow(FXApp* app) :
FXMainWindow(app, "sumo-gui main window", nullptr, nullptr, DECOR_ALL, 20, 20, 600, 400),
myAmFullScreen(false),
myTrackerLock(true),
myGLVisual(new FXGLVisual(app, VISUAL_DOUBLEBUFFER)),
myAmGaming(false),
myListInternal(false),
myListParking(true),
myListTeleporting(false) {
myStaticTooltipMenu = new MFXStaticToolTip(app);
myStaticTooltipView = new MFXStaticToolTip(app);
FXFontDesc fdesc;
app->getNormalFont()->getFontDesc(fdesc);
fdesc.weight = FXFont::Bold;
GUIDesignHeight = (int)(fdesc.size / 90.0 * 18) + 5;
myBoldFont = new FXFont(app, fdesc);
myFallbackFont = new FXFont(app, "Noto Sans CJK JP");
myTopDock = new FXDockSite(this, LAYOUT_SIDE_TOP | LAYOUT_FILL_X);
myBottomDock = new FXDockSite(this, LAYOUT_SIDE_BOTTOM | LAYOUT_FILL_X);
myLeftDock = new FXDockSite(this, LAYOUT_SIDE_LEFT | LAYOUT_FILL_Y);
myRightDock = new FXDockSite(this, LAYOUT_SIDE_RIGHT | LAYOUT_FILL_Y);
if (myInstance != nullptr) {
throw ProcessError("MainWindow initialized twice");
}
myInstance = this;
}
GUIMainWindow::~GUIMainWindow() {
delete myStaticTooltipMenu;
delete myStaticTooltipView;
delete myBoldFont;
delete myFallbackFont;
delete myTopDock;
delete myBottomDock;
delete myLeftDock;
delete myRightDock;
myInstance = nullptr;
}
void
GUIMainWindow::addGLChild(GUIGlChildWindow* child) {
myGLWindows.push_back(child);
}
void
GUIMainWindow::removeGLChild(GUIGlChildWindow* child) {
std::vector<GUIGlChildWindow*>::iterator i = std::find(myGLWindows.begin(), myGLWindows.end(), child);
if (i != myGLWindows.end()) {
myGLWindows.erase(i);
}
}
void
GUIMainWindow::addChild(FXMainWindow* child) {
myTrackerLock.lock();
myTrackerWindows.push_back(child);
myTrackerLock.unlock();
}
void
GUIMainWindow::removeChild(FXMainWindow* child) {
myTrackerLock.lock();
std::vector<FXMainWindow*>::iterator i = std::find(myTrackerWindows.begin(), myTrackerWindows.end(), child);
myTrackerWindows.erase(i);
myTrackerLock.unlock();
}
FXDockSite*
GUIMainWindow::getTopDock() {
return myTopDock;
}
std::vector<std::string>
GUIMainWindow::getViewIDs() const {
std::vector<std::string> ret;
for (GUIGlChildWindow* const window : myGLWindows) {
ret.push_back(window->getTitle().text());
}
return ret;
}
GUIGlChildWindow*
GUIMainWindow::getViewByID(const std::string& id) const {
for (GUIGlChildWindow* const window : myGLWindows) {
if (std::string(window->getTitle().text()) == id) {
return window;
}
}
return nullptr;
}
void
GUIMainWindow::removeViewByID(const std::string& id) {
for (GUIGlChildWindow* const window : myGLWindows) {
if (std::string(window->getTitle().text()) == id) {
window->close();
removeGLChild(window);
return;
}
}
}
FXFont*
GUIMainWindow::getBoldFont() {
return myBoldFont;
}
FXFont*
GUIMainWindow::getFallbackFont() {
return myFallbackFont;
}
const std::vector<GUIGlChildWindow*>&
GUIMainWindow::getViews() const {
return myGLWindows;
}
void
GUIMainWindow::updateChildren(int msg) {
myMDIClient->forallWindows(this, FXSEL(SEL_COMMAND, msg), nullptr);
myTrackerLock.lock();
for (int i = 0; i < (int)myTrackerWindows.size(); i++) {
myTrackerWindows[i]->handle(this, FXSEL(SEL_COMMAND, msg), nullptr);
}
myTrackerLock.unlock();
}
FXGLVisual*
GUIMainWindow::getGLVisual() const {
return myGLVisual;
}
MFXStaticToolTip*
GUIMainWindow::getStaticTooltipMenu() const {
return myStaticTooltipMenu;
}
MFXStaticToolTip*
GUIMainWindow::getStaticTooltipView() const {
return myStaticTooltipView;
}
FXLabel*
GUIMainWindow::getCartesianLabel() {
return myCartesianCoordinate;
}
FXLabel*
GUIMainWindow::getGeoLabel() {
return myGeoCoordinate;
}
FXLabel*
GUIMainWindow::getTestLabel() {
return myTestCoordinate;
}
FXHorizontalFrame*
GUIMainWindow::getTestFrame() {
return myTestFrame;
}
bool
GUIMainWindow::isGaming() const {
return myAmGaming;
}
bool
GUIMainWindow::listInternal() const {
return myListInternal;
}
bool
GUIMainWindow::listParking() const {
return myListParking;
}
bool
GUIMainWindow::listTeleporting() const {
return myListTeleporting;
}
GUIMainWindow*
GUIMainWindow::getInstance() {
if (myInstance != nullptr) {
return myInstance;
}
throw ProcessError("A GUIMainWindow instance was not yet constructed.");
}
GUISUMOAbstractView*
GUIMainWindow::getActiveView() const {
GUIGlChildWindow* w = dynamic_cast<GUIGlChildWindow*>(myMDIClient->getActiveChild());
if (w != nullptr) {
return w->getView();
}
return nullptr;
}
void
GUIMainWindow::setWindowSizeAndPos() {
int windowWidth = getApp()->reg().readIntEntry("SETTINGS", "width", 600);
int windowHeight = getApp()->reg().readIntEntry("SETTINGS", "height", 400);
const OptionsCont& oc = OptionsCont::getOptions();
if (oc.isSet("window-size")) {
std::vector<std::string> windowSize = oc.getStringVector("window-size");
if (windowSize.size() != 2) {
WRITE_ERROR(TL("option window-size requires INT,INT"));
} else {
try {
windowWidth = StringUtils::toInt(windowSize[0]);
windowHeight = StringUtils::toInt(windowSize[1]);
} catch (NumberFormatException& e) {
WRITE_ERROR("option window-size requires INT,INT " + toString(e.what()));
}
}
}
if (oc.isSet("window-size") || getApp()->reg().readIntEntry("SETTINGS", "maximized", 0) == 0 || oc.isSet("window-pos")) {
int x = MAX2(0, MIN2(getApp()->reg().readIntEntry("SETTINGS", "x", 150), getApp()->getRootWindow()->getWidth() - windowWidth));
int y = MAX2(50, MIN2(getApp()->reg().readIntEntry("SETTINGS", "y", 150), getApp()->getRootWindow()->getHeight() - windowHeight));
if (oc.isSet("window-pos")) {
std::vector<std::string> windowPos = oc.getStringVector("window-pos");
if (windowPos.size() != 2) {
WRITE_ERROR(TL("option window-pos requires INT,INT"));
} else {
try {
x = StringUtils::toInt(windowPos[0]);
y = StringUtils::toInt(windowPos[1]);
} catch (NumberFormatException& e) {
WRITE_ERROR("option window-pos requires INT,INT " + toString(e.what()));
}
}
}
move(x, y);
resize(windowWidth, windowHeight);
}
}
void
GUIMainWindow::storeWindowSizeAndPos() {
if (!myAmFullScreen) {
getApp()->reg().writeIntEntry("SETTINGS", "x", getX());
getApp()->reg().writeIntEntry("SETTINGS", "y", getY());
getApp()->reg().writeIntEntry("SETTINGS", "width", getWidth());
getApp()->reg().writeIntEntry("SETTINGS", "height", getHeight());
}
}
void
GUIMainWindow::buildLanguageMenu(FXMenuBar* menuBar) {
myLanguageMenu = new FXMenuPane(this);
GUIDesigns::buildFXMenuTitle(menuBar, TL("Langua&ge"), nullptr, myLanguageMenu);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "English", "", TL("Change language to english. (en)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_EN), this, MID_LANGUAGE_EN);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Deutsch", "", TL("Change language to german. (de)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_DE), this, MID_LANGUAGE_DE);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Español", "", TL("Change language to spanish. (es)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ES), this, MID_LANGUAGE_ES);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Português", "", TL("Change language to portuguese. (pt)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_PT), this, MID_LANGUAGE_PT);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Français", "", TL("Change language to french. (fr)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_FR), this, MID_LANGUAGE_FR);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Italiano", "", TL("Change language to italian. (it)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_IT), this, MID_LANGUAGE_IT);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "简体中文", "", TL("简体中文 (zh)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZH), this, MID_LANGUAGE_ZH);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "繁體中文", "", TL("繁體中文 (zh-Hant)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZHT), this, MID_LANGUAGE_ZHT);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Türkçe", "", TL("Change language to turkish. (tr)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_TR), this, MID_LANGUAGE_TR);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "Magyar", "", TL("Change language to hungarian. (hu)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_HU), this, MID_LANGUAGE_HU);
GUIDesigns::buildFXMenuCommandShortcut(myLanguageMenu, "日本語", "", TL("Change language to japanese. (ja)"),
GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_JA), this, MID_LANGUAGE_JA);
}
long
GUIMainWindow::onCmdChangeLanguage(FXObject*, FXSelector sel, void*) {
std::string langID;
std::string lang;
switch (FXSELID(sel)) {
case MID_LANGUAGE_DE:
langID = "de";
lang = TL("german");
break;
case MID_LANGUAGE_ES:
langID = "es";
lang = TL("spanish");
break;
case MID_LANGUAGE_PT:
langID = "pt";
lang = TL("portuguese");
break;
case MID_LANGUAGE_FR:
langID = "fr";
lang = TL("french");
break;
case MID_LANGUAGE_IT:
langID = "it";
lang = TL("italian");
break;
case MID_LANGUAGE_ZH:
langID = "zh";
lang = TL("simplified chinese");
break;
case MID_LANGUAGE_ZHT:
langID = "zh-Hant";
lang = TL("traditional chinese");
break;
case MID_LANGUAGE_TR:
langID = "tr";
lang = TL("turkish");
break;
case MID_LANGUAGE_HU:
langID = "hu";
lang = TL("hungarian");
break;
case MID_LANGUAGE_JA:
langID = "ja";
lang = TL("japanese");
break;
default:
langID = "C";
lang = TL("english");
break;
}
if (langID != gLanguage) {
gLanguage = langID;
WRITE_MESSAGE(TL("Language changed to ") + lang);
const std::string header = TL("Restart needed");
const std::string body = TL("Changing display language needs restart to take effect.") + std::string("\n") +
#ifdef DEBUG
#ifdef WIN32
TL("For the Debug build you might also need to set the LANG environment variable.") + std::string("\n") +
#endif
#endif
TL("Under development. You can help to improve the translation at:") + std::string("\n") +
"https://hosted.weblate.org/projects/eclipse-sumo/";
FXMessageBox::information(getApp(), MBOX_OK, header.c_str(), "%s", body.c_str());
std::string appKey = getApp()->reg().getAppKey().text();
if (appKey == "SUMO GUI") {
getApp()->reg().writeStringEntry("gui", "language", langID.c_str());
} else {
FXRegistry reg("SUMO GUI", "sumo-gui");
reg.read();
reg.writeStringEntry("gui", "language", langID.c_str());
reg.write();
}
}
return 1;
}
long
GUIMainWindow::onUpdChangeLanguage(FXObject* obj, FXSelector, void*) {
FXMenuCommand* menuCommand = dynamic_cast<FXMenuCommand*>(obj);
if (menuCommand) {
if ((gLanguage == "C") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_EN))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "de") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_DE))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "es") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ES))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "pt") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_PT))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "fr") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_FR))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "it") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_IT))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "zh") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZH))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "zh-Hant") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_ZHT))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "tr") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_TR))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else if ((gLanguage == "hu") && (menuCommand->getIcon() == GUIIconSubSys::getIcon(GUIIcon::LANGUAGE_HU))) {
menuCommand->setTextColor(GUIDesignTextColorBlue);
} else {
menuCommand->setTextColor(GUIDesignTextColorBlack);
}
}
return 1;
}