Path: blob/main/src/netedit/dialogs/file/GNEFileSelector.cpp
169684 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 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*) {223const auto filenameList = getSelectedFiles();224// first check if we have files to copy225if (filenameList.size() > 0) {226// get only first filename227const std::string originFilePath = filenameList.front();228// create default destiny filename229std::string destinyFilename = FXPath::absolute(FXPath::directory(originFilePath.c_str()), "CopyOf" + FXPath::name(originFilePath.c_str())).text();230// create file path dialog231const auto filePathDialog = new GNEFilePathDialog(myFileDialog->getApplicationWindow(), TL("Copy File"), TL("Select destiny file"), destinyFilename);232// continue depending of filePathDialog results233if (filePathDialog->getResult() == GNEDialog::Result::ACCEPT) {234// get destiny filename from dialog235destinyFilename = filePathDialog->getFilePath();236// check if we selected the same file237if (FXFile::identical(originFilePath.c_str(), destinyFilename.c_str())) {238// open error dialog239GNEErrorBasicDialog(myFileDialog->getApplicationWindow(), TL("Error copying file"),240TLF("Unable to copy file:\n%\n", destinyFilename),241TL("The source and destiny files are the same"));242} else {243// check if file exist244if (FXStat::exists(destinyFilename.c_str())) {245// open question dialog246const auto overwriteDialog = GNEQuestionBasicDialog(myFileDialog->getApplicationWindow(), GNEDialog::Buttons::YES_NO,247TL("Overwrite file"), TLF("The destiny file:\n%\n", destinyFilename),248TL("already exist. Overwrite?"));249// check if abort250if (overwriteDialog.getResult() != GNEDialog::Result::ACCEPT) {251return 1;252}253}254// try to copy overwritten255if (!FXFile::copyFiles(originFilePath.c_str(), destinyFilename.c_str(), TRUE)) {256// open error dialog257GNEErrorBasicDialog(myFileDialog->getApplicationWindow(), TL("Error copying file"),258TLF("Unable to copy file:\n%\n", destinyFilename),259TL("Check destiny file permissions"));260}261}262}263}264return 1;265}266267268long269GNEFileSelector::onCmdMove(FXObject*, FXSelector, void*) {270const auto filenameList = getSelectedFiles();271// first check if we have files to move272if (filenameList.size() > 0) {273// get only first filename274const std::string originFilePath = filenameList.front();275// create file path dialog276const auto filePathDialog = new GNEFilePathDialog(myFileDialog->getApplicationWindow(), TL("Move File"), TL("Select destiny file"), originFilePath);277// continue depending of filePathDialog results278if (filePathDialog->getResult() == GNEDialog::Result::ACCEPT) {279// get destiny filename from dialog280const std::string destinyFilename = filePathDialog->getFilePath();281// check if we selected the same file282if (FXFile::identical(originFilePath.c_str(), destinyFilename.c_str())) {283// open error dialog284GNEErrorBasicDialog(myFileDialog->getApplicationWindow(), TL("Error moving file"),285TLF("Unable to move file:\n%\n", destinyFilename),286TL("The source and destiny files are the same"));287} else {288// check if file exist289if (FXStat::exists(destinyFilename.c_str())) {290// open question dialog291const auto overwriteDialog = GNEQuestionBasicDialog(myFileDialog->getApplicationWindow(), GNEDialog::Buttons::YES_NO,292TL("Overwrite file"), TLF("The destiny file:\n%\n", destinyFilename),293TL("already exist. Overwrite?"));294// check if abort295if (overwriteDialog.getResult() != GNEDialog::Result::ACCEPT) {296return 1;297}298}299// try to move overwritten300if (!FXFile::moveFiles(originFilePath.c_str(), destinyFilename.c_str(), TRUE)) {301// open error dialog302GNEErrorBasicDialog(myFileDialog->getApplicationWindow(), TL("Error moving file"),303TLF("Unable to move file:\n%\n", destinyFilename),304TL("Check destiny file permissions"));305}306}307}308}309return 1;310}311312313long314GNEFileSelector::onCmdDelete(FXObject*, FXSelector, void*) {315const auto filenameList = getSelectedFiles();316// first check if we have files to link317if (filenameList.size() > 0) {318// get only first filename319const std::string fileToDelete = filenameList.front();320// open question dialog321const auto askDialog = GNEQuestionBasicDialog(myFileDialog->getApplicationWindow(), GNEDialog::Buttons::YES_NO,322TL("Deleting file"), TL("Are you sure you want to delete the file:"),323fileToDelete);324// check if continue325if (askDialog.getResult() == GNEDialog::Result::ACCEPT) {326// try to remove it327if (!FXFile::removeFiles(fileToDelete.c_str(), TRUE)) {328// open error dialog329GNEErrorBasicDialog(myFileDialog->getApplicationWindow(), TL("Error deleting file"),330TLF("Unable to delete file:\n%\n", fileToDelete),331TL("Check file permissions"));332}333}334}335return 1;336}337338339long340GNEFileSelector::onUpdSelected(FXObject* sender, FXSelector, void*) {341for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {342if (myFileSelector->isItemSelected(i)) {343sender->handle(this, FXSEL(SEL_COMMAND, ID_ENABLE), NULL);344return 1;345}346}347sender->handle(this, FXSEL(SEL_COMMAND, ID_DISABLE), NULL);348return 1;349}350351352long353GNEFileSelector::onCmdImageSize(FXObject*, FXSelector sel, void*) {354switch (FXSELID(sel)) {355case FXFileSelector::ID_NORMAL_SIZE:356setImageSize(32);357break;358case FXFileSelector::ID_MEDIUM_SIZE:359setImageSize(48);360break;361case FXFileSelector::ID_GIANT_SIZE:362setImageSize(64);363break;364}365return 1;366}367368369long370GNEFileSelector::onUpdImageSize(FXObject* sender, FXSelector sel, void*) {371FXbool check = FALSE;372switch (FXSELID(sel)) {373case FXFileSelector::ID_NORMAL_SIZE:374check = (getImageSize() == 32);375break;376case FXFileSelector::ID_MEDIUM_SIZE:377check = (getImageSize() == 48);378break;379case FXFileSelector::ID_GIANT_SIZE:380check = (getImageSize() == 64);381break;382}383sender->handle(this, check ? FXSEL(SEL_COMMAND, ID_CHECK) : FXSEL(SEL_COMMAND, ID_UNCHECK), NULL);384return 1;385}386387388long389GNEFileSelector::onKeyPress(FXObject* obj, FXSelector sel, void* ptr) {390// if ESC key is pressed, close dialog aborting391FXEvent* event = (FXEvent*)ptr;392if (event->code == KEY_Return) {393return onCmdAccept(obj, sel, ptr);394} else {395return FXVerticalFrame::onKeyPress(obj, sel, ptr);396}397}398399400long401GNEFileSelector::onPopupMenu(FXObject*, FXSelector, void* ptr) {402FXEvent* event = (FXEvent*)ptr;403if (event->moved) {404return 1;405}406407FXMenuPane filemenu(this);408new FXMenuCommand(&filemenu, TL("Up one level"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_DIRUP_ICON), this, FXFileSelector::ID_DIRECTORY_UP);409new FXMenuCommand(&filemenu, TL("Home directory"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_HOME), this, FXFileSelector::ID_HOME);410new FXMenuCommand(&filemenu, TL("Work directory"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_WORK), this, FXFileSelector::ID_WORK);411auto configDirectory = new FXMenuCommand(&filemenu, TL("Config directory"), nullptr, this, MID_GNE_BUTTON_CONFIG);412// disable if configuration file is empty413if (OptionsCont::getOptions().getString("configuration-file").empty()) {414configDirectory->disable();415}416// set icon and tip depending of config type417if (myConfigType == GNEFileDialog::ConfigType::NETEDIT) {418configDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::NETEDIT_MINI));419} else {420configDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::SUMO_MINI));421}422new FXMenuCommand(&filemenu, TL("Select all"), NULL, myFileSelector, FXFileList::ID_SELECT_ALL);423new FXMenuSeparator(&filemenu);424425FXMenuPane sortmenu(this);426new FXMenuCascade(&filemenu, TL("Sort by"), NULL, &sortmenu);427new FXMenuRadio(&sortmenu, TL("Name"), myFileSelector, FXFileList::ID_SORT_BY_NAME);428new FXMenuRadio(&sortmenu, TL("Type"), myFileSelector, FXFileList::ID_SORT_BY_TYPE);429new FXMenuRadio(&sortmenu, TL("Size"), myFileSelector, FXFileList::ID_SORT_BY_SIZE);430new FXMenuRadio(&sortmenu, TL("Time"), myFileSelector, FXFileList::ID_SORT_BY_TIME);431new FXMenuRadio(&sortmenu, TL("User"), myFileSelector, FXFileList::ID_SORT_BY_USER);432new FXMenuRadio(&sortmenu, TL("Group"), myFileSelector, FXFileList::ID_SORT_BY_GROUP);433new FXMenuSeparator(&sortmenu);434new FXMenuCheck(&sortmenu, TL("Reverse"), myFileSelector, FXFileList::ID_SORT_REVERSE);435new FXMenuCheck(&sortmenu, TL("Ignore case"), myFileSelector, FXFileList::ID_SORT_CASE);436437FXMenuPane viewmenu(this);438new FXMenuCascade(&filemenu, TL("View"), NULL, &viewmenu);439new FXMenuRadio(&viewmenu, TL("Small icons"), myFileSelector, FXFileList::ID_SHOW_MINI_ICONS);440new FXMenuRadio(&viewmenu, TL("Big icons"), myFileSelector, FXFileList::ID_SHOW_BIG_ICONS);441new FXMenuRadio(&viewmenu, TL("Details"), myFileSelector, FXFileList::ID_SHOW_DETAILS);442new FXMenuSeparator(&viewmenu);443new FXMenuRadio(&viewmenu, TL("Rows"), myFileSelector, FXFileList::ID_ARRANGE_BY_ROWS);444new FXMenuRadio(&viewmenu, TL("Columns"), myFileSelector, FXFileList::ID_ARRANGE_BY_COLUMNS);445new FXMenuSeparator(&viewmenu);446new FXMenuCheck(&viewmenu, TL("Hidden files"), myFileSelector, FXFileList::ID_TOGGLE_HIDDEN);447new FXMenuCheck(&viewmenu, TL("Preview images"), myFileSelector, FXFileList::ID_TOGGLE_IMAGES);448new FXMenuSeparator(&viewmenu);449new FXMenuRadio(&viewmenu, TL("Normal images"), this, FXFileSelector::ID_NORMAL_SIZE);450new FXMenuRadio(&viewmenu, TL("Medium images"), this, FXFileSelector::ID_MEDIUM_SIZE);451new FXMenuRadio(&viewmenu, TL("Giant images"), this, FXFileSelector::ID_GIANT_SIZE);452453FXMenuPane bookmenu(this);454FXMenuCascade* bookcasc = new FXMenuCascade(&filemenu, TL("Bookmarks"), NULL, &bookmenu);455bookcasc->setTarget(this);456bookcasc->setSelector(FXFileSelector::ID_BOOKMENU);457new FXMenuCommand(&bookmenu, TL("Set bookmark"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_SET), this, FXFileSelector::ID_BOOKMARK);458new FXMenuCommand(&bookmenu, TL("Clear bookmarks"), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_CLR), &myBookmarksRecentFiles, FXRecentFiles::ID_CLEAR);459FXMenuSeparator* sep1 = new FXMenuSeparator(&bookmenu);460sep1->setTarget(&myBookmarksRecentFiles);461sep1->setSelector(FXRecentFiles::ID_ANYFILES);462new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_1);463new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_2);464new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_3);465new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_4);466new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_5);467new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_6);468new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_7);469new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_8);470new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_9);471new FXMenuCommand(&bookmenu, FXString::null, NULL, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_10);472473new FXMenuSeparator(&filemenu);474new FXMenuCommand(&filemenu, TL("New directory..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FOLDER_NEW), this, FXFileSelector::ID_NEW);475new FXMenuCommand(&filemenu, TL("Copy..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_COPY), this, FXFileSelector::ID_COPY);476new FXMenuCommand(&filemenu, TL("Move..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_MOVE), this, FXFileSelector::ID_MOVE);477// disabled linker because it doesn't work478new FXMenuCommand(&filemenu, TL("Delete..."), GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_DELETE), this, FXFileSelector::ID_DELETE);479480filemenu.create();481filemenu.popup(NULL, event->root_x, event->root_y);482getApp()->runModalWhileShown(&filemenu);483return 1;484}485486487long488GNEFileSelector::onCmdFilter(FXObject*, FXSelector, void* ptr) {489const FXString pat = FXFileSelector::patternFromText((FXchar*)ptr);490myFileSelector->setPattern(pat);491if (myOpenMode == GNEFileDialog::OpenMode::SAVE) {492FXString ext = FXFileSelector::extensionFromPattern(pat);493if (!ext.empty()) {494FXString name = FXPath::stripExtension(myFilenameTextField->getText());495if (!name.empty()) {496myFilenameTextField->setText(name + "." + ext);497}498}499}500return 1;501}502503504void505GNEFileSelector::setDirectory(const FXString& path) {506const FXString abspath = FXPath::absolute(path);507myFileSelector->setDirectory(abspath);508myDirBox->setDirectory(abspath);509if (myOpenMode != GNEFileDialog::OpenMode::SAVE) {510myFilenameTextField->setText(FXString::null);511}512}513514515void516GNEFileSelector::setFilename(const FXString& path) {517const FXString fullname(FXPath::absolute(path));518FXString name(FXPath::name(fullname));519myFileSelector->setCurrentFile(fullname);520myDirBox->setDirectory(myFileSelector->getDirectory());521myFilenameTextField->setText(name);522}523524525std::string526GNEFileSelector::getFilename() const {527if (myOpenMode == GNEFileDialog::OpenMode::LOAD_MULTIPLE) {528for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {529if (myFileSelector->isItemSelected(i) && !myFileSelector->isItemDirectory(i)) {530return FXPath::absolute(myFileSelector->getDirectory(), myFileSelector->getItemFilename(i)).text();531}532}533} else if (!myFilenameTextField->getText().empty()) {534//return FXPath::absolute(myFileSelector->getDirectory(),myFilenameTextField->getText());535return FXPath::absolute(myFileSelector->getDirectory(), FXPath::expand(myFilenameTextField->getText())).text(); // FIXME don't always want to expand!536}537return "";538}539540541std::vector<std::string>542GNEFileSelector::getFilenames() const {543return getSelectedFilesOnly();544}545546547std::string548GNEFileSelector::getDirectory() const {549return myFileSelector->getDirectory().text();550}551552553const std::vector<std::string>&554GNEFileSelector::getFileExtension() const {555return myExtensions.at(myFileFilterComboBox->getCurrentItem());556}557558559void560GNEFileSelector::setItemSpace(FXint s) {561myFileSelector->setItemSpace(s);562}563564565FXint566GNEFileSelector::getItemSpace() const {567return myFileSelector->getItemSpace();568}569570571void572GNEFileSelector::setFileBoxStyle(FXuint style) {573myFileSelector->setListStyle((myFileSelector->getListStyle() & ~FILESTYLEMASK) | (style & FILESTYLEMASK));574}575576577FXuint578GNEFileSelector::getFileBoxStyle() const {579return myFileSelector->getListStyle()&FILESTYLEMASK;580}581582583void584GNEFileSelector::setMatchMode(FXuint mode) {585myFileSelector->setMatchMode(mode);586}587588589FXuint590GNEFileSelector::getMatchMode() const {591return myFileSelector->getMatchMode();592}593594595FXbool596GNEFileSelector::showHiddenFiles() const {597return myFileSelector->showHiddenFiles();598}599600601void602GNEFileSelector::showHiddenFiles(FXbool showing) {603myFileSelector->showHiddenFiles(showing);604}605606607FXbool608GNEFileSelector::showImages() const {609return myFileSelector->showImages();610}611612613void614GNEFileSelector::showImages(FXbool showing) {615myFileSelector->showImages(showing);616}617618619FXint620GNEFileSelector::getImageSize() const {621return myFileSelector->getImageSize();622}623624625void626GNEFileSelector::setImageSize(FXint size) {627myFileSelector->setImageSize(size);628}629630631MFXTextFieldIcon*632GNEFileSelector::getFilenameTextField() const {633return myFilenameTextField;634}635636637long638GNEFileSelector::onCmdItemSelected(FXObject*, FXSelector, void* ptr) {639const FXint index = (FXint)(FXival)ptr;640FXString text, file;641if (myOpenMode == GNEFileDialog::OpenMode::LOAD_MULTIPLE) {642for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {643if (myFileSelector->isItemSelected(i) && !myFileSelector->isItemDirectory(i)) {644if (!text.empty()) {645text += ' ';646}647text += "\"" + myFileSelector->getItemFilename(i) + "\"";648}649}650myFilenameTextField->setText(text);651} else if (myOpenMode == GNEFileDialog::OpenMode::LOAD_DIRECTORY) {652if (myFileSelector->isItemDirectory(index)) {653text = myFileSelector->getItemFilename(index);654myFilenameTextField->setText(text);655}656} else {657if (!myFileSelector->isItemDirectory(index)) {658text = myFileSelector->getItemFilename(index);659myFilenameTextField->setText(text);660}661}662return 1;663}664665666long667GNEFileSelector::onCmdItemDeselected(FXObject*, FXSelector, void*) {668FXString text, file;669if (myOpenMode == GNEFileDialog::OpenMode::LOAD_MULTIPLE) {670for (FXint i = 0; i < myFileSelector->getNumItems(); i++) {671if (myFileSelector->isItemSelected(i) && !myFileSelector->isItemDirectory(i)) {672if (!text.empty()) {673text += ' ';674}675text += "\"" + myFileSelector->getItemFilename(i) + "\"";676}677}678myFilenameTextField->setText(text);679}680return 1;681}682683684long685GNEFileSelector::onCmdItemDoubleClicked(FXObject* obj, FXSelector sel, void* ptr) {686const FXint index = (FXint)(FXival)ptr;687if (0 <= index) {688// If directory, open the directory689if (myFileSelector->isItemShare(index) || myFileSelector->isItemDirectory(index)) {690setDirectory(myFileSelector->getItemPathname(index));691return 1;692}693// Only return if we wanted a file694if (myOpenMode != GNEFileDialog::OpenMode::LOAD_DIRECTORY) {695return myFileDialog->onCmdAccept(obj, sel, ptr);696}697}698return 1;699}700701702long703GNEFileSelector::onCmdAccept(FXObject* obj, FXSelector sel, void* ptr) {704// Get (first) myFilenameTextField or directory705std::string path = getFilename();706// Only do something if a selection was made707if (path.size() > 0) {708// Is directory?709if (FXStat::isDirectory(path.c_str())) {710// In directory mode:- we got our answer!711if (myOpenMode == GNEFileDialog::OpenMode::LOAD_DIRECTORY) {712return myFileDialog->onCmdAccept(obj, sel, ptr);713}714// Hop over to that directory715myDirBox->setDirectory(path.c_str());716myFileSelector->setDirectory(path.c_str());717myFilenameTextField->setText(FXString::null);718return 1;719}720// Get directory part of path721FXString dir = FXPath::directory(path.c_str());722// In file mode, directory part of path should exist723if (FXStat::isDirectory(dir)) {724// In any mode, existing directory part is good enough725if (myOpenMode == GNEFileDialog::OpenMode::SAVE) {726return myFileDialog->onCmdAccept(obj, sel, ptr);727}728// Otherwise, the whole myFilenameTextField must exist and be a file729if (FXStat::exists(path.c_str())) {730return myFileDialog->onCmdAccept(obj, sel, ptr);731732}733}734// Go up to the lowest directory which still exists735while (!FXPath::isTopDirectory(dir) && !FXStat::isDirectory(dir)) {736dir = FXPath::upLevel(dir);737}738// Switch as far as we could go739myDirBox->setDirectory(dir);740myFileSelector->setDirectory(dir);741// Put the tail end back for further editing742FXASSERT(dir.length() <= (int)path.size());743if (ISPATHSEP(path[dir.length()])) {744path.erase(0, dir.length() + 1);745} else {746path.erase(0, dir.length());747}748// Replace text box with new stuff749myFilenameTextField->setText(path.c_str());750myFilenameTextField->selectAll();751}752// Beep753getApp()->beep();754return 1;755}756757758long759GNEFileSelector::onCmdDirectoryUp(FXObject*, FXSelector, void*) {760setDirectory(FXPath::upLevel(myFileSelector->getDirectory()));761return 1;762}763764765long766GNEFileSelector::onUpdDirectoryUp(FXObject* sender, FXSelector, void*) {767// check if this is a top directory768const bool topDirectory = FXPath::isTopDirectory(myFileSelector->getDirectory());769return sender->handle(this, topDirectory ? FXSEL(SEL_COMMAND, ID_DISABLE) : FXSEL(SEL_COMMAND, ID_ENABLE), NULL);770}771772773long774GNEFileSelector::onCmdHomeFolder(FXObject*, FXSelector, void*) {775setDirectory(FXSystem::getHomeDirectory());776return 1;777}778779780long781GNEFileSelector::onCmdWorkFolder(FXObject*, FXSelector, void*) {782setDirectory(FXSystem::getCurrentDirectory());783return 1;784}785786787long788GNEFileSelector::onCmdConfigFolder(FXObject*, FXSelector, void*) {789// get config file folder790const auto configFileFolder = FXPath::directory(OptionsCont::getOptions().getString("configuration-file").c_str());791setDirectory(configFileFolder);792return 1;793}794795796long797GNEFileSelector::onCmdVisit(FXObject*, FXSelector, void* ptr) {798setDirectory((FXchar*)ptr);799return 1;800}801802803long804GNEFileSelector::onCmdBookmark(FXObject*, FXSelector, void*) {805myBookmarksRecentFiles.appendFile(myFileSelector->getDirectory());806return 1;807}808809810long811GNEFileSelector::onCmdDirTree(FXObject*, FXSelector, void* ptr) {812myFileSelector->setDirectory((FXchar*)ptr);813if (myOpenMode == GNEFileDialog::OpenMode::LOAD_DIRECTORY) {814myFilenameTextField->setText(FXString::null);815}816return 1;817}818819820long821GNEFileSelector::onCmdNewFolder(FXObject*, FXSelector, void*) {822// create file path dialog823const auto filePathDialog = new GNEFilePathDialog(myFileDialog->getApplicationWindow(), TL("Create New Directory"), TL("Create new directory with name:"), "DirectoryName");824// continue depending of filePathDialog results825if (filePathDialog->getResult() == GNEDialog::Result::ACCEPT) {826const FXString dirname = FXPath::absolute(myFileSelector->getDirectory(), filePathDialog->getFilePath().c_str());827// check if exist828if (FXStat::exists(dirname)) {829// open error dialog830GNEErrorBasicDialog(myFileDialog->getApplicationWindow(), TL("Directory already Exists"),831TLF("The new directory:\n%", dirname.text()),832TL("already exists"));833return 1;834}835// try to create it836if (!FXDir::create(dirname)) {837// open error dialog838GNEErrorBasicDialog(myFileDialog->getApplicationWindow(), TL("Cannot create directory"),839TLF("Cannot create directory:\n%", dirname.text()),840TL("Check folder permissions"));841return 1;842}843// set as current directory844setDirectory(dirname);845}846return 1;847}848849850void851GNEFileSelector::buildBookmarkMenuPane(FXHorizontalFrame* navigatorHorizontalFrame) {852// create bookmarks menu pane853myBookmarkMenuPane = new FXMenuPane(this, POPUP_SHRINKWRAP);854new FXMenuCommand(myBookmarkMenuPane, (TL("Set bookmark") + std::string("\t\t") + TL("Bookmark current directory.")).c_str(),855GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_SET), this, FXFileSelector::ID_BOOKMARK);856new FXMenuCommand(myBookmarkMenuPane, (TL("Clear bookmarks") + std::string("\t\t") + TL("Clear bookmarks.")).c_str(),857GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_CLR), &myBookmarksRecentFiles, FXRecentFiles::ID_CLEAR);858FXMenuSeparator* separator = new FXMenuSeparator(myBookmarkMenuPane);859separator->setTarget(&myBookmarksRecentFiles);860separator->setSelector(FXRecentFiles::ID_ANYFILES);861new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_1);862new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_2);863new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_3);864new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_4);865new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_5);866new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_6);867new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_7);868new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_8);869new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_9);870new FXMenuCommand(myBookmarkMenuPane, FXString::null, nullptr, &myBookmarksRecentFiles, FXRecentFiles::ID_FILE_10);871new FXFrame(navigatorHorizontalFrame, LAYOUT_FIX_WIDTH, 0, 0, 4, 1);872myBookmarksRecentFiles.setTarget(this);873myBookmarksRecentFiles.setSelector(FXFileSelector::ID_VISIT);874}875876877void878GNEFileSelector::buildButtons(FXHorizontalFrame* navigatorHorizontalFrame, MFXStaticToolTip* staticTooltipMenu) {879// create button for going up one directory880auto goUpButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_DIRUP_ICON),881this, FXFileSelector::ID_DIRECTORY_UP, GUIDesignButtonIconFileDialog);882goUpButton->setTipText(TL("Go up one directory"));883// create button for go to home directory884auto goHomeButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_HOME),885this, FXFileSelector::ID_HOME, GUIDesignButtonIconFileDialog);886goHomeButton->setTipText(TL("Go to home directory"));887// create button for go to work directory888auto goWorkDirectory = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_GOTO_WORK),889this, FXFileSelector::ID_WORK, GUIDesignButtonIconFileDialog);890goWorkDirectory->setTipText(TL("Go to work directory"));891// create button for go to work directory892auto goConfigDirectory = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", nullptr,893this, MID_GNE_BUTTON_CONFIG, GUIDesignButtonIconFileDialog);894// disable if configuration file is empty895if (OptionsCont::getOptions().getString("configuration-file").empty()) {896goConfigDirectory->disable();897}898// set icon and tip depending of config type899if (myConfigType == GNEFileDialog::ConfigType::NETEDIT) {900goConfigDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::NETEDIT_MINI));901goConfigDirectory->setTipText(TL("Go to netedit config directory"));902} else {903goConfigDirectory->setIcon(GUIIconSubSys::getIcon(GUIIcon::SUMO_MINI));904goConfigDirectory->setTipText(TL("Go to sumo config directory"));905}906// create button for bookmarks menu907auto bookmenuTooltip = new MFXMenuButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_BOOK_SET),908myBookmarkMenuPane, this, GUIDesignButtonIconFileDialog);909bookmenuTooltip->setTipText(TL("Bookmarks"));910bookmenuTooltip->setSelector(FXFileSelector::ID_BOOKMENU);911// create button for creating a new directory912auto newDirectoryButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FOLDER_NEW),913this, FXFileSelector::ID_NEW, GUIDesignButtonIconFileDialog);914newDirectoryButton->setTipText(TL("Create new directory"));915// create button for show lists916auto showListButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_SHOW_SMALLICONS),917myFileSelector, FXFileList::ID_SHOW_MINI_ICONS, GUIDesignButtonIconFileDialog);918showListButton->setTipText(TL("Display directory with small icons"));919// create button for show icons920auto showIconsButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_SHOW_BIGICONS),921myFileSelector, FXFileList::ID_SHOW_BIG_ICONS, GUIDesignButtonIconFileDialog);922showIconsButton->setTipText(TL("Display directory with big icons"));923// create button for show details924auto showDetailsButton = new MFXButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_SHOW_DETAILS),925myFileSelector, FXFileList::ID_SHOW_DETAILS, GUIDesignButtonIconFileDialog);926showDetailsButton->setTipText(TL("Display detailed directory listing"));927// create button for toogle show/hide hidden files928auto showHiddeToogleButton = new MFXToggleButtonTooltip(navigatorHorizontalFrame, staticTooltipMenu, "", "",929GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_HIDDEN),930GUIIconSubSys::getIcon(GUIIcon::FILEDIALOG_FILE_SHOWN),931myFileSelector, FXFileList::ID_TOGGLE_HIDDEN, GUIDesignButtonIconFileDialog);932showHiddeToogleButton->setTipText(TL("Toggle show hidden files and directories"));933}934935936void937GNEFileSelector::buildShortcuts() {938// set shortcuts939FXAccelTable* table = getShell()->getAccelTable();940if (table) {941table->addAccel(MKUINT(KEY_BackSpace, 0), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_DIRECTORY_UP));942table->addAccel(MKUINT(KEY_Delete, 0), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_DELETE));943table->addAccel(MKUINT(KEY_h, CONTROLMASK), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_HOME));944table->addAccel(MKUINT(KEY_w, CONTROLMASK), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_WORK));945table->addAccel(MKUINT(KEY_n, CONTROLMASK), this, FXSEL(SEL_COMMAND, FXFileSelector::ID_NEW));946table->addAccel(MKUINT(KEY_a, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SELECT_ALL));947table->addAccel(MKUINT(KEY_b, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SHOW_BIG_ICONS));948table->addAccel(MKUINT(KEY_s, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SHOW_MINI_ICONS));949table->addAccel(MKUINT(KEY_l, CONTROLMASK), myFileSelector, FXSEL(SEL_COMMAND, FXFileList::ID_SHOW_DETAILS));950}951}952953954void955GNEFileSelector::parseExtensions(const std::vector<std::string>& extensions) {956// convert extensions in FXString957for (const auto& extension : extensions) {958// first get all characters within () excluding spaces959const std::string cleanExtension = FXFileSelector::patternFromText(extension.c_str()).text();960// declare subextensions961std::vector<std::string> subExtensions;962// now subdivide963if (cleanExtension != "*") {964// split extensions965const auto subExtensionsStr = StringTokenizer(cleanExtension, ",").getVector();966for (const auto& subExtensionStr : subExtensionsStr) {967// ignore first *968subExtensions.push_back(subExtensionStr.substr(1));969}970}971myExtensions.push_back(subExtensions);972}973}974975/*******************************************************************************/976977978