Path: blob/main/src/netedit/dialogs/file/GNEFileSelector.cpp
193674 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNEFileSelector.cpp14/// @author Pablo Alvarez Lopez15/// @date Aug 202516///17// widget used for file selection18/****************************************************************************/1920#include <fxkeys.h>21#include <netedit/dialogs/basic/GNEErrorBasicDialog.h>22#include <netedit/dialogs/basic/GNEQuestionBasicDialog.h>23#include <netedit/GNEApplicationWindow.h>24#include <utils/common/StringTokenizer.h>25#include <utils/foxtools/MFXTextFieldIcon.h>26#include <utils/foxtools/MFXToggleButtonTooltip.h>27#include <utils/gui/div/GUIDesigns.h>28#include <utils/gui/div/GUIIOGlobals.h>2930#include "GNEFilePathDialog.h"31#include "GNEFileSelector.h"3233#define FILELISTMASK (ICONLIST_EXTENDEDSELECT|ICONLIST_SINGLESELECT|ICONLIST_BROWSESELECT|ICONLIST_MULTIPLESELECT)34#define FILESTYLEMASK (ICONLIST_DETAILED|ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS|ICONLIST_ROWS|ICONLIST_COLUMNS|ICONLIST_AUTOSIZE)3536// ===========================================================================37// FOX callback mapping38// ===========================================================================3940// Map41FXDEFMAP(GNEFileSelector) GNEFileSelectorMap[] = {42// interaction43FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_ACCEPT, GNEFileSelector::onCmdAccept),44FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_FILEFILTER, GNEFileSelector::onCmdFilter),45FXMAPFUNC(SEL_DOUBLECLICKED, FXFileSelector::ID_FILELIST, GNEFileSelector::onCmdItemDoubleClicked),46FXMAPFUNC(SEL_SELECTED, FXFileSelector::ID_FILELIST, GNEFileSelector::onCmdItemSelected),47FXMAPFUNC(SEL_DESELECTED, FXFileSelector::ID_FILELIST, GNEFileSelector::onCmdItemDeselected),48FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, FXFileSelector::ID_FILELIST, GNEFileSelector::onPopupMenu),49FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_DIRECTORY_UP, GNEFileSelector::onCmdDirectoryUp),50FXMAPFUNC(SEL_UPDATE, FXFileSelector::ID_DIRECTORY_UP, GNEFileSelector::onUpdDirectoryUp),51FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_DIRTREE, GNEFileSelector::onCmdDirTree),52FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_HOME, GNEFileSelector::onCmdHomeFolder),53FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_WORK, GNEFileSelector::onCmdWorkFolder),54FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_VISIT, GNEFileSelector::onCmdVisit),55FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_BOOKMARK, GNEFileSelector::onCmdBookmark),56FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_NEW, GNEFileSelector::onCmdNewFolder),57FXMAPFUNC(SEL_UPDATE, FXFileSelector::ID_NEW, GNEFileSelector::onUpdNewFolder),58FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_DELETE, GNEFileSelector::onCmdDelete),59FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_MOVE, GNEFileSelector::onCmdMove),60FXMAPFUNC(SEL_COMMAND, FXFileSelector::ID_COPY, GNEFileSelector::onCmdCopy),61FXMAPFUNC(SEL_UPDATE, FXFileSelector::ID_COPY, GNEFileSelector::onUpdSelected),62FXMAPFUNC(SEL_UPDATE, FXFileSelector::ID_MOVE, GNEFileSelector::onUpdSelected),63FXMAPFUNC(SEL_UPDATE, FXFileSelector::ID_DELETE, GNEFileSelector::onUpdSelected),64FXMAPFUNCS(SEL_COMMAND, FXFileSelector::ID_NORMAL_SIZE, FXFileSelector::ID_GIANT_SIZE, GNEFileSelector::onCmdImageSize),65FXMAPFUNCS(SEL_UPDATE, FXFileSelector::ID_NORMAL_SIZE, FXFileSelector::ID_GIANT_SIZE, GNEFileSelector::onUpdImageSize),66FXMAPFUNC(SEL_KEYPRESS, 0, GNEFileSelector::onKeyPress),67FXMAPFUNC(SEL_COMMAND, MID_GNE_BUTTON_CONFIG, GNEFileSelector::onCmdConfigFolder),68};6970// Implementation71FXIMPLEMENT(GNEFileSelector, FXVerticalFrame, GNEFileSelectorMap, ARRAYNUMBER(GNEFileSelectorMap))7273// ===========================================================================74// member method definitions75// ===========================================================================7677GNEFileSelector::GNEFileSelector(GNEFileDialog* fileDialog, const std::vector<std::string>& extensions,78GNEFileDialog::OpenMode openMode, GNEFileDialog::ConfigType configType):79FXVerticalFrame(fileDialog->getContentFrame(), GUIDesignAuxiliarFrame),80myFileDialog(fileDialog),81myOpenMode(openMode),82myConfigType(configType),83myBookmarksRecentFiles(fileDialog->getApplicationWindow()->getApp(), TL("Visited Directories")) {84// get static tooltip85const auto tooltipMenu = fileDialog->getApplicationWindow()->getStaticTooltipMenu();86// create horizontal frame for top buttons87auto navigatorHorizontalFrame = new FXHorizontalFrame(this, GUIDesignDialogContentHorizontalFrame);88// create two horizontal frame for file selector89auto externFileboxframe = new FXHorizontalFrame(this, GUIDesignDialogContentFrame);90auto fileboxframe = new FXHorizontalFrame(externFileboxframe, GUIDesignFileBoxFrame);91// horizontal frame for filename92auto filenameHorizontalFrame = new FXHorizontalFrame(this, GUIDesignDialogContentHorizontalFrame);93// first create file selector94myFileSelector = new FXFileList(fileboxframe, this, FXFileSelector::ID_FILELIST, GUIDesignFileList);95// label for directory96new FXLabel(navigatorHorizontalFrame, TL("Directory:"), nullptr, GUIDesignLabelFixed(100));97// create directory box98myDirBox = new FXDirBox(navigatorHorizontalFrame, this, FXFileSelector::ID_DIRTREE, GUIDesignDirBox);99myDirBox->setNumVisible(5);100myDirBox->setAssociations(myFileSelector->getAssociations());101// build bookmark menu pane102buildBookmarkMenuPane(navigatorHorizontalFrame);103// build buttons104buildButtons(navigatorHorizontalFrame, tooltipMenu);105// create label for filename106new FXLabel(filenameHorizontalFrame,107TL("File Name:"),108nullptr, GUIDesignLabelFixed(100));109// create filename text field and set focus110myFilenameTextField = new MFXTextFieldIcon(filenameHorizontalFrame, tooltipMenu, GUIIcon::EMPTY,111this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextFieldFileDialog);112// create comboBox for file filter113myFileFilterComboBox = new MFXComboBoxIcon(filenameHorizontalFrame, tooltipMenu, false, GUIDesignComboBoxVisibleItems,114this, FXFileSelector::ID_FILEFILTER, GUIDesignComboBoxFileDialog);115// build shortcuts116buildShortcuts();117// continue depending of open mode118switch (myOpenMode) {119case GNEFileDialog::OpenMode::SAVE:120myFileSelector->showOnlyDirectories(FALSE);121myFileSelector->setListStyle((myFileSelector->getListStyle() & ~FILELISTMASK) | ICONLIST_BROWSESELECT);122break;123case GNEFileDialog::OpenMode::LOAD_SINGLE:124myFileSelector->showOnlyDirectories(FALSE);125myFileSelector->setListStyle((myFileSelector->getListStyle() & ~FILELISTMASK) | ICONLIST_BROWSESELECT);126break;127case GNEFileDialog::OpenMode::LOAD_MULTIPLE:128myFileSelector->showOnlyDirectories(FALSE);129myFileSelector->setListStyle((myFileSelector->getListStyle() & ~FILELISTMASK) | ICONLIST_EXTENDEDSELECT);130break;131case GNEFileDialog::OpenMode::LOAD_DIRECTORY:132myFileSelector->showOnlyDirectories(TRUE);133myFileSelector->setListStyle((myFileSelector->getListStyle() & ~FILELISTMASK) | ICONLIST_BROWSESELECT);134break;135default:136throw ProcessError("Invalid open mode");137}138// set directory139if (gCurrentFolder.length() != 0) {140setDirectory(gCurrentFolder);141} else {142setDirectory(FXSystem::getCurrentDirectory());143}144// set extensions145if (extensions.size() == 0) {146throw ProcessError("At least one extension is needed");147} else {148for (const auto& extension : extensions) {149myFileFilterComboBox->appendIconItem(extension.c_str());150}151}152myFileFilterComboBox->setNumVisible(FXMIN((int)extensions.size(), 12));153// parse extensions154parseExtensions(extensions);155// apply first filter156myFileSelector->setPattern(FXFileSelector::patternFromText(extensions.front().c_str()));157}158159160GNEFileSelector::~GNEFileSelector() {161FXAccelTable* table = getShell()->getAccelTable();162if (table) {163table->removeAccel(MKUINT(KEY_BackSpace, 0));164table->removeAccel(MKUINT(KEY_Delete, 0));165table->removeAccel(MKUINT(KEY_h, CONTROLMASK));166table->removeAccel(MKUINT(KEY_w, CONTROLMASK));167table->removeAccel(MKUINT(KEY_n, CONTROLMASK));168table->removeAccel(MKUINT(KEY_a, CONTROLMASK));169table->removeAccel(MKUINT(KEY_b, CONTROLMASK));170table->removeAccel(MKUINT(KEY_s, CONTROLMASK));171table->removeAccel(MKUINT(KEY_l, CONTROLMASK));172}173delete myBookmarkMenuPane;174}175176177void178GNEFileSelector::setPath(const std::string& path) {179myFilenameTextField->setText(path.c_str(), TRUE);180}181182183void184GNEFileSelector::setFilter(const int index) {185myFileFilterComboBox->setCurrentItem(index, TRUE);186}187188189long190GNEFileSelector::onUpdNewFolder(FXObject* sender, FXSelector, void*) {191// check if directory is writable192const bool writable = FXStat::isWritable(myFileSelector->getDirectory());193return sender->handle(this, writable ? FXSEL(SEL_COMMAND, ID_ENABLE) : FXSEL(SEL_COMMAND, ID_DISABLE), NULL);194}195196197std::vector<std::string>198GNEFileSelector::getSelectedFiles() const {199std::vector<std::string> files;200for (int i = 0; i < myFileSelector->getNumItems(); i++) {201if (myFileSelector->isItemSelected(i) && (myFileSelector->getItemFilename(i) != "..") && (myFileSelector->getItemFilename(i) != ".")) {202files.push_back(myFileSelector->getItemPathname(i).text());203}204}205return files;206}207208209std::vector<std::string>210GNEFileSelector::getSelectedFilesOnly() const {211std::vector<std::string> files;212for (int i = 0; i < myFileSelector->getNumItems(); i++) {213if (myFileSelector->isItemSelected(i) && !myFileSelector->isItemDirectory(i)) {214files.push_back(myFileSelector->getItemPathname(i).text());215}216}217return files;218}219220221long222GNEFileSelector::onCmdCopy(FXObject*, FXSelector, void*) {223auto GNEApp = myFileDialog->getApplicationWindow();224const auto filenameList = getSelectedFiles();225// first check if we have files to copy226if (filenameList.size() > 0) {227// get only first filename228const std::string originFilePath = filenameList.front();229// create default destiny filename230std::string destinyFilename = FXPath::absolute(FXPath::directory(originFilePath.c_str()), "CopyOf" + FXPath::name(originFilePath.c_str())).text();231// create file path dialog232const auto filePathDialog = new GNEFilePathDialog(GNEApp, myFileDialog, TL("Copy File"), TL("Select destination file"), destinyFilename);233// continue depending of filePathDialog results234if (filePathDialog->getResult() == GNEDialog::Result::ACCEPT) {235// get destiny filename from dialog236destinyFilename = filePathDialog->getFilePath();237// check if we selected the same file238if (FXFile::identical(originFilePath.c_str(), destinyFilename.c_str())) {239// open error dialog240GNEErrorBasicDialog(GNEApp, myFileDialog, TL("Error copying file"),241TLF("Unable to copy file: %", destinyFilename),242TL("The source and destination files are the same"));243} else {244// check if file exist245if (FXStat::exists(destinyFilename.c_str())) {246// open question dialog247const GNEQuestionBasicDialog overwriteDialog(GNEApp, myFileDialog, GNEDialog::Buttons::YES_NO,248TL("Overwrite file"), TLF("The destination file: %", destinyFilename),249TL("already exist. Overwrite?"));250// check if abort251if (overwriteDialog.getResult() != GNEDialog::Result::ACCEPT) {252return 1;253}254}255// try to copy overwritten256if (!FXFile::copyFiles(originFilePath.c_str(), destinyFilename.c_str(), TRUE)) {257// open error dialog258GNEErrorBasicDialog(GNEApp, myFileDialog, TL("Error copying file"),259TLF("Unable to copy file: %", destinyFilename),260TL("Check destination file permissions"));261}262}263}264}265return 1;266}267268269long270GNEFileSelector::onCmdMove(FXObject*, FXSelector, void*) {271auto GNEApp = myFileDialog->getApplicationWindow();272const auto filenameList = getSelectedFiles();273// first check if we have files to move274if (filenameList.size() > 0) {275// get only first filename276const std::string originFilePath = filenameList.front();277// create file path dialog278const auto filePathDialog = new GNEFilePathDialog(GNEApp, myFileDialog, TL("Move File"), TL("Select destination file"), originFilePath);279// continue depending of filePathDialog results280if (filePathDialog->getResult() == GNEDialog::Result::ACCEPT) {281// get destiny filename from dialog282const std::string destinyFilename = filePathDialog->getFilePath();283// check if we selected the same file284if (FXFile::identical(originFilePath.c_str(), destinyFilename.c_str())) {285// open error dialog286GNEErrorBasicDialog(GNEApp, myFileDialog, TL("Error moving file"),287TLF("Unable to move file:\n%\n", destinyFilename),288TL("The source and destination files are the same"));289} else {290// check if file exist291if (FXStat::exists(destinyFilename.c_str())) {292// open question dialog293const GNEQuestionBasicDialog overwriteDialog(GNEApp, myFileDialog, GNEDialog::Buttons::YES_NO,294TL("Overwrite file"), TLF("The destination file: %", destinyFilename),295TL("already exist. Overwrite?"));296// check if abort297if (overwriteDialog.getResult() != GNEDialog::Result::ACCEPT) {298return 1;299}300}301// try to move overwritten302if (!FXFile::moveFiles(originFilePath.c_str(), destinyFilename.c_str(), TRUE)) {303// open error dialog304GNEErrorBasicDialog(GNEApp, myFileDialog, TL("Error moving file"),305TLF("Unable to move file:\n%\n", destinyFilename),306TL("Check destination file permissions"));307}308}309}310}311return 1;312}313314315long316GNEFileSelector::onCmdDelete(FXObject*, FXSelector, void*) {317auto GNEApp = myFileDialog->getApplicationWindow();318const auto filenameList = getSelectedFiles();319// first check if we have files to link320if (filenameList.size() > 0) {321// get only first filename322const std::string fileToDelete = filenameList.front();323// open question dialog324const GNEQuestionBasicDialog askDialog(GNEApp, myFileDialog, GNEDialog::Buttons::YES_NO,325TL("Deleting file"), TL("Are you sure you want to delete the file:"),326fileToDelete);327// check if continue328if (askDialog.getResult() == GNEDialog::Result::ACCEPT) {329// try to remove it330if (!FXFile::removeFiles(fileToDelete.c_str(), TRUE)) {331// open error dialog332GNEErrorBasicDialog(GNEApp, myFileDialog, TL("Error deleting file"),333TLF("Unable to delete file: %", fileToDelete),334TL("Check file permissions"));335}336}337}338return 1;339}340341342long343GNEFileSelector::onUpdSelected(FXObject* sender, FXSelector, void*) {344for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {345if (myFileSelector->isItemSelected(i)) {346sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), NULL);347return 1;348}349}350sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), NULL);351return 1;352}353354355long356GNEFileSelector::onCmdImageSize(FXObject*, FXSelector sel, void*) {357switch (FXSELID(sel)) {358case FXFileSelector::ID_NORMAL_SIZE:359setImageSize(32);360break;361case FXFileSelector::ID_MEDIUM_SIZE:362setImageSize(48);363break;364case FXFileSelector::ID_GIANT_SIZE:365setImageSize(64);366break;367default:368setImageSize(32);369break;370}371return 1;372}373374375long376GNEFileSelector::onUpdImageSize(FXObject* sender, FXSelector sel, void*) {377FXbool check = FALSE;378switch (FXSELID(sel)) {379case FXFileSelector::ID_NORMAL_SIZE:380check = (getImageSize() == 32);381break;382case FXFileSelector::ID_MEDIUM_SIZE:383check = (getImageSize() == 48);384break;385case FXFileSelector::ID_GIANT_SIZE:386check = (getImageSize() == 64);387break;388default:389check = (getImageSize() == 32);390break;391}392sender->handle(this, check ? FXSEL(SEL_COMMAND, ID_CHECK) : FXSEL(SEL_COMMAND, ID_UNCHECK), NULL);393return 1;394}395396397long398GNEFileSelector::onKeyPress(FXObject* obj, FXSelector sel, void* ptr) {399// if ESC key is pressed, close dialog aborting400FXEvent* event = (FXEvent*)ptr;401if (event->code == KEY_Return) {402return onCmdAccept(obj, sel, ptr);403} else {404return FXVerticalFrame::onKeyPress(obj, sel, ptr);405}406}407408409long410GNEFileSelector::onPopupMenu(FXObject*, FXSelector, void* ptr) {411FXEvent* event = (FXEvent*)ptr;412if (event->moved) {413return 1;414}415416FXMenuPane filemenu(this);417new FXMenuCommand(&filemenu, TL("Up one level"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_DIRUP_ICON), this, FXFileSelector::ID_DIRECTORY_UP);418new FXMenuCommand(&filemenu, TL("Home directory"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_HOME), this, FXFileSelector::ID_HOME);419new FXMenuCommand(&filemenu, TL("Work directory"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_WORK), this, FXFileSelector::ID_WORK);420auto configDirectory = new FXMenuCommand(&filemenu, TL("Config directory"), nullptr, this, MID_GNE_BUTTON_CONFIG);421// disable if configuration file is empty422if (OptionsCont::getOptions().getString("configuration-file").empty()) {423configDirectory->disable();424}425// set icon and tip depending of config type426if (myConfigType == GNEFileDialog::ConfigType::NETEDIT) {427configDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::NETEDIT_MINI));428} else {429configDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::SUMO_MINI));430}431new FXMenuCommand(&filemenu, TL("Select all"), NULL, myFileSelector, FXFileList::ID_SELECT_ALL);432new FXMenuSeparator(&filemenu);433434FXMenuPane sortmenu(this);435new FXMenuCascade(&filemenu, TL("Sort by"), NULL, &sortmenu);436new FXMenuRadio(&sortmenu, TL("Name"), myFileSelector, FXFileList::ID_SORT_BY_NAME);437new FXMenuRadio(&sortmenu, TL("Type"), myFileSelector, FXFileList::ID_SORT_BY_TYPE);438new FXMenuRadio(&sortmenu, TL("Size"), myFileSelector, FXFileList::ID_SORT_BY_SIZE);439new FXMenuRadio(&sortmenu, TL("Time"), myFileSelector, FXFileList::ID_SORT_BY_TIME);440new FXMenuRadio(&sortmenu, TL("User"), myFileSelector, FXFileList::ID_SORT_BY_USER);441new FXMenuRadio(&sortmenu, TL("Group"), myFileSelector, FXFileList::ID_SORT_BY_GROUP);442new FXMenuSeparator(&sortmenu);443new FXMenuCheck(&sortmenu, TL("Reverse"), myFileSelector, FXFileList::ID_SORT_REVERSE);444new FXMenuCheck(&sortmenu, TL("Ignore case"), myFileSelector, FXFileList::ID_SORT_CASE);445446FXMenuPane viewmenu(this);447new FXMenuCascade(&filemenu, TL("View"), NULL, &viewmenu);448new FXMenuRadio(&viewmenu, TL("Small icons"), myFileSelector, FXFileList::ID_SHOW_MINI_ICONS);449new FXMenuRadio(&viewmenu, TL("Big icons"), myFileSelector, FXFileList::ID_SHOW_BIG_ICONS);450new FXMenuRadio(&viewmenu, TL("Details"), myFileSelector, FXFileList::ID_SHOW_DETAILS);451new FXMenuSeparator(&viewmenu);452new FXMenuRadio(&viewmenu, TL("Rows"), myFileSelector, FXFileList::ID_ARRANGE_BY_ROWS);453new FXMenuRadio(&viewmenu, TL("Columns"), myFileSelector, FXFileList::ID_ARRANGE_BY_COLUMNS);454new FXMenuSeparator(&viewmenu);455new FXMenuCheck(&viewmenu, TL("Hidden files"), myFileSelector, FXFileList::ID_TOGGLE_HIDDEN);456new FXMenuCheck(&viewmenu, TL("Preview images"), myFileSelector, FXFileList::ID_TOGGLE_IMAGES);457new FXMenuSeparator(&viewmenu);458new FXMenuRadio(&viewmenu, TL("Normal images"), this, FXFileSelector::ID_NORMAL_SIZE);459new FXMenuRadio(&viewmenu, TL("Medium images"), this, FXFileSelector::ID_MEDIUM_SIZE);460new FXMenuRadio(&viewmenu, TL("Giant images"), this, FXFileSelector::ID_GIANT_SIZE);461462FXMenuPane bookmenu(this);463FXMenuCascade* bookcasc = new FXMenuCascade(&filemenu, TL("Bookmarks"), NULL, &bookmenu);464bookcasc->setTarget(this);465bookcasc->setSelector(FXFileSelector::ID_BOOKMENU);466new FXMenuCommand(&bookmenu, TL("Set bookmark"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_SET), this, FXFileSelector::ID_BOOKMARK);467new FXMenuCommand(&bookmenu, TL("Clear bookmarks"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_CLR), &myBookmarksRecentFiles, FXRecentFiles::ID_CLEAR);468FXMenuSeparator* sep1 = new FXMenuSeparator(&bookmenu);469sep1->setTarget(&myBookmarksRecentFiles);470sep1->setSelector(FXRecentFiles::ID_ANYFILES);471new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_1);472new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_2);473new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_3);474new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_4);475new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_5);476new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_6);477new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_7);478new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_8);479new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_9);480new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_10);481482new FXMenuSeparator(&filemenu);483new FXMenuCommand(&filemenu, TL("New directory..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FOLDER_NEW), this, FXFileSelector::ID_NEW);484new FXMenuCommand(&filemenu, TL("Copy..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_COPY), this, FXFileSelector::ID_COPY);485new FXMenuCommand(&filemenu, TL("Move..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_MOVE), this, FXFileSelector::ID_MOVE);486// disabled linker because it doesn't work487new FXMenuCommand(&filemenu, TL("Delete..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_DELETE), this, FXFileSelector::ID_DELETE);488489filemenu.create();490filemenu.popup(NULL, event->root_x, event->root_y);491getApp()->runModalWhileShown(&filemenu);492return 1;493}494495496long497GNEFileSelector::onCmdFilter(FXObject*, FXSelector, void* ptr) {498const FXString pat = FXFileSelector::patternFromText((FXchar*)ptr);499myFileSelector->setPattern(pat);500if (myOpenMode == GNEFileDialog::OpenMode::SAVE) {501FXString ext = FXFileSelector::extensionFromPattern(pat);502if (!ext.empty()) {503FXString name = FXPath::stripExtension(myFilenameTextField->getText());504if (!name.empty()) {505myFilenameTextField->setText(name + "." + ext);506}507}508}509return 1;510}511512513void514GNEFileSelector::setDirectory(const FXString& path) {515const FXString abspath = FXPath::absolute(path);516myFileSelector->setDirectory(abspath);517myDirBox->setDirectory(abspath);518if (myOpenMode != GNEFileDialog::OpenMode::SAVE) {519myFilenameTextField->setText(FXString::null);520}521}522523524void525GNEFileSelector::setFilename(const FXString& path) {526const FXString fullname(FXPath::absolute(path));527FXString name(FXPath::name(fullname));528myFileSelector->setCurrentFile(fullname);529myDirBox->setDirectory(myFileSelector->getDirectory());530myFilenameTextField->setText(name);531}532533534std::string535GNEFileSelector::getFilename() const {536// continue depending if we write something in the text field537if (myFilenameTextField->getText().empty()) {538for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {539if (myFileSelector->isItemSelected(i) &&540!myFileSelector->isItemDirectory(i)) {541return FXPath::absolute(myFileSelector->getDirectory(), myFileSelector->getItemFilename(i)).text();542}543}544return "";545} else {546return FXPath::absolute(myFileSelector->getDirectory(), myFilenameTextField->getText()).text();547}548}549550551std::string552GNEFileSelector::getDirectory() const {553// continue depending if we write something in the text field554if (myFilenameTextField->getText().empty()) {555for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {556if (myFileSelector->isItemSelected(i) &&557myFileSelector->isItemDirectory(i)) {558return FXPath::absolute(myFileSelector->getDirectory(), myFileSelector->getItemFilename(i)).text();559}560}561} else if (FXStat::isDirectory(myFilenameTextField->getText())) {562return myFilenameTextField->getText().text();563} else {564for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {565if (myFileSelector->isItemDirectory(i) &&566(myFileSelector->getItemFilename(i) == myFilenameTextField->getText())) {567return FXPath::absolute(myFileSelector->getDirectory(), myFileSelector->getItemFilename(i)).text();568}569}570}571return "";572}573574575std::vector<std::string>576GNEFileSelector::getFilenames() const {577return getSelectedFilesOnly();578}579580581std::string582GNEFileSelector::getCurrentDirectory() const {583return myFileSelector->getDirectory().text();584}585586587const std::vector<std::string>&588GNEFileSelector::getFileExtension() const {589return myExtensions.at(myFileFilterComboBox->getCurrentItem());590}591592593void594GNEFileSelector::setItemSpace(FXint s) {595myFileSelector->setItemSpace(s);596}597598599FXint600GNEFileSelector::getItemSpace() const {601return myFileSelector->getItemSpace();602}603604605void606GNEFileSelector::setFileBoxStyle(FXuint style) {607myFileSelector->setListStyle((myFileSelector->getListStyle() & ~FILESTYLEMASK) | (style & FILESTYLEMASK));608}609610611FXuint612GNEFileSelector::getFileBoxStyle() const {613return myFileSelector->getListStyle()&FILESTYLEMASK;614}615616617void618GNEFileSelector::setMatchMode(FXuint mode) {619myFileSelector->setMatchMode(mode);620}621622623FXuint624GNEFileSelector::getMatchMode() const {625return myFileSelector->getMatchMode();626}627628629FXbool630GNEFileSelector::showHiddenFiles() const {631return myFileSelector->showHiddenFiles();632}633634635void636GNEFileSelector::showHiddenFiles(FXbool showing) {637myFileSelector->showHiddenFiles(showing);638}639640641FXbool642GNEFileSelector::showImages() const {643return myFileSelector->showImages();644}645646647void648GNEFileSelector::showImages(FXbool showing) {649myFileSelector->showImages(showing);650}651652653FXint654GNEFileSelector::getImageSize() const {655return myFileSelector->getImageSize();656}657658659void660GNEFileSelector::setImageSize(FXint size) {661myFileSelector->setImageSize(size);662}663664665MFXTextFieldIcon*666GNEFileSelector::getFilenameTextField() const {667return myFilenameTextField;668}669670671long672GNEFileSelector::onCmdItemSelected(FXObject*, FXSelector, void* ptr) {673const FXint index = (FXint)(FXival)ptr;674FXString text, file;675if (myOpenMode == GNEFileDialog::OpenMode::LOAD_MULTIPLE) {676for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {677if (myFileSelector->isItemSelected(i) && !myFileSelector->isItemDirectory(i)) {678if (!text.empty()) {679text += ' ';680}681text += "\"" + myFileSelector->getItemFilename(i) + "\"";682}683}684myFilenameTextField->setText(text);685} else if (myOpenMode == GNEFileDialog::OpenMode::LOAD_DIRECTORY) {686if (myFileSelector->isItemDirectory(index)) {687text = myFileSelector->getItemFilename(index);688myFilenameTextField->setText(text);689}690} else {691if (!myFileSelector->isItemDirectory(index)) {692text = myFileSelector->getItemFilename(index);693myFilenameTextField->setText(text);694}695}696return 1;697}698699700long701GNEFileSelector::onCmdItemDeselected(FXObject*, FXSelector, void*) {702FXString text, file;703if (myOpenMode == GNEFileDialog::OpenMode::LOAD_MULTIPLE) {704for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {705if (myFileSelector->isItemSelected(i) && !myFileSelector->isItemDirectory(i)) {706if (!text.empty()) {707text += ' ';708}709text += "\"" + myFileSelector->getItemFilename(i) + "\"";710}711}712myFilenameTextField->setText(text);713}714return 1;715}716717718long719GNEFileSelector::onCmdItemDoubleClicked(FXObject* obj, FXSelector sel, void* ptr) {720const FXint index = (FXint)(FXival)ptr;721if (0 <= index) {722// If directory, open the directory723if (myFileSelector->isItemShare(index) || myFileSelector->isItemDirectory(index)) {724setDirectory(myFileSelector->getItemPathname(index));725return 1;726}727// Only return if we wanted a file728if (myOpenMode != GNEFileDialog::OpenMode::LOAD_DIRECTORY) {729return onCmdAccept(obj, sel, ptr);730}731}732return 1;733}734735736long737GNEFileSelector::onCmdAccept(FXObject* obj, FXSelector sel, void* ptr) {738auto GNEApp = myFileDialog->getApplicationWindow();739// get directory740const std::string directory = getDirectory();741// continue if we selected a directory742if (directory.size() > 0) {743// In directory mode:- we got our answer!744if (myOpenMode == GNEFileDialog::OpenMode::LOAD_DIRECTORY) {745return myFileDialog->onCmdAccept(obj, sel, ptr);746} else {747// Hop over to that directory748setDirectory(directory.c_str());749myFilenameTextField->setText(FXString::null);750return 1;751}752}753// get filename754std::string filename = getFilename();755// continue if we selected a filename756if (filename.size() > 0) {757// Get directory part of path758FXString dir = FXPath::directory(filename.c_str());759// In file mode, directory part of path should exist760if (FXStat::isDirectory(dir)) {761// set directory762setDirectory(dir);763// ensure that filename has extension764const std::string filenameExtension = myFileDialog->assureExtension(filename);765const std::string file = FXPath::name(filenameExtension.c_str()).text();766// now continue if we're loading or saving767if (myOpenMode == GNEFileDialog::OpenMode::SAVE) {768// ask if file exist769if (FXStat::exists(filenameExtension.c_str())) {770// open question dialog771const GNEQuestionBasicDialog overwriteDialog(GNEApp, myFileDialog, GNEDialog::Buttons::YES_NO,772TL("Overwrite file"), TLF("The selected file: %", file),773TL("already exist. Overwrite?"));774// check if abort775if (overwriteDialog.getResult() != GNEDialog::Result::ACCEPT) {776return 1;777}778}779return myFileDialog->onCmdAccept(obj, sel, ptr);780} else {781// check that file exist782if (FXStat::exists(filenameExtension.c_str())) {783return myFileDialog->onCmdAccept(obj, sel, ptr);784} else {785// open error dialog786GNEErrorBasicDialog(GNEApp, myFileDialog,787TL("File doesn't exist"),788TLF("The selected file '%'", file),789TL("doesn't exist"));790return 0;791}792}793}794}795// Beep796getApp()->beep();797return 1;798}799800801long802GNEFileSelector::onCmdDirectoryUp(FXObject*, FXSelector, void*) {803setDirectory(FXPath::upLevel(myFileSelector->getDirectory()));804return 1;805}806807808long809GNEFileSelector::onUpdDirectoryUp(FXObject* sender, FXSelector, void*) {810// check if this is a top directory811const bool topDirectory = FXPath::isTopDirectory(myFileSelector->getDirectory());812return sender->handle(this, topDirectory ? FXSEL(SEL_COMMAND, ID_DISABLE) : FXSEL(SEL_COMMAND, ID_ENABLE), NULL);813}814815816long817GNEFileSelector::onCmdHomeFolder(FXObject*, FXSelector, void*) {818setDirectory(FXSystem::getHomeDirectory());819return 1;820}821822823long824GNEFileSelector::onCmdWorkFolder(FXObject*, FXSelector, void*) {825setDirectory(FXSystem::getCurrentDirectory());826return 1;827}828829830long831GNEFileSelector::onCmdConfigFolder(FXObject*, FXSelector, void*) {832// get config file folder833const auto configFileFolder = FXPath::directory(OptionsCont::getOptions().getString("configuration-file").c_str());834setDirectory(configFileFolder);835return 1;836}837838839long840GNEFileSelector::onCmdVisit(FXObject*, FXSelector, void* ptr) {841setDirectory((FXchar*)ptr);842return 1;843}844845846long847GNEFileSelector::onCmdBookmark(FXObject*, FXSelector, void*) {848myBookmarksRecentFiles.appendFile(myFileSelector->getDirectory());849return 1;850}851852853long854GNEFileSelector::onCmdDirTree(FXObject*, FXSelector, void* ptr) {855myFileSelector->setDirectory((FXchar*)ptr);856if (myOpenMode == GNEFileDialog::OpenMode::LOAD_DIRECTORY) {857myFilenameTextField->setText(FXString::null);858}859return 1;860}861862863long864GNEFileSelector::onCmdNewFolder(FXObject*, FXSelector, void*) {865auto GNEApp = myFileDialog->getApplicationWindow();866// create file path dialog867const auto filePathDialog = new GNEFilePathDialog(GNEApp, myFileDialog, TL("Create New Directory"),868TL("Create new directory with name:"), "DirectoryName");869// continue depending of filePathDialog results870if (filePathDialog->getResult() == GNEDialog::Result::ACCEPT) {871const FXString dirname = FXPath::absolute(myFileSelector->getDirectory(), filePathDialog->getFilePath().c_str());872// check if exist873if (FXStat::exists(dirname)) {874// open error dialog875GNEErrorBasicDialog(GNEApp, myFileDialog, TL("Directory already Exists"),876TLF("The new directory:\n%", dirname.text()),877TL("already exists"));878return 1;879}880// try to create it881if (!FXDir::create(dirname)) {882// open error dialog883GNEErrorBasicDialog(GNEApp, myFileDialog, TL("Cannot create directory"),884TLF("Cannot create directory:\n%", dirname.text()),885TL("Check folder permissions"));886return 1;887}888// set as current directory889setDirectory(dirname);890}891return 1;892}893894895void896GNEFileSelector::buildBookmarkMenuPane(FXHorizontalFrame* navigatorHorizontalFrame) {897// create bookmarks menu pane898myBookmarkMenuPane = new FXMenuPane(this, POPUP_SHRINKWRAP);899new FXMenuCommand(myBookmarkMenuPane, (TL("Set bookmark") + std::string("\t\t") + TL("Bookmark current directory.")).c_str(),900GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_SET), this, FXFileSelector::ID_BOOKMARK);901new FXMenuCommand(myBookmarkMenuPane, (TL("Clear bookmarks") + std::string("\t\t") + TL("Clear bookmarks.")).c_str(),902GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_CLR), &myBookmarksRecentFiles, FXRecentFiles::ID_CLEAR);903FXMenuSeparator* separator = new FXMenuSeparator(myBookmarkMenuPane);904separator->setTarget(&myBookmarksRecentFiles);905separator->setSelector(FXRecentFiles::ID_ANYFILES);906new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_1);907new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_2);908new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_3);909new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_4);910new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_5);911new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_6);912new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_7);913new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_8);914new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_9);915new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_10);916new FXFrame(navigatorHorizontalFrame, LAYOUT_FIX_WIDTH, 0, 0, 4, 1);917myBookmarksRecentFiles.setTarget(this);918myBookmarksRecentFiles.setSelector(FXFileSelector::ID_VISIT);919}920921922void923GNEFileSelector::buildButtons(FXHorizontalFrame* navigatorHorizontalFrame, MFXStaticToolTip* staticTooltipMenu) {924// create button for going up one directory925auto goUpButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_DIRUP_ICON),926this, FXFileSelector::ID_DIRECTORY_UP, GUIDesignButtonIconFileDialog);927goUpButton->setTipText(TL("Go up one directory"));928// create button for go to home directory929auto goHomeButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_HOME),930this, FXFileSelector::ID_HOME, GUIDesignButtonIconFileDialog);931goHomeButton->setTipText(TL("Go to home directory"));932// create button for go to work directory933auto goWorkDirectory = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_WORK),934this, FXFileSelector::ID_WORK, GUIDesignButtonIconFileDialog);935goWorkDirectory->setTipText(TL("Go to work directory"));936// create button for go to work directory937auto goConfigDirectory = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", nullptr,938this, MID_GNE_BUTTON_CONFIG, GUIDesignButtonIconFileDialog);939// disable if configuration file is empty940if (OptionsCont::getOptions().getString("configuration-file").empty()) {941goConfigDirectory->disable();942}943// set icon and tip depending of config type944if (myConfigType == GNEFileDialog::ConfigType::NETEDIT) {945goConfigDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::NETEDIT_MINI));946goConfigDirectory->setTipText(TL("Go to netedit config directory"));947} else {948goConfigDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::SUMO_MINI));949goConfigDirectory->setTipText(TL("Go to sumo config directory"));950}951// create button for bookmarks menu952auto bookmenuTooltip = new MFXMenuButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_SET),953myBookmarkMenuPane, this, GUIDesignButtonIconFileDialog);954bookmenuTooltip->setTipText(TL("Bookmarks"));955bookmenuTooltip->setSelector(FXFileSelector::ID_BOOKMENU);956// create button for creating a new directory957auto newDirectoryButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FOLDER_NEW),958this, FXFileSelector::ID_NEW, GUIDesignButtonIconFileDialog);959newDirectoryButton->setTipText(TL("Create new directory"));960// create button for show lists961auto showListButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_SHOW_SMALLICONS),962myFileSelector, FXFileList::ID_SHOW_MINI_ICONS, GUIDesignButtonIconFileDialog);963showListButton->setTipText(TL("Display directory with small icons"));964// create button for show icons965auto showIconsButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_SHOW_BIGICONS),966myFileSelector, FXFileList::ID_SHOW_BIG_ICONS, GUIDesignButtonIconFileDialog);967showIconsButton->setTipText(TL("Display directory with big icons"));968// create button for show details969auto showDetailsButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_SHOW_DETAILS),970myFileSelector, FXFileList::ID_SHOW_DETAILS, GUIDesignButtonIconFileDialog);971showDetailsButton->setTipText(TL("Display detailed directory listing"));972// create button for toogle show/hide hidden files973auto showHiddeToogleButton = new MFXToggleButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", "",974GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_HIDDEN),975GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_SHOWN),976myFileSelector, FXFileList::ID_TOGGLE_HIDDEN, GUIDesignButtonIconFileDialog);977showHiddeToogleButton->setTipText(TL("Toggle show hidden files and directories"));978}979980981void982GNEFileSelector::buildShortcuts() {983// set shortcuts984FXAccelTable* table = getShell()->getAccelTable();985if (table) {986table->addAccel(MKUINT(KEY_BackSpace, 0), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_DIRECTORY_UP));987table->addAccel(MKUINT(KEY_Delete, 0), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_DELETE));988table->addAccel(MKUINT(KEY_h, CONTROLMASK), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_HOME));989table->addAccel(MKUINT(KEY_w, CONTROLMASK), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_WORK));990table->addAccel(MKUINT(KEY_n, CONTROLMASK), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_NEW));991table->addAccel(MKUINT(KEY_a, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SELECT_ALL));992table->addAccel(MKUINT(KEY_b, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SHOW_BIG_ICONS));993table->addAccel(MKUINT(KEY_s, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SHOW_MINI_ICONS));994table->addAccel(MKUINT(KEY_l, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SHOW_DETAILS));995}996}997998999void1000GNEFileSelector::parseExtensions(const std::vector<std::string>& extensions) {1001// convert extensions in FXString1002for (const auto& extension : extensions) {1003// first get all characters within () excluding spaces1004const std::string cleanExtension = FXFileSelector::patternFromText(extension.c_str()).text();1005// declare subextensions1006std::vector<std::string> subExtensions;1007// now subdivide1008if (cleanExtension != "*") {1009// split extensions1010const auto subExtensionsStr = StringTokenizer(cleanExtension, ",").getVector();1011for (const auto& subExtensionStr : subExtensionsStr) {1012// ignore first *1013subExtensions.push_back(subExtensionStr.substr(1));1014}1015}1016myExtensions.push_back(subExtensions);1017}1018}10191020/*******************************************************************************/102110221023