eric6/UI/Browser.py

changeset 8114
b7abbf3f82a3
parent 8109
486984bb52e3
child 8143
2c730d5fd177
--- a/eric6/UI/Browser.py	Thu Feb 18 17:43:32 2021 +0100
+++ b/eric6/UI/Browser.py	Thu Feb 18 17:44:53 2021 +0100
@@ -8,6 +8,7 @@
 """
 
 import os
+import shutil
 
 from PyQt5.QtCore import (
     pyqtSignal, pyqtSlot, Qt, QUrl, QCoreApplication, QItemSelectionModel,
@@ -15,7 +16,8 @@
 )
 from PyQt5.QtGui import QDesktopServices
 from PyQt5.QtWidgets import (
-    QTreeView, QApplication, QMenu, QAbstractItemView, QAction
+    QTreeView, QApplication, QMenu, QAbstractItemView, QAction, QInputDialog,
+    QLineEdit, QDialog
 )
 
 from E5Gui.E5Application import e5App
@@ -25,7 +27,8 @@
 from .BrowserModel import (
     BrowserModel, BrowserDirectoryItem, BrowserFileItem, BrowserClassItem,
     BrowserMethodItem, BrowserClassAttributeItem, BrowserImportItem,
-    BrowserImportsItem, BrowserSysPathItem, BrowserGlobalsItem
+    BrowserImportsItem, BrowserSysPathItem, BrowserGlobalsItem,
+    BrowserItemDirectory
 )
 from .BrowserSortFilterProxyModel import BrowserSortFilterProxyModel
 
@@ -213,6 +216,14 @@
         self.showHiddenFilesAct.setChecked(
             Preferences.getUI("BrowsersListHiddenFiles"))
         
+        self.__newMenu = QMenu(QCoreApplication.translate('Browser', "New"),
+                               self)
+        self.__newMenu.addAction(
+            QCoreApplication.translate('Browser', 'Directory'),
+            self._newDirectory)
+        self.__newMenu.addAction(
+            QCoreApplication.translate('Browser', 'File'), self._newFile)
+        
         # create the popup menu for source files
         self.sourceMenu = QMenu(self)
         self.sourceMenu.addAction(
@@ -234,6 +245,11 @@
             self._copyToClipboard)
         self.sourceMenu.addSeparator()
         self.sourceMenu.addAction(self.showHiddenFilesAct)
+        self.sourceMenu.addSeparator()
+        self.sourceMenu.addMenu(self.__newMenu)
+        self.sourceMenu.addAction(
+            QCoreApplication.translate('Browser', 'Delete'),
+            self._deleteFileOrDirectory)
         
         # create the popup menu for general use
         self.menu = QMenu(self)
@@ -255,6 +271,11 @@
             self._copyToClipboard)
         self.menu.addSeparator()
         self.menu.addAction(self.showHiddenFilesAct)
+        self.menu.addSeparator()
+        self.menu.addMenu(self.__newMenu)
+        self.menu.addAction(
+            QCoreApplication.translate('Browser', 'Delete'),
+            self._deleteFileOrDirectory)
         
         # create the menu for multiple selected files
         self.multiMenu = QMenu(self)
@@ -262,6 +283,10 @@
             QCoreApplication.translate('Browser', 'Open'), self._openItem)
         self.multiMenu.addSeparator()
         self.multiMenu.addAction(self.showHiddenFilesAct)
+        self.multiMenu.addSeparator()
+        self.multiMenu.addAction(
+            QCoreApplication.translate('Browser', 'Delete'),
+            self.__deleteMultiple)
         
         # create the directory menu
         self.dirMenu = QMenu(self)
@@ -291,6 +316,11 @@
             self._copyToClipboard)
         self.dirMenu.addSeparator()
         self.dirMenu.addAction(self.showHiddenFilesAct)
+        self.dirMenu.addSeparator()
+        self.dirMenu.addMenu(self.__newMenu)
+        self.dirMenu.addAction(
+            QCoreApplication.translate('Browser', 'Delete'),
+            self._deleteFileOrDirectory)
         
         # create the attribute menu
         self.gotoMenu = QMenu(QCoreApplication.translate('Browser', "Goto"),
@@ -698,7 +728,7 @@
         Public method to get the selected items.
         
         @param filterList list of classes to check against
-        @return list of selected items (list of BroweserItem)
+        @return list of selected items (list of BrowserItem)
         """
         selectedItems = []
         indexes = self.selectedIndexes()
@@ -819,3 +849,202 @@
         """
         return isinstance(
             item, (BrowserDirectoryItem, BrowserFileItem, BrowserSysPathItem))
+    
+    @pyqtSlot()
+    def _newDirectory(self):
+        """
+        Protected slot to create a new directory.
+        """
+        index = self.currentIndex()
+        if index.isValid():
+            dname = self.model().item(index).dirName()
+            newName, ok = QInputDialog.getText(
+                self,
+                self.tr("New Directory"),
+                self.tr("Name for new directory:"),
+                QLineEdit.Normal)
+            if ok and bool(newName):
+                dirpath = os.path.join(dname, newName)
+                if os.path.exists(dirpath):
+                    E5MessageBox.warning(
+                        self,
+                        self.tr("New Directory"),
+                        self.tr("A file or directory named <b>{0}</b> exists"
+                                " already. Aborting...")
+                        .format(newName))
+                else:
+                    try:
+                        os.mkdir(dirpath, mode=0o751)
+                    except OSError as err:
+                        E5MessageBox.critical(
+                            self,
+                            self.tr("New Directory"),
+                            self.tr("<p>The directory <b>{0}</b> could not be"
+                                    " created.</p><p>Reason: {1}</p>")
+                            .format(newName, str(err)))
+    
+    @pyqtSlot()
+    def _newFile(self):
+        """
+        Protected slot to create a new file.
+        """
+        index = self.currentIndex()
+        if index.isValid():
+            dname = self.model().item(index).dirName()
+            fname, ok = QInputDialog.getText(
+                self,
+                self.tr("New File"),
+                self.tr("Name for new file:"),
+                QLineEdit.Normal)
+            if ok and bool(fname):
+                filepath = os.path.join(dname, fname)
+                if os.path.exists(filepath):
+                    E5MessageBox.warning(
+                        self,
+                        self.tr("New File"),
+                        self.tr("A file or directory named <b>{0}</b> exists"
+                                " already. Aborting...")
+                        .format(fname))
+                else:
+                    try:
+                        f = open(filepath, "w")
+                        f.close()
+                    except OSError as err:
+                        E5MessageBox.critical(
+                            self,
+                            self.tr("New File"),
+                            self.tr("<p>The file <b>{0}</b> could not be"
+                                    " created.</p><p>Reason: {1}</p>")
+                            .format(fname, str(err)))
+    
+    @pyqtSlot()
+    def _deleteFileOrDirectory(self):
+        """
+        Protected slot to delete a directory or file.
+        """
+        index = self.currentIndex()
+        if index.isValid():
+            itm = self.model().item(index)
+            if itm.type() == BrowserItemDirectory:
+                self.__deleteDirectory(itm.dirName())
+            else:
+                self.__deleteFile(itm.fileName())
+    
+    def __deleteFile(self, fn):
+        """
+        Private method to delete a file.
+        
+        @param fn filename to be deleted
+        @type str
+        """
+        try:
+            from ThirdParty.Send2Trash.send2trash import send2trash as s2t
+            trashMsg = self.tr("Do you really want to move this file to the"
+                               " trash?")
+        except ImportError:
+            s2t = os.remove
+            trashMsg = self.tr("Do you really want to delete this file?")
+        
+        from UI.DeleteFilesConfirmationDialog import (
+            DeleteFilesConfirmationDialog
+        )
+        dlg = DeleteFilesConfirmationDialog(
+            self.parent(),
+            self.tr("Delete File"),
+            trashMsg,
+            [fn])
+        if dlg.exec() == QDialog.Accepted:
+            try:
+                s2t(fn)
+            except OSError as err:
+                E5MessageBox.critical(
+                    self.ui,
+                    self.tr("Delete File"),
+                    self.tr(
+                        "<p>The selected file <b>{0}</b> could not be"
+                        " deleted.</p><p>Reason: {1}</p>")
+                    .format(fn, str(err))
+                )
+    
+    def __deleteDirectory(self, dn):
+        """
+        Private method to delete a directory.
+        
+        @param dn directory name to be removed from the project
+        @type str
+        """
+        try:
+            from ThirdParty.Send2Trash.send2trash import send2trash
+            s2tAvailable = True
+            trashMsg = self.tr("Do you really want to move this directory to"
+                               " the trash?")
+        except ImportError:
+            s2tAvailable = False
+            trashMsg = self.tr("Do you really want to delete this directory?")
+        
+        from UI.DeleteFilesConfirmationDialog import (
+            DeleteFilesConfirmationDialog
+        )
+        dlg = DeleteFilesConfirmationDialog(
+            self.parent(),
+            self.tr("Delete Directory"),
+            trashMsg,
+            [dn])
+        if dlg.exec() == QDialog.Accepted:
+            try:
+                if s2tAvailable:
+                    send2trash(dn)
+                else:
+                    shutil.rmtree(dn, True)
+            except OSError as err:
+                E5MessageBox.critical(
+                    self.ui,
+                    self.tr("Delete Directory"),
+                    self.tr(
+                        "<p>The selected directory <b>{0}</b> could not be"
+                        " deleted.</p><p>Reason: {1}</p>")
+                    .format(dn, str(err))
+                )
+    
+    @pyqtSlot()
+    def __deleteMultiple(self):
+        """
+        Private slot to delete multiple directories and files.
+        
+        Note: The context menu for multi selection is only shown for file
+        items.
+        """
+        fileNames = []
+        for itm in self.getSelectedItems():
+            fileNames.append(itm.fileName())
+        
+        try:
+            from ThirdParty.Send2Trash.send2trash import send2trash as s2t
+            trashMsg = self.tr("Do you really want to move these files to the"
+                               " trash?")
+        except ImportError:
+            s2t = os.remove
+            trashMsg = self.tr("Do you really want to delete these files?")
+        
+        from UI.DeleteFilesConfirmationDialog import (
+            DeleteFilesConfirmationDialog
+        )
+        dlg = DeleteFilesConfirmationDialog(
+            self.parent(),
+            self.tr("Delete Files"),
+            trashMsg,
+            sorted(fileNames)
+        )
+        if dlg.exec() == QDialog.Accepted:
+            for fn in fileNames:
+                try:
+                    s2t(fn)
+                except OSError as err:
+                    E5MessageBox.critical(
+                        self.ui,
+                        self.tr("Delete File"),
+                        self.tr(
+                            "<p>The selected file <b>{0}</b> could not be"
+                            " deleted.</p><p>Reason: {1}</p>")
+                        .format(fn, str(err))
+                    )

eric ide

mercurial