diff -r 3308e8349e4c -r 4274f189ff78 src/eric7/RemoteServerInterface/EricServerFileDialog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/RemoteServerInterface/EricServerFileDialog.py Fri Feb 02 11:29:08 2024 +0100 @@ -0,0 +1,977 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a file dialog showing the file system of the eric-ide server. +""" + +import enum +import fnmatch +import re + +from PyQt6.QtCore import QLocale, Qt, pyqtSlot, QPoint +from PyQt6.QtWidgets import ( + QDialog, QTreeWidgetItem, QAbstractItemView, QCompleter, QLineEdit, QInputDialog, + QMenu +) + +from eric7.EricGui import EricPixmapCache +from eric7.EricGui.EricFileIconProvider import EricFileIconProvider +from eric7.EricWidgets import EricMessageBox +from eric7.EricWidgets.EricApplication import ericApp +from eric7.Globals import dataString +from eric7.SystemUtilities import FileSystemUtilities + +from .Ui_EricServerFileDialog import Ui_EricServerFileDialog + + +class AcceptMode(enum.Enum): + """ + Class defining the dialog accept modes. + """ + AcceptOpen = 0 + AcceptSave = 1 + + +class FileMode(enum.Enum): + """ + Class defining what the user may select in the file dialog. + """ + AnyFile = 0 + ExistingFile = 1 + Directory = 2 + ExistingFiles = 3 + +class EricServerFileDialog(QDialog, Ui_EricServerFileDialog): + """ + Class implementing a file dialog showing the file system of the eric-ide server. + """ + + IsDirectoryRole = Qt.ItemDataRole.UserRole + + def __init__(self, parent=None, caption="", directory="", filter=""): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + @param caption dialog title (defaults to "") + @type str (optional) + @param directory initial directory (defaults to "") + @type str (optional) + @param filter Qt file filter string (defaults to "") + @type str (optional) + """ + super().__init__(parent) + self.setupUi(self) + + # finish UI setup + self.backButton.setIcon(EricPixmapCache.getIcon("1leftarrow")) + self.forwardButton.setIcon(EricPixmapCache.getIcon("1rightarrow")) + self.upButton.setIcon(EricPixmapCache.getIcon("1uparrow")) + self.newDirButton.setIcon(EricPixmapCache.getIcon("dirNew")) + self.reloadButton.setIcon(EricPixmapCache.getIcon("reload")) + self.cancelButton.setIcon(EricPixmapCache.getIcon("dialog-cancel")) + + self.setWindowTitle(caption) + + self.__iconProvider = EricFileIconProvider() + + self.__nameCompleter = QCompleter() + self.__nameCompleter.setModel(self.listing.model()) + self.__nameCompleter.setCompletionColumn(0) + self.__nameCompleter.activated.connect(self.__nameCompleterActivated) + self.nameEdit.setCompleter(self.__nameCompleter) + + self.__contextMenu = QMenu(self) + + self.__fsInterface = ericApp().getObject("EricServer").getServiceInterface( + "FileSystem" + ) + + # set some default values + self.__fileMode = FileMode.ExistingFile + self.__dirsOnly = False + self.__acceptMode = AcceptMode.AcceptOpen + self.__showHidden = False + self.__sep = "/" + self.__filters = [] + + self.__history = [] + self.__currentHistoryIndex = -1 # empty history + self.__updateHistoryButtons() + + self.__filenameCache = [] + self.__directoryCache = [] + self.__selectedDirectory = None + + self.setNameFilters(filter.split(";;")) + + self.reloadButton.clicked.connect(self.__reload) + self.cancelButton.clicked.connect(self.reject) + + self.treeCombo.currentTextChanged.connect(self.setDirectory) + + self.setDirectory(FileSystemUtilities.plainFileName(directory)) + + def acceptMode(self): + """ + Public method to get the accept mode of the dialog. + + @return accept mode + @rtype AcceptMode + """ + return self.__acceptMode + + def setAcceptMode(self, mode): + """ + Public method to set the accept mode of the dialog. + + @param mode accept mode + @type AcceptMode + """ + self.__acceptMode = mode + + self.__updateOkButton() + + def fileMode(self): + """ + Public method to get the current file mode of the dialog. + + @return file mode + @rtype FileMode + """ + return self.__fileMode + + def setFileMode(self, mode): + """ + Public method to set the file mode of the dialog. + + @param mode file mode + @type FileMode + """ + self.__fileMode = mode + + self.listing.clearSelection() + if mode == FileMode.ExistingFiles: + self.listing.setSelectionMode( + QAbstractItemView.SelectionMode.ExtendedSelection + ) + else: + self.listing.setSelectionMode( + QAbstractItemView.SelectionMode.SingleSelection + ) + + self.__updateOkButton() + + def setNameFilters(self, filters): + """ + Public method to set the list of file/directory name filters. + + @param filters list of filter expressions + ("filter_name (pattern1 ... patternN)") + @type list of str + """ + self.__filters = [ + f.split(" (", 1)[1].split(")", 1)[0].split() for f in filters + ] + + self.filterCombo.clear() + self.filterCombo.addItems(filters) + + def setNameFilter(self, filter): + """ + Public method to set the current name filter. + + @param filter filter text to make current + @type str + """ + self.filterCombo.setCurrentText(filter) + + def setDirectoriesOnly(self, dirsOnly): + """ + Public method to set a flag to just show directories. + + @param dirsOnly flag indicating to just show directories + @type bool + """ + self.__dirsOnly = dirsOnly + + filters = self.__filters[self.filterCombo.currentIndex()] + self.__filterList(filters) + + def __addToHistory(self, entry): + """ + Private method to add a directory to the history list. + + @param entry name of the directory to be added + @type str + """ + try: + # is in the history already? + index = self.__history.index(entry) + self.__currentHistoryIndex = index + except ValueError: + # new entry + self.__history.append(entry) + self.__currentHistoryIndex = len(self.__history) - 1 + + self.__updateHistoryButtons() + + @pyqtSlot() + def __updateHistoryButtons(self): + """ + Private method to update the enabled state of the back and forward buttons. + """ + if not self.__history: + self.backButton.setEnabled(False) + self.forwardButton.setEnabled(False) + else: + self.backButton.setEnabled(self.__currentHistoryIndex > 0) + self.forwardButton.setEnabled( + self.__currentHistoryIndex < len(self.__history) - 1 + ) + + @pyqtSlot() + def on_backButton_clicked(self): + """ + Private slot to move back in history of visited directories. + """ + self.setDirectory(self.__history[self.__currentHistoryIndex - 1]) + + @pyqtSlot() + def on_forwardButton_clicked(self): + """ + Private slot to move forward in history of visited directories. + """ + self.setDirectory(self.__history[self.__currentHistoryIndex + 1]) + + @pyqtSlot() + def __updateUpButton(self): + """ + Private slot to update the enabled state of the 'Up' button. + """ + self.upButton.setEnabled( + self.treeCombo.currentIndex() < self.treeCombo.count() - 1 + ) + + @pyqtSlot() + def on_upButton_clicked(self): + """ + Private slot to move up one level in the hierarchy. + """ + self.treeCombo.setCurrentIndex(self.treeCombo.currentIndex() + 1) + + @pyqtSlot() + def on_newDirButton_clicked(self): + """ + Private slot to create a new directory. + """ + newDir, ok = QInputDialog.getText( + self, + self.tr("New Directory"), + self.tr("Enter the name for the new directory:"), + QLineEdit.EchoMode.Normal, + ) + if ok and newDir: + if newDir in self.__directoryCache or newDir in self.__filenameCache: + EricMessageBox.warning( + self, + self.tr("New Directory"), + self.tr( + "<p>A file or directory with the name <b>{0}</b> exists" + " already. Aborting...</p>" + ).format(newDir), + ) + return + + ok, error = self.__fsInterface.mkdir(self.__getFullPath(newDir)) + if ok: + # refresh + self.__reload() + else: + EricMessageBox.critical( + self, + self.tr("New Directory"), + self.tr( + "<p>The directory <b>{0}</b> could not be created.</p>" + "<p>Reason: {1}</p>" + ).format( + self.__getFullPath(newDir), + error if error else self.tr("Unknown"), + ), + ) + + @pyqtSlot() + def __reload(self): + """ + Private slot to reload the directory listing. + """ + self.setDirectory(self.treeCombo.currentText()) + + @pyqtSlot(QTreeWidgetItem, int) + def on_listing_itemActivated(self, item, column): + """ + Private slot to handle the activation of an item in the list. + + @param item reference to the activated item + @type QTreeWidgetItem + @param column column number (unused) + @type int + """ + if ( + item.data(0, EricServerFileDialog.IsDirectoryRole) + and self.__fileMode != FileMode.Directory + ): + self.setDirectory(self.__getFullPath(item.text(0))) + else: + self.accept() + + @pyqtSlot() + def on_listing_itemSelectionChanged(self): + """ + Private slot to handle the selection of listed items. + """ + for itm in self.listing.selectedItems(): + if itm.data(0, EricServerFileDialog.IsDirectoryRole): + self.__selectedDirectory = itm.text(0) + break + else: + self.__selectedDirectory = None + + selected = [] + for itm in self.listing.selectedItems(): + isDir = itm.data(0, EricServerFileDialog.IsDirectoryRole) + if self.__fileMode == FileMode.Directory and isDir: + selected.append(itm.text(0)) + elif not isDir: + selected.append(itm.text(0)) + + if len(selected) == 1: + self.nameEdit.setText(selected[0]) + elif len(selected) > 1: + self.nameEdit.setText( + '"{0}"'.format('" "'.join(selected)) + ) + + self.__updateOkButton() + + @pyqtSlot() + def __nameCompleterActivated(self): + """ + Private slot handling the activation of the completer. + """ + if self.okButton.isEnabled(): + self.okButton.animateClick() + + @pyqtSlot(str) + def on_nameEdit_textChanged(self, name): + """ + Private slot handling the editing of a file or directory name. + + @param name current text of the name edit + @type str + """ + self.listing.clearSelection() + items = self.listing.findItems(name, Qt.MatchFlag.MatchExactly) + for itm in items: + itm.setSelected(True) + + self.__updateOkButton() + + def __getNames(self): + """ + Private method to get the selected names list. + + @return list containing the selected names + @rtype list of str + """ + namesStr = self.nameEdit.text() + if namesStr.startswith('"'): + namesStr = namesStr[1:] + if namesStr.endswith('"'): + namesStr = namesStr[:-1] + names = re.split(r'"\s+"', namesStr) + return names + + def __getFullPath(self, name): + """ + Private method to get the full path for a given file or directory name. + + @param name name of the file or directory + @type str + @return full path of the file or directory + @rtype str + """ + return "{0}{1}{2}".format(self.treeCombo.currentText(), self.__sep, name) + + @pyqtSlot() + def __updateOkButton(self): + """ + Private slot to set the 'OK' button state, icon and label. + """ + # 1. adjust icon and label + if ( + self.__acceptMode == AcceptMode.AcceptOpen + or self.__selectedDirectory is not None + ): + self.okButton.setIcon(EricPixmapCache.getIcon("dialog-ok")) + self.okButton.setText(self.tr("Open")) + else: + self.okButton.setIcon(EricPixmapCache.getIcon("fileSave")) + self.okButton.setText(self.tr("Save")) + + # 2. adjust enabled state + if self.__selectedDirectory and self.__fileMode != FileMode.Directory: + self.okButton.setEnabled(True) + elif self.__fileMode == FileMode.AnyFile: + self.okButton.setEnabled(bool(self.nameEdit.text())) + elif self.__fileMode == FileMode.ExistingFile: + self.okButton.setEnabled( + self.nameEdit.text() in self.__filenameCache + ) + elif self.__fileMode == FileMode.ExistingFiles: + names = self.__getNames() + self.okButton.setEnabled( + all(n in self.__filenameCache for n in names) + ) + elif self.__fileMode == FileMode.Directory: + self.okButton.setEnabled( + self.nameEdit.text() in self.__directoryCache + ) + else: + self.okButton.setEnabled(False) + + @pyqtSlot() + def on_okButton_clicked(self): + """ + Private slot handling the press of the OK button. + """ + if self.__selectedDirectory and self.__fileMode != FileMode.Directory: + self.setDirectory(self.__getFullPath(self.__selectedDirectory)) + else: + self.accept() + + @pyqtSlot(int) + def on_filterCombo_currentIndexChanged(self, index): + """ + Slot documentation goes here. + + @param index DESCRIPTION + @type int + """ + filters = self.__filters[index] + self.__filterList(filters) + + @pyqtSlot(str) + def setDirectory(self, directory): + """ + Public slot to set the current directory and populate the tree list. + + @param directory directory to be set as current. An empty directory sets the + server's current directory. + @type str + """ + self.__filenameCache.clear() + self.__directoryCache.clear() + + + directory, sep, dirListing = self.__fsInterface.listdir(directory) + + self.__sep = sep + + # 1. populate the directory tree combo box + self.treeCombo.blockSignals(True) + self.treeCombo.clear() + if len(directory) > 1 and directory.endswith(sep): + directory = directory[:-1] + if len(directory) > 2 and directory[1] == ":": + # starts with a Windows drive letter + directory = directory[2:] + directoryParts = directory.split(sep) + while directoryParts: + if directoryParts[-1]: + self.treeCombo.addItem(sep.join(directoryParts)) + directoryParts.pop() + self.treeCombo.addItem(sep) + self.treeCombo.blockSignals(False) + + # 2. populate the directory listing + self.listing.clear() + for dirEntry in sorted( + dirListing, + key=lambda d: " " + d['name'].lower() if d["is_dir"] else d["name"].lower(), + ): + if dirEntry["is_dir"]: + type_ = self.tr("Directory") + iconName = "dirClosed" + sizeStr = "" + self.__directoryCache.append(dirEntry["name"]) + else: + type_ = self.tr("File") + iconName = self.__iconProvider.fileIconName(dirEntry["name"]) + sizeStr = dataString(dirEntry["size"], QLocale.system()) + self.__filenameCache.append(dirEntry["name"]) + itm = QTreeWidgetItem( + self.listing, + [dirEntry["name"], sizeStr, type_, dirEntry["mtime"]] + ) + itm.setIcon(0, EricPixmapCache.getIcon(iconName)) + itm.setTextAlignment(1, Qt.AlignmentFlag.AlignRight) + itm.setTextAlignment(2, Qt.AlignmentFlag.AlignHCenter) + itm.setData(0, EricServerFileDialog.IsDirectoryRole, dirEntry["is_dir"]) + + + filters = self.__filters[self.filterCombo.currentIndex()] + self.__filterList(filters) + + # 3. add the directory to the history + self.__addToHistory(directory) + + # 4. update some dependent states + self.nameEdit.clear() + self.__updateUpButton() + + @pyqtSlot(QPoint) + def on_listing_customContextMenuRequested(self, pos): + """ + Priovate slot to show a context menu. + + @param pos mouse pointer position to show the menu at + @type QPoint + """ + self.__contextMenu.clear() + + itm = self.listing.itemAt(pos) + if itm is not None: + self.__contextMenu.addAction( + self.tr("Rename"), lambda: self.__renameItem(itm) + ) + self.__contextMenu.addAction( + self.tr("Delete"), lambda: self.__deleteItem(itm) + ) + self.__contextMenu.addSeparator() + act = self.__contextMenu.addAction(self.tr("Show Hidden Files")) + act.setCheckable(True) + act.setChecked(self.__showHidden) + act.toggled.connect(self.__showHiddenToggled) + self.__contextMenu.addAction( + self.tr("New Directory"), self.on_newDirButton_clicked + ) + + self.__contextMenu.popup(self.listing.mapToGlobal(pos)) + + @pyqtSlot(QTreeWidgetItem) + def __renameItem(self, item): + """ + Private slot to rename the given file/directory item. + + @param item reference to the item to be renamed + @type QTreeWidgetItem + """ + title = ( + self.tr("Rename Directory") + if item.data(0, EricServerFileDialog.IsDirectoryRole) + else self.tr("Rename File") + ) + + newName, ok = QInputDialog.getText( + self, + title, + self.tr("<p>Enter the new name <b>{0}</b>:</p>").format(item.text(0)), + QLineEdit.EchoMode.Normal, + item.text(0), + ) + if ok and newName: + if newName in self.__directoryCache or newName in self.__filenameCache: + EricMessageBox.warning( + self, + title, + self.tr( + "<p>A file or directory with the name <b>{0}</b> exists" + " already. Aborting...</p>" + ).format(newName), + ) + return + + ok, error = self.__fsInterface.replace( + self.__getFullPath(item.text(0)), self.__getFullPath(newName) + ) + if ok: + # refresh + self.__reload() + else: + EricMessageBox.critical( + self, + title, + self.tr( + "<p>The renaming operation failed.</p>" + "<p>Reason: {0}</p>" + ).format(error if error else self.tr("Unknown")), + ) + + @pyqtSlot(QTreeWidgetItem) + def __deleteItem(self, item): + """ + Private slot to delete the given file/directory item. + + @param item reference to the item to be deleted + @type QTreeWidgetItem + """ + isDir = item.data(0, EricServerFileDialog.IsDirectoryRole) + if isDir: + title = self.tr("Delete Directory") + itemType = self.tr("directory") + else: + title = self.tr("Delete File") + itemType = self.tr("file") + + yes = EricMessageBox.yesNo( + self, + title, + self.tr("Shall the selected {0} really be deleted?").format(itemType), + ) + if yes: + ok, error = ( + self.__fsInterface.rmdir(self.__getFullPath(item.text(0))) + if isDir + else self.__fsInterface.remove(self.__getFullPath(item.text(0))) + ) + if ok: + # refresh + self.__reload() + else: + EricMessageBox.critical( + self, + title, + self.tr( + "<p>The deletion operation failed.</p>" + "<p>Reason: {0}</p>" + ).format(error if error else self.tr("Unknown")), + ) + + @pyqtSlot(bool) + def __showHiddenToggled(self, on): + """ + Private slot to handle toggling the display of hidden files/directories. + + @param on flag indicating to show hidden files and directories + @type bool + """ + self.__showHidden = on + filters = self.__filters[self.filterCombo.currentIndex()] + self.__filterList(filters) + + def selectedFiles(self): + """ + Public method to get the selected files or the current viewport path. + + @return selected files or current viewport path + @rtype str + """ + return [self.__getFullPath(n) for n in self.__getNames()] + + def selectedNameFilter(self): + """ + Public method to get the selected name filter. + + @return selected name filter + @rtype str + """ + return self.filterCombo.currentText() + + def __isHidden(self, name): + """ + Private method to check, if the given name is indicating a hidden file or + directory. + + @param name name of the file or directory + @type str + @return flag indicating a hidden file or directory + @rtype bool + """ + return name.startswith(".") or name.endswith("~") + + def __filterList(self, filters): + """ + Private method to filter the files and directories list based on the given + filters and whether hidden files/directories should be shown. + + @param filters list of filter patterns (only applied to files + @type list of str + """ + self.listing.clearSelection() + for row in range(self.listing.topLevelItemCount()): + itm = self.listing.topLevelItem(row) + name = itm.text(0) + if ( + self.__dirsOnly + and not itm.data(0, EricServerFileDialog.IsDirectoryRole) + ): + itm.setHidden(True) + elif not self.__showHidden and self.__isHidden(name): + # applies to files and directories + itm.setHidden(True) + elif not itm.data(0, EricServerFileDialog.IsDirectoryRole): + # it is a file item, apply the filter + itm.setHidden(not any(fnmatch.fnmatch(name, pat) for pat in filters)) + else: + itm.setHidden(False) + + # resize the columns + for column in range(4): + self.listing.resizeColumnToContents(column) + +########################################################################### +## Module functions mimicing the interface of EricFileDialog/QFileDialog +########################################################################### + + +def getOpenFileName( + parent=None, caption="", directory="", filterStr="", initialFilter="", + withRemote=True +): + """ + Module function to get the name of a file for opening it. + + @param parent parent widget of the dialog (defaults to None) + @type QWidget (optional) + @param caption window title of the dialog (defaults to "") + @type str (optional) + @param directory working directory of the dialog (defaults to "") + @type str (optional) + @param filterStr filter string for the dialog (defaults to "") + @type str (optional) + @param initialFilter initial filter for the dialog (defaults to "") + @type str (optional) + @param withRemote flag indicating to create the file names with the remote + indicator (defaults to True) + @type bool (optional) + @return name of file to be opened + @rtype str + """ + return getOpenFileNameAndFilter( + parent, caption, directory, filterStr, initialFilter, withRemote + )[0] + + +def getOpenFileNameAndFilter( + parent=None, caption="", directory="", filterStr="", initialFilter="", + withRemote=True +): + """ + Module function to get the name of a file for opening it and the selected + file name filter. + + @param parent parent widget of the dialog (defaults to None) + @type QWidget (optional) + @param caption window title of the dialog (defaults to "") + @type str (optional) + @param directory working directory of the dialog (defaults to "") + @type str (optional) + @param filterStr filter string for the dialog (defaults to "") + @type str (optional) + @param initialFilter initial filter for the dialog (defaults to "") + @type str (optional) + @param withRemote flag indicating to create the file names with the remote + indicator (defaults to True) + @type bool (optional) + @return tuple containing the list of file names to be opened and the + selected file name filter + @rtype tuple of (list of str, str) + """ + dlg = EricServerFileDialog( + parent=parent, caption=caption, directory=directory, filter=filterStr + ) + dlg.setFileMode(FileMode.ExistingFile) + dlg.setNameFilter(initialFilter) + if dlg.exec() == QDialog.DialogCode.Accepted: + if withRemote: + fileName = FileSystemUtilities.remoteFileName(dlg.selectedFiles()[0]) + else: + fileName = dlg.selectedFiles()[0] + selectedFilter = dlg.selectedNameFilter() + else: + fileName = "" + selectedFilter = "" + + return fileName, selectedFilter + + +def getOpenFileNames( + parent=None, caption="", directory="", filterStr="", initialFilter="", + withRemote=True +): + """ + Module function to get a list of names of files for opening. + + @param parent parent widget of the dialog (defaults to None) + @type QWidget (optional) + @param caption window title of the dialog (defaults to "") + @type str (optional) + @param directory working directory of the dialog (defaults to "") + @type str (optional) + @param filterStr filter string for the dialog (defaults to "") + @type str (optional) + @param initialFilter initial filter for the dialog (defaults to "") + @type str (optional) + @param withRemote flag indicating to create the file names with the remote + indicator (defaults to True) + @type bool (optional) + @return list of file names to be opened + @rtype list of str + """ + return getOpenFileNamesAndFilter( + parent, caption, directory, filterStr, initialFilter, withRemote + )[0] + + +def getOpenFileNamesAndFilter( + parent=None, caption="", directory="", filterStr="", initialFilter="", + withRemote=True +): + """ + Module function to get a list of names of files for opening and the + selected file name filter. + + @param parent parent widget of the dialog (defaults to None) + @type QWidget (optional) + @param caption window title of the dialog (defaults to "") + @type str (optional) + @param directory working directory of the dialog (defaults to "") + @type str (optional) + @param filterStr filter string for the dialog (defaults to "") + @type str (optional) + @param initialFilter initial filter for the dialog (defaults to "") + @type str (optional) + @param withRemote flag indicating to create the file names with the remote + indicator (defaults to True) + @type bool (optional) + @return tuple containing the list of file names to be opened and the + selected file name filter + @rtype tuple of (list of str, str) + """ + dlg = EricServerFileDialog( + parent=parent, caption=caption, directory=directory, filter=filterStr + ) + dlg.setFileMode(FileMode.ExistingFiles) + dlg.setNameFilter(initialFilter) + if dlg.exec() == QDialog.DialogCode.Accepted: + if withRemote: + filesList = [ + FileSystemUtilities.remoteFileName(f) for f in dlg.selectedFiles() + ] + else: + filesList = dlg.selectedFiles() + selectedFilter = dlg.selectedNameFilter() + else: + filesList = [] + selectedFilter = "" + + return filesList, selectedFilter + + +def getSaveFileName( + parent=None, caption="", directory="", filterStr="", initialFilter="", + withRemote=True +): + """ + Module function to get the name of a file for saving. + + @param parent parent widget of the dialog (defaults to None) + @type QWidget (optional) + @param caption window title of the dialog (defaults to "") + @type str (optional) + @param directory working directory of the dialog (defaults to "") + @type str (optional) + @param filterStr filter string for the dialog (defaults to "") + @type str (optional) + @param initialFilter initial filter for the dialog (defaults to "") + @type str (optional) + @param withRemote flag indicating to create the file names with the remote + indicator (defaults to True) + @type bool (optional) + @return name of file to be saved + @rtype str + """ + return getSaveFileNameAndFilter( + parent, caption, directory, filterStr, initialFilter, withRemote + )[0] + + +def getSaveFileNameAndFilter( + parent=None, caption="", directory="", filterStr="", initialFilter="", + withRemote=True +): + """ + Module function to get the name of a file for saving and the selected file name + filter. + + @param parent parent widget of the dialog (defaults to None) + @type QWidget (optional) + @param caption window title of the dialog (defaults to "") + @type str (optional) + @param directory working directory of the dialog (defaults to "") + @type str (optional) + @param filterStr filter string for the dialog (defaults to "") + @type str (optional) + @param initialFilter initial filter for the dialog (defaults to "") + @type str (optional) + @param withRemote flag indicating to create the file names with the remote + indicator (defaults to True) + @type bool (optional) + @return name of file to be saved and selected filte + @rtype tuple of (str, str) + """ + dlg = EricServerFileDialog( + parent=parent, caption=caption, directory=directory, filter=filterStr + ) + dlg.setFileMode(FileMode.AnyFile) + dlg.setNameFilter(initialFilter) + if dlg.exec() == QDialog.DialogCode.Accepted: + if withRemote: + fileName = FileSystemUtilities.remoteFileName(dlg.selectedFiles()[0]) + else: + fileName = dlg.selectedFiles()[0] + selectedFilter = dlg.selectedNameFilter() + else: + fileName = "" + selectedFilter = "" + + return fileName, selectedFilter + + +def getExistingDirectory( + parent=None, caption="", directory="", dirsOnly=True, withRemote=True +): + """ + Module function to get the name of a directory. + + @param parent parent widget of the dialog (defaults to None) + @type QWidget (optional) + @param caption window title of the dialog (defaults to "") + @type str (optional) + @param directory working directory of the dialog (defaults to "") + @type str (optional) + @param dirsOnly flag indicating to just show directories (defaults to True) + @type bool (optional) + @param withRemote flag indicating to create the file names with the remote + indicator (defaults to True) + @type bool (optional) + @return name of selected directory + @rtype str + """ + dlg = EricServerFileDialog(parent=parent, caption=caption, directory=directory) + dlg.setFileMode(FileMode.Directory) + dlg.setDirectoriesOnly(dirsOnly) + if dlg.exec() == QDialog.DialogCode.Accepted: + if withRemote: + dirName = FileSystemUtilities.remoteFileName(dlg.selectedFiles()[0]) + else: + dirName = dlg.selectedFiles()[0] + else: + dirName = "" + + return dirName