src/eric7/UI/Browser.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9278
36448ca469c2
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
9 9
10 import os 10 import os
11 import shutil 11 import shutil
12 12
13 from PyQt6.QtCore import ( 13 from PyQt6.QtCore import (
14 pyqtSignal, pyqtSlot, Qt, QUrl, QCoreApplication, QItemSelectionModel, 14 pyqtSignal,
15 QModelIndex, QElapsedTimer 15 pyqtSlot,
16 Qt,
17 QUrl,
18 QCoreApplication,
19 QItemSelectionModel,
20 QModelIndex,
21 QElapsedTimer,
16 ) 22 )
17 from PyQt6.QtGui import QAction, QDesktopServices 23 from PyQt6.QtGui import QAction, QDesktopServices
18 from PyQt6.QtWidgets import ( 24 from PyQt6.QtWidgets import (
19 QTreeView, QApplication, QMenu, QAbstractItemView, QInputDialog, 25 QTreeView,
20 QLineEdit, QDialog 26 QApplication,
27 QMenu,
28 QAbstractItemView,
29 QInputDialog,
30 QLineEdit,
31 QDialog,
21 ) 32 )
22 33
23 from EricWidgets.EricApplication import ericApp 34 from EricWidgets.EricApplication import ericApp
24 from EricWidgets import EricFileDialog, EricMessageBox 35 from EricWidgets import EricFileDialog, EricMessageBox
25 36
26 from Project.ProjectBrowserModel import ProjectBrowserSimpleDirectoryItem 37 from Project.ProjectBrowserModel import ProjectBrowserSimpleDirectoryItem
27 from .BrowserModel import ( 38 from .BrowserModel import (
28 BrowserModel, BrowserDirectoryItem, BrowserFileItem, BrowserClassItem, 39 BrowserModel,
29 BrowserMethodItem, BrowserClassAttributeItem, BrowserImportItem, 40 BrowserDirectoryItem,
30 BrowserImportsItem, BrowserSysPathItem, BrowserGlobalsItem, 41 BrowserFileItem,
31 BrowserItemDirectory 42 BrowserClassItem,
43 BrowserMethodItem,
44 BrowserClassAttributeItem,
45 BrowserImportItem,
46 BrowserImportsItem,
47 BrowserSysPathItem,
48 BrowserGlobalsItem,
49 BrowserItemDirectory,
32 ) 50 )
33 from .BrowserSortFilterProxyModel import BrowserSortFilterProxyModel 51 from .BrowserSortFilterProxyModel import BrowserSortFilterProxyModel
34 52
35 import UI.PixmapCache 53 import UI.PixmapCache
36 import Preferences 54 import Preferences
39 57
40 58
41 class Browser(QTreeView): 59 class Browser(QTreeView):
42 """ 60 """
43 Class used to display a file system tree. 61 Class used to display a file system tree.
44 62
45 Via the context menu that 63 Via the context menu that
46 is displayed by a right click the user can select various actions on 64 is displayed by a right click the user can select various actions on
47 the selected file. 65 the selected file.
48 66
49 @signal sourceFile(filename) emitted to open a Python file at a line (str) 67 @signal sourceFile(filename) emitted to open a Python file at a line (str)
50 @signal sourceFile(filename, lineno) emitted to open a Python file at a 68 @signal sourceFile(filename, lineno) emitted to open a Python file at a
51 line (str, int) 69 line (str, int)
52 @signal sourceFile(filename, lineno, type) emitted to open a Python file 70 @signal sourceFile(filename, lineno, type) emitted to open a Python file
53 at a line giving an explicit file type (str, int, str) 71 at a line giving an explicit file type (str, int, str)
70 @signal umlFile(filename) emitted to open an eric UML file (str) 88 @signal umlFile(filename) emitted to open an eric UML file (str)
71 @signal binaryFile(filename) emitted to open a file as binary (str) 89 @signal binaryFile(filename) emitted to open a file as binary (str)
72 @signal testFile(filename) emitted to open a Python file for a 90 @signal testFile(filename) emitted to open a Python file for a
73 unit test (str) 91 unit test (str)
74 """ 92 """
75 sourceFile = pyqtSignal((str, ), (str, int), (str, list), (str, int, str)) 93
94 sourceFile = pyqtSignal((str,), (str, int), (str, list), (str, int, str))
76 designerFile = pyqtSignal(str) 95 designerFile = pyqtSignal(str)
77 linguistFile = pyqtSignal(str) 96 linguistFile = pyqtSignal(str)
78 trpreview = pyqtSignal((list, ), (list, bool)) 97 trpreview = pyqtSignal((list,), (list, bool))
79 projectFile = pyqtSignal(str) 98 projectFile = pyqtSignal(str)
80 multiProjectFile = pyqtSignal(str) 99 multiProjectFile = pyqtSignal(str)
81 pixmapFile = pyqtSignal(str) 100 pixmapFile = pyqtSignal(str)
82 pixmapEditFile = pyqtSignal(str) 101 pixmapEditFile = pyqtSignal(str)
83 svgFile = pyqtSignal(str) 102 svgFile = pyqtSignal(str)
84 umlFile = pyqtSignal(str) 103 umlFile = pyqtSignal(str)
85 binaryFile = pyqtSignal(str) 104 binaryFile = pyqtSignal(str)
86 testFile = pyqtSignal(str) 105 testFile = pyqtSignal(str)
87 106
88 def __init__(self, parent=None): 107 def __init__(self, parent=None):
89 """ 108 """
90 Constructor 109 Constructor
91 110
92 @param parent parent widget (QWidget) 111 @param parent parent widget (QWidget)
93 """ 112 """
94 super().__init__(parent) 113 super().__init__(parent)
95 114
96 self.setWindowTitle(QCoreApplication.translate('Browser', 115 self.setWindowTitle(QCoreApplication.translate("Browser", "File-Browser"))
97 'File-Browser'))
98 self.setWindowIcon(UI.PixmapCache.getIcon("eric")) 116 self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
99 117
100 self.__model = BrowserModel() 118 self.__model = BrowserModel()
101 self.__sortModel = BrowserSortFilterProxyModel() 119 self.__sortModel = BrowserSortFilterProxyModel()
102 self.__sortModel.setSourceModel(self.__model) 120 self.__sortModel.setSourceModel(self.__model)
103 self.setModel(self.__sortModel) 121 self.setModel(self.__sortModel)
104 122
105 self.selectedItemsFilter = [BrowserFileItem] 123 self.selectedItemsFilter = [BrowserFileItem]
106 124
107 self._activating = False 125 self._activating = False
108 126
109 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 127 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
110 self.customContextMenuRequested.connect(self._contextMenuRequested) 128 self.customContextMenuRequested.connect(self._contextMenuRequested)
111 self.activated.connect(self._openItem) 129 self.activated.connect(self._openItem)
112 self.expanded.connect(self._resizeColumns) 130 self.expanded.connect(self._resizeColumns)
113 self.collapsed.connect(self._resizeColumns) 131 self.collapsed.connect(self._resizeColumns)
114 132
115 self.setWhatsThis(QCoreApplication.translate( 133 self.setWhatsThis(
116 'Browser', 134 QCoreApplication.translate(
117 """<b>The Browser Window</b>""" 135 "Browser",
118 """<p>This allows you to easily navigate the hierarchy of""" 136 """<b>The Browser Window</b>"""
119 """ directories and files on your system, identify the Python""" 137 """<p>This allows you to easily navigate the hierarchy of"""
120 """ programs and open them up in a Source Viewer window. The""" 138 """ directories and files on your system, identify the Python"""
121 """ window displays several separate hierarchies.</p>""" 139 """ programs and open them up in a Source Viewer window. The"""
122 """<p>The first hierarchy is only shown if you have opened a""" 140 """ window displays several separate hierarchies.</p>"""
123 """ program for debugging and its root is the directory""" 141 """<p>The first hierarchy is only shown if you have opened a"""
124 """ containing that program. Usually all of the separate files""" 142 """ program for debugging and its root is the directory"""
125 """ that make up a Python application are held in the same""" 143 """ containing that program. Usually all of the separate files"""
126 """ directory, so this hierarchy gives you easy access to most""" 144 """ that make up a Python application are held in the same"""
127 """ of what you will need.</p>""" 145 """ directory, so this hierarchy gives you easy access to most"""
128 """<p>The next hierarchy is used to easily navigate the""" 146 """ of what you will need.</p>"""
129 """ directories that are specified in the Python""" 147 """<p>The next hierarchy is used to easily navigate the"""
130 """ <tt>sys.path</tt> variable.</p>""" 148 """ directories that are specified in the Python"""
131 """<p>The remaining hierarchies allow you navigate your system""" 149 """ <tt>sys.path</tt> variable.</p>"""
132 """ as a whole. On a UNIX system there will be a hierarchy with""" 150 """<p>The remaining hierarchies allow you navigate your system"""
133 """ <tt>/</tt> at its root and another with the user home""" 151 """ as a whole. On a UNIX system there will be a hierarchy with"""
134 """ directory. On a Windows system there will be a hierarchy for""" 152 """ <tt>/</tt> at its root and another with the user home"""
135 """ each drive on the""" 153 """ directory. On a Windows system there will be a hierarchy for"""
136 """ system.</p>""" 154 """ each drive on the"""
137 """<p>Python programs (i.e. those with a <tt>.py</tt> file name""" 155 """ system.</p>"""
138 """ suffix) are identified in the hierarchies with a Python""" 156 """<p>Python programs (i.e. those with a <tt>.py</tt> file name"""
139 """ icon. The right mouse button will popup a menu which lets""" 157 """ suffix) are identified in the hierarchies with a Python"""
140 """ you open the file in a Source Viewer window, open the file""" 158 """ icon. The right mouse button will popup a menu which lets"""
141 """ for debugging or use it for a test run.</p>""" 159 """ you open the file in a Source Viewer window, open the file"""
142 """<p>The context menu of a class, function or method allows you""" 160 """ for debugging or use it for a test run.</p>"""
143 """ to open the file defining this class, function or method and""" 161 """<p>The context menu of a class, function or method allows you"""
144 """ will ensure, that the correct source line is visible.</p>""" 162 """ to open the file defining this class, function or method and"""
145 """<p>Qt-Designer files (i.e. those with a <tt>.ui</tt> file""" 163 """ will ensure, that the correct source line is visible.</p>"""
146 """ name suffix) are shown with a Designer icon. The context""" 164 """<p>Qt-Designer files (i.e. those with a <tt>.ui</tt> file"""
147 """ menu of these files allows you to start Qt-Designer with""" 165 """ name suffix) are shown with a Designer icon. The context"""
148 """ that file.</p>""" 166 """ menu of these files allows you to start Qt-Designer with"""
149 """<p>Qt-Linguist files (i.e. those with a <tt>.ts</tt> file""" 167 """ that file.</p>"""
150 """ name suffix) are shown with a Linguist icon. The context""" 168 """<p>Qt-Linguist files (i.e. those with a <tt>.ts</tt> file"""
151 """ menu of these files allows you to start Qt-Linguist with""" 169 """ name suffix) are shown with a Linguist icon. The context"""
152 """ that file.</p>""" 170 """ menu of these files allows you to start Qt-Linguist with"""
153 )) 171 """ that file.</p>""",
154 172 )
173 )
174
155 self.__createPopupMenus() 175 self.__createPopupMenus()
156 176
157 self._init() # perform common initialization tasks 177 self._init() # perform common initialization tasks
158 178
159 self._keyboardSearchString = "" 179 self._keyboardSearchString = ""
160 self._keyboardSearchTimer = QElapsedTimer() 180 self._keyboardSearchTimer = QElapsedTimer()
161 self._keyboardSearchTimer.invalidate() 181 self._keyboardSearchTimer.invalidate()
162 182
163 def _init(self): 183 def _init(self):
164 """ 184 """
165 Protected method to perform initialization tasks common to this 185 Protected method to perform initialization tasks common to this
166 base class and all derived classes. 186 base class and all derived classes.
167 """ 187 """
168 self.setRootIsDecorated(True) 188 self.setRootIsDecorated(True)
169 self.setAlternatingRowColors(True) 189 self.setAlternatingRowColors(True)
170 190
171 header = self.header() 191 header = self.header()
172 header.setSortIndicator(0, Qt.SortOrder.AscendingOrder) 192 header.setSortIndicator(0, Qt.SortOrder.AscendingOrder)
173 header.setSortIndicatorShown(True) 193 header.setSortIndicatorShown(True)
174 header.setSectionsClickable(True) 194 header.setSectionsClickable(True)
175 195
176 self.setSortingEnabled(True) 196 self.setSortingEnabled(True)
177 197
178 self.setSelectionMode( 198 self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
179 QAbstractItemView.SelectionMode.ExtendedSelection) 199 self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
180 self.setSelectionBehavior( 200
181 QAbstractItemView.SelectionBehavior.SelectRows)
182
183 self.header().setStretchLastSection(True) 201 self.header().setStretchLastSection(True)
184 self.headerSize0 = 0 202 self.headerSize0 = 0
185 self.layoutDisplay() 203 self.layoutDisplay()
186 204
187 def layoutDisplay(self): 205 def layoutDisplay(self):
188 """ 206 """
189 Public slot to perform a layout operation. 207 Public slot to perform a layout operation.
190 """ 208 """
191 self._resizeColumns(QModelIndex()) 209 self._resizeColumns(QModelIndex())
192 self._resort() 210 self._resort()
193 211
194 def _resizeColumns(self, index): 212 def _resizeColumns(self, index):
195 """ 213 """
196 Protected slot to resize the view when items get expanded or collapsed. 214 Protected slot to resize the view when items get expanded or collapsed.
197 215
198 @param index index of item (QModelIndex) 216 @param index index of item (QModelIndex)
199 """ 217 """
200 w = max(100, self.sizeHintForColumn(0)) 218 w = max(100, self.sizeHintForColumn(0))
201 if w != self.headerSize0: 219 if w != self.headerSize0:
202 self.header().resizeSection(0, w) 220 self.header().resizeSection(0, w)
203 self.headerSize0 = w 221 self.headerSize0 = w
204 222
205 def _resort(self): 223 def _resort(self):
206 """ 224 """
207 Protected slot to resort the tree. 225 Protected slot to resort the tree.
208 """ 226 """
209 self.model().sort(self.header().sortIndicatorSection(), 227 self.model().sort(
210 self.header().sortIndicatorOrder()) 228 self.header().sortIndicatorSection(), self.header().sortIndicatorOrder()
211 229 )
230
212 def __createPopupMenus(self): 231 def __createPopupMenus(self):
213 """ 232 """
214 Private method to generate the various popup menus. 233 Private method to generate the various popup menus.
215 """ 234 """
216 self.showHiddenFilesAct = QAction( 235 self.showHiddenFilesAct = QAction(
217 QCoreApplication.translate('Browser', 'Show Hidden Files')) 236 QCoreApplication.translate("Browser", "Show Hidden Files")
237 )
218 self.showHiddenFilesAct.setCheckable(True) 238 self.showHiddenFilesAct.setCheckable(True)
219 self.showHiddenFilesAct.toggled.connect(self._showHidden) 239 self.showHiddenFilesAct.toggled.connect(self._showHidden)
220 self.showHiddenFilesAct.setChecked( 240 self.showHiddenFilesAct.setChecked(Preferences.getUI("BrowsersListHiddenFiles"))
221 Preferences.getUI("BrowsersListHiddenFiles")) 241
222 242 self.__newMenu = QMenu(QCoreApplication.translate("Browser", "New"), self)
223 self.__newMenu = QMenu(QCoreApplication.translate('Browser', "New"),
224 self)
225 self.__newMenu.addAction( 243 self.__newMenu.addAction(
226 QCoreApplication.translate('Browser', 'Directory'), 244 QCoreApplication.translate("Browser", "Directory"), self._newDirectory
227 self._newDirectory) 245 )
228 self.__newMenu.addAction( 246 self.__newMenu.addAction(
229 QCoreApplication.translate('Browser', 'File'), self._newFile) 247 QCoreApplication.translate("Browser", "File"), self._newFile
230 248 )
249
231 # create the popup menu for source files 250 # create the popup menu for source files
232 self.sourceMenu = QMenu(self) 251 self.sourceMenu = QMenu(self)
233 self.sourceMenu.addAction( 252 self.sourceMenu.addAction(
234 QCoreApplication.translate('Browser', 'Open'), self._openItem) 253 QCoreApplication.translate("Browser", "Open"), self._openItem
254 )
235 self.testingAct = self.sourceMenu.addAction( 255 self.testingAct = self.sourceMenu.addAction(
236 QCoreApplication.translate('Browser', 'Run Test...'), 256 QCoreApplication.translate("Browser", "Run Test..."), self.handleTesting
237 self.handleTesting) 257 )
238 self.sourceMenu.addSeparator() 258 self.sourceMenu.addSeparator()
239 self.mimeTypeAct = self.sourceMenu.addAction( 259 self.mimeTypeAct = self.sourceMenu.addAction(
240 QCoreApplication.translate('Browser', 'Show Mime-Type'), 260 QCoreApplication.translate("Browser", "Show Mime-Type"), self.__showMimeType
241 self.__showMimeType) 261 )
242 self.sourceMenu.addSeparator() 262 self.sourceMenu.addSeparator()
243 self.sourceMenu.addAction( 263 self.sourceMenu.addAction(
244 QCoreApplication.translate('Browser', 'Refresh Source File'), 264 QCoreApplication.translate("Browser", "Refresh Source File"),
245 self.__refreshSource) 265 self.__refreshSource,
266 )
246 self.sourceMenu.addSeparator() 267 self.sourceMenu.addSeparator()
247 self.sourceMenu.addAction( 268 self.sourceMenu.addAction(
248 QCoreApplication.translate('Browser', 'Copy Path to Clipboard'), 269 QCoreApplication.translate("Browser", "Copy Path to Clipboard"),
249 self._copyToClipboard) 270 self._copyToClipboard,
271 )
250 self.sourceMenu.addSeparator() 272 self.sourceMenu.addSeparator()
251 self.sourceMenu.addAction(self.showHiddenFilesAct) 273 self.sourceMenu.addAction(self.showHiddenFilesAct)
252 self.sourceMenu.addSeparator() 274 self.sourceMenu.addSeparator()
253 self.sourceMenu.addMenu(self.__newMenu) 275 self.sourceMenu.addMenu(self.__newMenu)
254 self.sourceMenu.addAction( 276 self.sourceMenu.addAction(
255 QCoreApplication.translate('Browser', 'Delete'), 277 QCoreApplication.translate("Browser", "Delete"), self._deleteFileOrDirectory
256 self._deleteFileOrDirectory) 278 )
257 279
258 # create the popup menu for general use 280 # create the popup menu for general use
259 self.menu = QMenu(self) 281 self.menu = QMenu(self)
260 self.menu.addAction( 282 self.menu.addAction(
261 QCoreApplication.translate('Browser', 'Open'), self._openItem) 283 QCoreApplication.translate("Browser", "Open"), self._openItem
284 )
262 self.menu.addAction( 285 self.menu.addAction(
263 QCoreApplication.translate('Browser', 'Open in Hex Editor'), 286 QCoreApplication.translate("Browser", "Open in Hex Editor"),
264 self._openHexEditor) 287 self._openHexEditor,
288 )
265 self.editPixmapAct = self.menu.addAction( 289 self.editPixmapAct = self.menu.addAction(
266 QCoreApplication.translate('Browser', 'Open in Icon Editor'), 290 QCoreApplication.translate("Browser", "Open in Icon Editor"),
267 self._editPixmap) 291 self._editPixmap,
292 )
268 self.openInEditorAct = self.menu.addAction( 293 self.openInEditorAct = self.menu.addAction(
269 QCoreApplication.translate('Browser', 'Open in Editor'), 294 QCoreApplication.translate("Browser", "Open in Editor"),
270 self._openFileInEditor) 295 self._openFileInEditor,
296 )
271 self.menu.addSeparator() 297 self.menu.addSeparator()
272 self.mimeTypeAct = self.menu.addAction( 298 self.mimeTypeAct = self.menu.addAction(
273 QCoreApplication.translate('Browser', 'Show Mime-Type'), 299 QCoreApplication.translate("Browser", "Show Mime-Type"), self.__showMimeType
274 self.__showMimeType) 300 )
275 self.menu.addSeparator() 301 self.menu.addSeparator()
276 self.menu.addAction( 302 self.menu.addAction(
277 QCoreApplication.translate('Browser', 'Copy Path to Clipboard'), 303 QCoreApplication.translate("Browser", "Copy Path to Clipboard"),
278 self._copyToClipboard) 304 self._copyToClipboard,
305 )
279 self.menu.addSeparator() 306 self.menu.addSeparator()
280 self.menu.addAction(self.showHiddenFilesAct) 307 self.menu.addAction(self.showHiddenFilesAct)
281 self.menu.addSeparator() 308 self.menu.addSeparator()
282 self.menu.addMenu(self.__newMenu) 309 self.menu.addMenu(self.__newMenu)
283 self.menu.addAction( 310 self.menu.addAction(
284 QCoreApplication.translate('Browser', 'Delete'), 311 QCoreApplication.translate("Browser", "Delete"), self._deleteFileOrDirectory
285 self._deleteFileOrDirectory) 312 )
286 313
287 # create the menu for multiple selected files 314 # create the menu for multiple selected files
288 self.multiMenu = QMenu(self) 315 self.multiMenu = QMenu(self)
289 self.multiMenu.addAction( 316 self.multiMenu.addAction(
290 QCoreApplication.translate('Browser', 'Open'), self._openItem) 317 QCoreApplication.translate("Browser", "Open"), self._openItem
318 )
291 self.multiMenu.addSeparator() 319 self.multiMenu.addSeparator()
292 self.multiMenu.addAction(self.showHiddenFilesAct) 320 self.multiMenu.addAction(self.showHiddenFilesAct)
293 self.multiMenu.addSeparator() 321 self.multiMenu.addSeparator()
294 self.multiMenu.addAction( 322 self.multiMenu.addAction(
295 QCoreApplication.translate('Browser', 'Delete'), 323 QCoreApplication.translate("Browser", "Delete"), self.__deleteMultiple
296 self.__deleteMultiple) 324 )
297 325
298 # create the directory menu 326 # create the directory menu
299 self.dirMenu = QMenu(self) 327 self.dirMenu = QMenu(self)
300 self.dirMenu.addAction( 328 self.dirMenu.addAction(
301 QCoreApplication.translate('Browser', 'New toplevel directory...'), 329 QCoreApplication.translate("Browser", "New toplevel directory..."),
302 self.__newToplevelDir) 330 self.__newToplevelDir,
331 )
303 self.addAsTopLevelAct = self.dirMenu.addAction( 332 self.addAsTopLevelAct = self.dirMenu.addAction(
304 QCoreApplication.translate('Browser', 'Add as toplevel directory'), 333 QCoreApplication.translate("Browser", "Add as toplevel directory"),
305 self.__addAsToplevelDir) 334 self.__addAsToplevelDir,
335 )
306 self.removeFromToplevelAct = self.dirMenu.addAction( 336 self.removeFromToplevelAct = self.dirMenu.addAction(
307 QCoreApplication.translate('Browser', 'Remove from toplevel'), 337 QCoreApplication.translate("Browser", "Remove from toplevel"),
308 self.__removeToplevel) 338 self.__removeToplevel,
339 )
309 self.dirMenu.addSeparator() 340 self.dirMenu.addSeparator()
310 self.dirMenu.addAction( 341 self.dirMenu.addAction(
311 QCoreApplication.translate('Browser', 'Refresh directory'), 342 QCoreApplication.translate("Browser", "Refresh directory"),
312 self.__refreshDirectory) 343 self.__refreshDirectory,
344 )
313 self.dirMenu.addSeparator() 345 self.dirMenu.addSeparator()
314 self.dirMenu.addAction( 346 self.dirMenu.addAction(
315 QCoreApplication.translate('Browser', 'Find in this directory'), 347 QCoreApplication.translate("Browser", "Find in this directory"),
316 self.__findInDirectory) 348 self.__findInDirectory,
349 )
317 self.dirMenu.addAction( 350 self.dirMenu.addAction(
318 QCoreApplication.translate( 351 QCoreApplication.translate("Browser", "Find && Replace in this directory"),
319 'Browser', 'Find && Replace in this directory'), 352 self.__replaceInDirectory,
320 self.__replaceInDirectory) 353 )
321 self.dirMenu.addAction( 354 self.dirMenu.addAction(
322 QCoreApplication.translate('Browser', 'Copy Path to Clipboard'), 355 QCoreApplication.translate("Browser", "Copy Path to Clipboard"),
323 self._copyToClipboard) 356 self._copyToClipboard,
357 )
324 self.dirMenu.addSeparator() 358 self.dirMenu.addSeparator()
325 self.dirMenu.addAction(self.showHiddenFilesAct) 359 self.dirMenu.addAction(self.showHiddenFilesAct)
326 self.dirMenu.addSeparator() 360 self.dirMenu.addSeparator()
327 self.dirMenu.addMenu(self.__newMenu) 361 self.dirMenu.addMenu(self.__newMenu)
328 self.dirMenu.addAction( 362 self.dirMenu.addAction(
329 QCoreApplication.translate('Browser', 'Delete'), 363 QCoreApplication.translate("Browser", "Delete"), self._deleteFileOrDirectory
330 self._deleteFileOrDirectory) 364 )
331 365
332 # create the attribute menu 366 # create the attribute menu
333 self.gotoMenu = QMenu(QCoreApplication.translate('Browser', "Goto"), 367 self.gotoMenu = QMenu(QCoreApplication.translate("Browser", "Goto"), self)
334 self)
335 self.gotoMenu.aboutToShow.connect(self._showGotoMenu) 368 self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
336 self.gotoMenu.triggered.connect(self._gotoAttribute) 369 self.gotoMenu.triggered.connect(self._gotoAttribute)
337 370
338 self.attributeMenu = QMenu(self) 371 self.attributeMenu = QMenu(self)
339 self.attributeMenu.addAction( 372 self.attributeMenu.addAction(
340 QCoreApplication.translate('Browser', 'New toplevel directory...'), 373 QCoreApplication.translate("Browser", "New toplevel directory..."),
341 self.__newToplevelDir) 374 self.__newToplevelDir,
375 )
342 self.attributeMenu.addSeparator() 376 self.attributeMenu.addSeparator()
343 self.attributeMenu.addMenu(self.gotoMenu) 377 self.attributeMenu.addMenu(self.gotoMenu)
344 378
345 # create the background menu 379 # create the background menu
346 self.backMenu = QMenu(self) 380 self.backMenu = QMenu(self)
347 self.backMenu.addAction( 381 self.backMenu.addAction(
348 QCoreApplication.translate('Browser', 'New toplevel directory...'), 382 QCoreApplication.translate("Browser", "New toplevel directory..."),
349 self.__newToplevelDir) 383 self.__newToplevelDir,
384 )
350 self.backMenu.addSeparator() 385 self.backMenu.addSeparator()
351 self.backMenu.addAction(self.showHiddenFilesAct) 386 self.backMenu.addAction(self.showHiddenFilesAct)
352 387
353 def mouseDoubleClickEvent(self, mouseEvent): 388 def mouseDoubleClickEvent(self, mouseEvent):
354 """ 389 """
355 Protected method of QAbstractItemView. 390 Protected method of QAbstractItemView.
356 391
357 Reimplemented to disable expanding/collapsing of items when 392 Reimplemented to disable expanding/collapsing of items when
358 double-clicking. Instead the double-clicked entry is opened. 393 double-clicking. Instead the double-clicked entry is opened.
359 394
360 @param mouseEvent the mouse event (QMouseEvent) 395 @param mouseEvent the mouse event (QMouseEvent)
361 """ 396 """
362 index = self.indexAt(mouseEvent.position().toPoint()) 397 index = self.indexAt(mouseEvent.position().toPoint())
363 if index.isValid(): 398 if index.isValid():
364 itm = self.model().item(index) 399 itm = self.model().item(index)
365 if isinstance(itm, ( 400 if isinstance(
366 BrowserDirectoryItem, BrowserImportsItem, 401 itm,
367 ProjectBrowserSimpleDirectoryItem, BrowserSysPathItem, 402 (
368 BrowserGlobalsItem)): 403 BrowserDirectoryItem,
404 BrowserImportsItem,
405 ProjectBrowserSimpleDirectoryItem,
406 BrowserSysPathItem,
407 BrowserGlobalsItem,
408 ),
409 ):
369 self.setExpanded(index, not self.isExpanded(index)) 410 self.setExpanded(index, not self.isExpanded(index))
370 else: 411 else:
371 self._openItem() 412 self._openItem()
372 413
373 def _contextMenuRequested(self, coord): 414 def _contextMenuRequested(self, coord):
374 """ 415 """
375 Protected slot to show the context menu of the listview. 416 Protected slot to show the context menu of the listview.
376 417
377 @param coord the position of the mouse pointer (QPoint) 418 @param coord the position of the mouse pointer (QPoint)
378 """ 419 """
379 categories = self.getSelectedItemsCountCategorized( 420 categories = self.getSelectedItemsCountCategorized(
380 [BrowserDirectoryItem, BrowserFileItem, 421 [BrowserDirectoryItem, BrowserFileItem, BrowserClassItem, BrowserMethodItem]
381 BrowserClassItem, BrowserMethodItem]) 422 )
382 cnt = categories["sum"] 423 cnt = categories["sum"]
383 bfcnt = categories[str(BrowserFileItem)] 424 bfcnt = categories[str(BrowserFileItem)]
384 if cnt > 1 and cnt == bfcnt: 425 if cnt > 1 and cnt == bfcnt:
385 self.multiMenu.popup(self.mapToGlobal(coord)) 426 self.multiMenu.popup(self.mapToGlobal(coord))
386 else: 427 else:
387 index = self.indexAt(coord) 428 index = self.indexAt(coord)
388 429
389 if index.isValid(): 430 if index.isValid():
390 self.setCurrentIndex(index) 431 self.setCurrentIndex(index)
391 flags = ( 432 flags = (
392 QItemSelectionModel.SelectionFlag.ClearAndSelect | 433 QItemSelectionModel.SelectionFlag.ClearAndSelect
393 QItemSelectionModel.SelectionFlag.Rows 434 | QItemSelectionModel.SelectionFlag.Rows
394 ) 435 )
395 self.selectionModel().select(index, flags) 436 self.selectionModel().select(index, flags)
396 437
397 itm = self.model().item(index) 438 itm = self.model().item(index)
398 coord = self.mapToGlobal(coord) 439 coord = self.mapToGlobal(coord)
399 if isinstance(itm, BrowserFileItem): 440 if isinstance(itm, BrowserFileItem):
400 if itm.isPython3File(): 441 if itm.isPython3File():
401 if itm.fileName().endswith('.py'): 442 if itm.fileName().endswith(".py"):
402 self.testingAct.setEnabled(True) 443 self.testingAct.setEnabled(True)
403 else: 444 else:
404 self.testingAct.setEnabled(False) 445 self.testingAct.setEnabled(False)
405 self.sourceMenu.popup(coord) 446 self.sourceMenu.popup(coord)
406 else: 447 else:
407 self.editPixmapAct.setVisible(itm.isPixmapFile()) 448 self.editPixmapAct.setVisible(itm.isPixmapFile())
408 self.openInEditorAct.setVisible(itm.isSvgFile()) 449 self.openInEditorAct.setVisible(itm.isSvgFile())
409 self.menu.popup(coord) 450 self.menu.popup(coord)
410 elif isinstance( 451 elif isinstance(
411 itm, 452 itm, (BrowserClassItem, BrowserMethodItem, BrowserImportItem)
412 (BrowserClassItem, BrowserMethodItem, BrowserImportItem)
413 ): 453 ):
414 self.editPixmapAct.setVisible(False) 454 self.editPixmapAct.setVisible(False)
415 self.menu.popup(coord) 455 self.menu.popup(coord)
416 elif isinstance(itm, BrowserClassAttributeItem): 456 elif isinstance(itm, BrowserClassAttributeItem):
417 self.attributeMenu.popup(coord) 457 self.attributeMenu.popup(coord)
425 self.dirMenu.popup(coord) 465 self.dirMenu.popup(coord)
426 else: 466 else:
427 self.backMenu.popup(coord) 467 self.backMenu.popup(coord)
428 else: 468 else:
429 self.backMenu.popup(self.mapToGlobal(coord)) 469 self.backMenu.popup(self.mapToGlobal(coord))
430 470
431 def _showGotoMenu(self): 471 def _showGotoMenu(self):
432 """ 472 """
433 Protected slot to prepare the goto submenu of the attribute menu. 473 Protected slot to prepare the goto submenu of the attribute menu.
434 """ 474 """
435 self.gotoMenu.clear() 475 self.gotoMenu.clear()
436 476
437 itm = self.model().item(self.currentIndex()) 477 itm = self.model().item(self.currentIndex())
438 linenos = itm.linenos() 478 linenos = itm.linenos()
439 fileName = itm.fileName() 479 fileName = itm.fileName()
440 480
441 for lineno in sorted(linenos): 481 for lineno in sorted(linenos):
442 act = self.gotoMenu.addAction( 482 act = self.gotoMenu.addAction(
443 QCoreApplication.translate( 483 QCoreApplication.translate("Browser", "Line {0}").format(lineno)
444 'Browser', "Line {0}").format(lineno)) 484 )
445 act.setData([fileName, lineno]) 485 act.setData([fileName, lineno])
446 486
447 def _gotoAttribute(self, act): 487 def _gotoAttribute(self, act):
448 """ 488 """
449 Protected slot to handle the selection of the goto menu. 489 Protected slot to handle the selection of the goto menu.
450 490
451 @param act reference to the action (EricAction) 491 @param act reference to the action (EricAction)
452 """ 492 """
453 fileName, lineno = act.data() 493 fileName, lineno = act.data()
454 self.sourceFile[str, int].emit(fileName, lineno) 494 self.sourceFile[str, int].emit(fileName, lineno)
455 495
456 def handlePreferencesChanged(self): 496 def handlePreferencesChanged(self):
457 """ 497 """
458 Public slot used to handle the preferencesChanged signal. 498 Public slot used to handle the preferencesChanged signal.
459 """ 499 """
460 self.model().preferencesChanged() 500 self.model().preferencesChanged()
461 self._resort() 501 self._resort()
462 502
463 def _openItem(self): 503 def _openItem(self):
464 """ 504 """
465 Protected slot to handle the open popup menu entry. 505 Protected slot to handle the open popup menu entry.
466 """ 506 """
467 itmList = self.getSelectedItems( 507 itmList = self.getSelectedItems(
468 [BrowserFileItem, BrowserClassItem, 508 [
469 BrowserMethodItem, BrowserClassAttributeItem, 509 BrowserFileItem,
470 BrowserImportItem]) 510 BrowserClassItem,
471 511 BrowserMethodItem,
512 BrowserClassAttributeItem,
513 BrowserImportItem,
514 ]
515 )
516
472 if not self._activating: 517 if not self._activating:
473 self._activating = True 518 self._activating = True
474 for itm in itmList: 519 for itm in itmList:
475 if isinstance(itm, BrowserFileItem): 520 if isinstance(itm, BrowserFileItem):
476 if ( 521 if (
477 itm.isPython3File() or 522 itm.isPython3File()
478 itm.isIdlFile() or 523 or itm.isIdlFile()
479 itm.isProtobufFile() or 524 or itm.isProtobufFile()
480 itm.isResourcesFile() 525 or itm.isResourcesFile()
481 ): 526 ):
482 self.sourceFile[str].emit(itm.fileName()) 527 self.sourceFile[str].emit(itm.fileName())
483 elif itm.isRubyFile(): 528 elif itm.isRubyFile():
484 self.sourceFile[str, int, str].emit( 529 self.sourceFile[str, int, str].emit(itm.fileName(), -1, "Ruby")
485 itm.fileName(), -1, "Ruby")
486 elif itm.isDFile(): 530 elif itm.isDFile():
487 self.sourceFile[str, int, str].emit( 531 self.sourceFile[str, int, str].emit(itm.fileName(), -1, "D")
488 itm.fileName(), -1, "D")
489 elif itm.isDesignerFile(): 532 elif itm.isDesignerFile():
490 self.designerFile.emit(itm.fileName()) 533 self.designerFile.emit(itm.fileName())
491 elif itm.isLinguistFile(): 534 elif itm.isLinguistFile():
492 if itm.fileExt() == '.ts': 535 if itm.fileExt() == ".ts":
493 self.linguistFile.emit(itm.fileName()) 536 self.linguistFile.emit(itm.fileName())
494 else: 537 else:
495 self.trpreview.emit([itm.fileName()]) 538 self.trpreview.emit([itm.fileName()])
496 elif itm.isProjectFile(): 539 elif itm.isProjectFile():
497 self.projectFile.emit(itm.fileName()) 540 self.projectFile.emit(itm.fileName())
508 self.sourceFile[str].emit(itm.fileName()) 551 self.sourceFile[str].emit(itm.fileName())
509 else: 552 else:
510 QDesktopServices.openUrl(QUrl(itm.fileName())) 553 QDesktopServices.openUrl(QUrl(itm.fileName()))
511 elif isinstance(itm, BrowserClassItem): 554 elif isinstance(itm, BrowserClassItem):
512 self.sourceFile[str, int].emit( 555 self.sourceFile[str, int].emit(
513 itm.fileName(), itm.classObject().lineno) 556 itm.fileName(), itm.classObject().lineno
557 )
514 elif isinstance(itm, BrowserMethodItem): 558 elif isinstance(itm, BrowserMethodItem):
515 self.sourceFile[str, int].emit( 559 self.sourceFile[str, int].emit(
516 itm.fileName(), itm.functionObject().lineno) 560 itm.fileName(), itm.functionObject().lineno
561 )
517 elif isinstance(itm, BrowserClassAttributeItem): 562 elif isinstance(itm, BrowserClassAttributeItem):
518 self.sourceFile[str, int].emit( 563 self.sourceFile[str, int].emit(
519 itm.fileName(), itm.attributeObject().lineno) 564 itm.fileName(), itm.attributeObject().lineno
565 )
520 elif isinstance(itm, BrowserImportItem): 566 elif isinstance(itm, BrowserImportItem):
521 self.sourceFile[str, list].emit( 567 self.sourceFile[str, list].emit(itm.fileName(), itm.linenos())
522 itm.fileName(), itm.linenos())
523 self._activating = False 568 self._activating = False
524 569
525 def __showMimeType(self): 570 def __showMimeType(self):
526 """ 571 """
527 Private slot to show the mime type of the selected entry. 572 Private slot to show the mime type of the selected entry.
528 """ 573 """
529 itmList = self.getSelectedItems( 574 itmList = self.getSelectedItems(
530 [BrowserFileItem, BrowserClassItem, 575 [
531 BrowserMethodItem, BrowserClassAttributeItem, 576 BrowserFileItem,
532 BrowserImportItem]) 577 BrowserClassItem,
578 BrowserMethodItem,
579 BrowserClassAttributeItem,
580 BrowserImportItem,
581 ]
582 )
533 if itmList: 583 if itmList:
534 mimetype = Utilities.MimeTypes.mimeType(itmList[0].fileName()) 584 mimetype = Utilities.MimeTypes.mimeType(itmList[0].fileName())
535 if mimetype is None: 585 if mimetype is None:
536 EricMessageBox.warning( 586 EricMessageBox.warning(
537 self, 587 self,
538 QCoreApplication.translate('Browser', "Show Mime-Type"), 588 QCoreApplication.translate("Browser", "Show Mime-Type"),
539 QCoreApplication.translate( 589 QCoreApplication.translate(
540 'Browser', 590 "Browser",
541 """The mime type of the file could not be""" 591 """The mime type of the file could not be""" """ determined.""",
542 """ determined.""")) 592 ),
593 )
543 elif mimetype.split("/")[0] == "text": 594 elif mimetype.split("/")[0] == "text":
544 EricMessageBox.information( 595 EricMessageBox.information(
545 self, 596 self,
546 QCoreApplication.translate('Browser', "Show Mime-Type"), 597 QCoreApplication.translate("Browser", "Show Mime-Type"),
547 QCoreApplication.translate( 598 QCoreApplication.translate(
548 'Browser', 599 "Browser", """The file has the mime type <b>{0}</b>."""
549 """The file has the mime type <b>{0}</b>.""") 600 ).format(mimetype),
550 .format(mimetype)) 601 )
551 else: 602 else:
552 textMimeTypesList = Preferences.getUI("TextMimeTypes") 603 textMimeTypesList = Preferences.getUI("TextMimeTypes")
553 if mimetype in textMimeTypesList: 604 if mimetype in textMimeTypesList:
554 EricMessageBox.information( 605 EricMessageBox.information(
555 self, 606 self,
607 QCoreApplication.translate("Browser", "Show Mime-Type"),
556 QCoreApplication.translate( 608 QCoreApplication.translate(
557 'Browser', "Show Mime-Type"), 609 "Browser", """The file has the mime type <b>{0}</b>."""
558 QCoreApplication.translate( 610 ).format(mimetype),
559 'Browser', 611 )
560 """The file has the mime type <b>{0}</b>.""")
561 .format(mimetype))
562 else: 612 else:
563 ok = EricMessageBox.yesNo( 613 ok = EricMessageBox.yesNo(
564 self, 614 self,
615 QCoreApplication.translate("Browser", "Show Mime-Type"),
565 QCoreApplication.translate( 616 QCoreApplication.translate(
566 'Browser', "Show Mime-Type"), 617 "Browser",
567 QCoreApplication.translate(
568 'Browser',
569 """The file has the mime type <b>{0}</b>.""" 618 """The file has the mime type <b>{0}</b>."""
570 """<br/> Shall it be added to the list of""" 619 """<br/> Shall it be added to the list of"""
571 """ text mime types?""").format(mimetype)) 620 """ text mime types?""",
621 ).format(mimetype),
622 )
572 if ok: 623 if ok:
573 textMimeTypesList.append(mimetype) 624 textMimeTypesList.append(mimetype)
574 Preferences.setUI("TextMimeTypes", textMimeTypesList) 625 Preferences.setUI("TextMimeTypes", textMimeTypesList)
575 626
576 def __refreshSource(self): 627 def __refreshSource(self):
577 """ 628 """
578 Private slot to refresh the structure of a source file. 629 Private slot to refresh the structure of a source file.
579 """ 630 """
580 itmList = self.getSelectedItems([BrowserFileItem]) 631 itmList = self.getSelectedItems([BrowserFileItem])
581 if itmList: 632 if itmList:
582 self.__model.repopulateFileItem(itmList[0]) 633 self.__model.repopulateFileItem(itmList[0])
583 634
584 def _editPixmap(self): 635 def _editPixmap(self):
585 """ 636 """
586 Protected slot to handle the open in icon editor popup menu entry. 637 Protected slot to handle the open in icon editor popup menu entry.
587 """ 638 """
588 itmList = self.getSelectedItems([BrowserFileItem]) 639 itmList = self.getSelectedItems([BrowserFileItem])
589 640
590 for itm in itmList: 641 for itm in itmList:
591 if isinstance(itm, BrowserFileItem) and itm.isPixmapFile(): 642 if isinstance(itm, BrowserFileItem) and itm.isPixmapFile():
592 self.pixmapEditFile.emit(itm.fileName()) 643 self.pixmapEditFile.emit(itm.fileName())
593 644
594 def _openHexEditor(self): 645 def _openHexEditor(self):
595 """ 646 """
596 Protected slot to handle the open in hex editor popup menu entry. 647 Protected slot to handle the open in hex editor popup menu entry.
597 """ 648 """
598 itmList = self.getSelectedItems([BrowserFileItem]) 649 itmList = self.getSelectedItems([BrowserFileItem])
599 650
600 for itm in itmList: 651 for itm in itmList:
601 if isinstance(itm, BrowserFileItem): 652 if isinstance(itm, BrowserFileItem):
602 self.binaryFile.emit(itm.fileName()) 653 self.binaryFile.emit(itm.fileName())
603 654
604 def _openFileInEditor(self): 655 def _openFileInEditor(self):
605 """ 656 """
606 Protected slot to handle the Open in Editor menu action. 657 Protected slot to handle the Open in Editor menu action.
607 """ 658 """
608 itmList = self.getSelectedItems([BrowserFileItem]) 659 itmList = self.getSelectedItems([BrowserFileItem])
609 660
610 for itm in itmList: 661 for itm in itmList:
611 if Utilities.MimeTypes.isTextFile(itm.fileName()): 662 if Utilities.MimeTypes.isTextFile(itm.fileName()):
612 self.sourceFile.emit(itm.fileName()) 663 self.sourceFile.emit(itm.fileName())
613 664
614 def _copyToClipboard(self): 665 def _copyToClipboard(self):
615 """ 666 """
616 Protected method to copy the text shown for an entry to the clipboard. 667 Protected method to copy the text shown for an entry to the clipboard.
617 """ 668 """
618 itm = self.model().item(self.currentIndex()) 669 itm = self.model().item(self.currentIndex())
621 except AttributeError: 672 except AttributeError:
622 try: 673 try:
623 fn = itm.dirName() 674 fn = itm.dirName()
624 except AttributeError: 675 except AttributeError:
625 fn = "" 676 fn = ""
626 677
627 if fn: 678 if fn:
628 cb = QApplication.clipboard() 679 cb = QApplication.clipboard()
629 cb.setText(fn) 680 cb.setText(fn)
630 681
631 @pyqtSlot(bool) 682 @pyqtSlot(bool)
632 def _showHidden(self, checked): 683 def _showHidden(self, checked):
633 """ 684 """
634 Protected slot to show or hide hidden files. 685 Protected slot to show or hide hidden files.
635 686
636 @param checked flag indicating the state of the action 687 @param checked flag indicating the state of the action
637 @type bool 688 @type bool
638 """ 689 """
639 self.__sortModel.setShowHiddenFiles(checked) 690 self.__sortModel.setShowHiddenFiles(checked)
640 # remember the current state 691 # remember the current state
641 Preferences.setUI("BrowsersListHiddenFiles", checked) 692 Preferences.setUI("BrowsersListHiddenFiles", checked)
642 693
643 def handleTesting(self): 694 def handleTesting(self):
644 """ 695 """
645 Public slot to handle the testing popup menu entry. 696 Public slot to handle the testing popup menu entry.
646 """ 697 """
647 try: 698 try:
651 except AttributeError: 702 except AttributeError:
652 pyfn = None 703 pyfn = None
653 704
654 if pyfn is not None: 705 if pyfn is not None:
655 self.testFile.emit(pyfn) 706 self.testFile.emit(pyfn)
656 707
657 def __newToplevelDir(self): 708 def __newToplevelDir(self):
658 """ 709 """
659 Private slot to handle the New toplevel directory popup menu entry. 710 Private slot to handle the New toplevel directory popup menu entry.
660 """ 711 """
661 dname = EricFileDialog.getExistingDirectory( 712 dname = EricFileDialog.getExistingDirectory(
662 None, 713 None,
663 QCoreApplication.translate('Browser', "New toplevel directory"), 714 QCoreApplication.translate("Browser", "New toplevel directory"),
664 "", 715 "",
665 EricFileDialog.ShowDirsOnly) 716 EricFileDialog.ShowDirsOnly,
717 )
666 if dname: 718 if dname:
667 dname = os.path.abspath(Utilities.toNativeSeparators(dname)) 719 dname = os.path.abspath(Utilities.toNativeSeparators(dname))
668 self.__model.addTopLevelDir(dname) 720 self.__model.addTopLevelDir(dname)
669 721
670 def __removeToplevel(self): 722 def __removeToplevel(self):
671 """ 723 """
672 Private slot to handle the Remove from toplevel popup menu entry. 724 Private slot to handle the Remove from toplevel popup menu entry.
673 """ 725 """
674 index = self.currentIndex() 726 index = self.currentIndex()
675 sindex = self.model().mapToSource(index) 727 sindex = self.model().mapToSource(index)
676 self.__model.removeToplevelDir(sindex) 728 self.__model.removeToplevelDir(sindex)
677 729
678 def __addAsToplevelDir(self): 730 def __addAsToplevelDir(self):
679 """ 731 """
680 Private slot to handle the Add as toplevel directory popup menu entry. 732 Private slot to handle the Add as toplevel directory popup menu entry.
681 """ 733 """
682 index = self.currentIndex() 734 index = self.currentIndex()
683 dname = self.model().item(index).dirName() 735 dname = self.model().item(index).dirName()
684 self.__model.addTopLevelDir(dname) 736 self.__model.addTopLevelDir(dname)
685 737
686 def __refreshDirectory(self): 738 def __refreshDirectory(self):
687 """ 739 """
688 Private slot to refresh a directory entry. 740 Private slot to refresh a directory entry.
689 """ 741 """
690 index = self.currentIndex() 742 index = self.currentIndex()
691 refreshDir = self.model().item(index).dirName() 743 refreshDir = self.model().item(index).dirName()
692 self.__model.directoryChanged(refreshDir) 744 self.__model.directoryChanged(refreshDir)
693 745
694 def __findInDirectory(self): 746 def __findInDirectory(self):
695 """ 747 """
696 Private slot to handle the Find in directory popup menu entry. 748 Private slot to handle the Find in directory popup menu entry.
697 """ 749 """
698 index = self.currentIndex() 750 index = self.currentIndex()
699 searchDir = self.model().item(index).dirName() 751 searchDir = self.model().item(index).dirName()
700 752
701 ericApp().getObject("UserInterface").showFindFilesWidget( 753 ericApp().getObject("UserInterface").showFindFilesWidget(searchDir=searchDir)
702 searchDir=searchDir) 754
703
704 def __replaceInDirectory(self): 755 def __replaceInDirectory(self):
705 """ 756 """
706 Private slot to handle the Find&Replace in directory popup menu entry. 757 Private slot to handle the Find&Replace in directory popup menu entry.
707 """ 758 """
708 index = self.currentIndex() 759 index = self.currentIndex()
709 searchDir = self.model().item(index).dirName() 760 searchDir = self.model().item(index).dirName()
710 761
711 ericApp().getObject("UserInterface").showReplaceFilesWidget( 762 ericApp().getObject("UserInterface").showReplaceFilesWidget(searchDir=searchDir)
712 searchDir=searchDir) 763
713
714 def handleProgramChange(self, fn): 764 def handleProgramChange(self, fn):
715 """ 765 """
716 Public slot to handle the programChange signal. 766 Public slot to handle the programChange signal.
717 767
718 @param fn file name (string) 768 @param fn file name (string)
719 """ 769 """
720 self.__model.programChange(os.path.dirname(fn)) 770 self.__model.programChange(os.path.dirname(fn))
721 771
722 def handleInterpreterChanged(self, interpreter): 772 def handleInterpreterChanged(self, interpreter):
723 """ 773 """
724 Public slot to handle a change of the debug client's interpreter. 774 Public slot to handle a change of the debug client's interpreter.
725 775
726 @param interpreter interpreter of the debug client (string) 776 @param interpreter interpreter of the debug client (string)
727 """ 777 """
728 self.__model.interpreterChanged(interpreter) 778 self.__model.interpreterChanged(interpreter)
729 779
730 def wantedItem(self, itm, filterList=None): 780 def wantedItem(self, itm, filterList=None):
731 """ 781 """
732 Public method to check type of an item. 782 Public method to check type of an item.
733 783
734 @param itm the item to check (BrowserItem) 784 @param itm the item to check (BrowserItem)
735 @param filterList list of classes to check against 785 @param filterList list of classes to check against
736 @return flag indicating item is a valid type (boolean) 786 @return flag indicating item is a valid type (boolean)
737 """ 787 """
738 if filterList is None: 788 if filterList is None:
739 filterList = self.selectedItemsFilter 789 filterList = self.selectedItemsFilter
740 790
741 return any(isinstance(itm, typ) for typ in filterList) 791 return any(isinstance(itm, typ) for typ in filterList)
742 792
743 def getSelectedItems(self, filterList=None): 793 def getSelectedItems(self, filterList=None):
744 """ 794 """
745 Public method to get the selected items. 795 Public method to get the selected items.
746 796
747 @param filterList list of classes to check against 797 @param filterList list of classes to check against
748 @return list of selected items (list of BrowserItem) 798 @return list of selected items (list of BrowserItem)
749 """ 799 """
750 selectedItems = [] 800 selectedItems = []
751 indexes = self.selectedIndexes() 801 indexes = self.selectedIndexes()
753 if index.column() == 0: 803 if index.column() == 0:
754 itm = self.model().item(index) 804 itm = self.model().item(index)
755 if self.wantedItem(itm, filterList): 805 if self.wantedItem(itm, filterList):
756 selectedItems.append(itm) 806 selectedItems.append(itm)
757 return selectedItems 807 return selectedItems
758 808
759 def getSelectedItemsCount(self, filterList=None): 809 def getSelectedItemsCount(self, filterList=None):
760 """ 810 """
761 Public method to get the count of items selected. 811 Public method to get the count of items selected.
762 812
763 @param filterList list of classes to check against 813 @param filterList list of classes to check against
764 @return count of items selected (integer) 814 @return count of items selected (integer)
765 """ 815 """
766 count = 0 816 count = 0
767 indexes = self.selectedIndexes() 817 indexes = self.selectedIndexes()
769 if index.column() == 0: 819 if index.column() == 0:
770 itm = self.model().item(index) 820 itm = self.model().item(index)
771 if self.wantedItem(itm, filterList): 821 if self.wantedItem(itm, filterList):
772 count += 1 822 count += 1
773 return count 823 return count
774 824
775 def getSelectedItemsCountCategorized(self, filterList=None): 825 def getSelectedItemsCountCategorized(self, filterList=None):
776 """ 826 """
777 Public method to get a categorized count of selected items. 827 Public method to get a categorized count of selected items.
778 828
779 @param filterList list of classes to check against 829 @param filterList list of classes to check against
780 @return a dictionary containing the counts of items belonging 830 @return a dictionary containing the counts of items belonging
781 to the individual filter classes. The keys of the dictionary 831 to the individual filter classes. The keys of the dictionary
782 are the string representation of the classes given in the 832 are the string representation of the classes given in the
783 filter (i.e. str(filterClass)). The dictionary contains 833 filter (i.e. str(filterClass)). The dictionary contains
788 filterList = self.selectedItemsFilter 838 filterList = self.selectedItemsFilter
789 categories = {} 839 categories = {}
790 categories["sum"] = 0 840 categories["sum"] = 0
791 for typ in filterList: 841 for typ in filterList:
792 categories[str(typ)] = 0 842 categories[str(typ)] = 0
793 843
794 indexes = self.selectedIndexes() 844 indexes = self.selectedIndexes()
795 for index in indexes: 845 for index in indexes:
796 if index.column() == 0: 846 if index.column() == 0:
797 itm = self.model().item(index) 847 itm = self.model().item(index)
798 for typ in filterList: 848 for typ in filterList:
799 if isinstance(itm, typ): 849 if isinstance(itm, typ):
800 categories["sum"] += 1 850 categories["sum"] += 1
801 categories[str(typ)] += 1 851 categories[str(typ)] += 1
802 852
803 return categories 853 return categories
804 854
805 def saveToplevelDirs(self): 855 def saveToplevelDirs(self):
806 """ 856 """
807 Public slot to save the toplevel directories. 857 Public slot to save the toplevel directories.
808 """ 858 """
809 self.__model.saveToplevelDirs() 859 self.__model.saveToplevelDirs()
810 860
811 def keyboardSearch(self, search): 861 def keyboardSearch(self, search):
812 """ 862 """
813 Public function to search the tree via the keyboard. 863 Public function to search the tree via the keyboard.
814 864
815 @param search the character entered via the keyboard 865 @param search the character entered via the keyboard
816 @type str 866 @type str
817 """ 867 """
818 if self.model().rowCount() == 0: 868 if self.model().rowCount() == 0:
819 return 869 return
820 870
821 startIndex = ( 871 startIndex = (
822 self.currentIndex() 872 self.currentIndex()
823 if self.currentIndex().isValid() else 873 if self.currentIndex().isValid()
824 self.model().index(0, 0) 874 else self.model().index(0, 0)
825 ) 875 )
826 876
827 keyboardSearchTimeWasValid = self._keyboardSearchTimer.isValid() 877 keyboardSearchTimeWasValid = self._keyboardSearchTimer.isValid()
828 keyboardSearchTimeElapsed = self._keyboardSearchTimer.restart() 878 keyboardSearchTimeElapsed = self._keyboardSearchTimer.restart()
829 if ( 879 if (
830 not search or 880 not search
831 not keyboardSearchTimeWasValid or 881 or not keyboardSearchTimeWasValid
832 keyboardSearchTimeElapsed > 882 or keyboardSearchTimeElapsed > QApplication.keyboardInputInterval()
833 QApplication.keyboardInputInterval()
834 ): 883 ):
835 self._keyboardSearchString = search.lower() 884 self._keyboardSearchString = search.lower()
836 else: 885 else:
837 self._keyboardSearchString += search.lower() 886 self._keyboardSearchString += search.lower()
838 887
839 index = startIndex 888 index = startIndex
840 found = False 889 found = False
841 while True: 890 while True:
842 name = self.model().data(index) 891 name = self.model().data(index)
843 if ( 892 if name.lower().startswith(
844 name.lower().startswith(self._keyboardSearchString) and 893 self._keyboardSearchString
845 self._keyboardSearchType(self.model().item(index)) 894 ) and self._keyboardSearchType(self.model().item(index)):
846 ):
847 found = True 895 found = True
848 break 896 break
849 897
850 index = self.indexBelow(index) 898 index = self.indexBelow(index)
851 if not index.isValid(): 899 if not index.isValid():
852 index = self.model().index(0, 0) 900 index = self.model().index(0, 0)
853 if index == startIndex: 901 if index == startIndex:
854 break 902 break
855 903
856 if found: 904 if found:
857 self.setCurrentIndex(index) 905 self.setCurrentIndex(index)
858 906
859 def _keyboardSearchType(self, item): 907 def _keyboardSearchType(self, item):
860 """ 908 """
861 Protected method to check, if the item is of the correct type. 909 Protected method to check, if the item is of the correct type.
862 910
863 @param item reference to the item 911 @param item reference to the item
864 @type BrowserItem 912 @type BrowserItem
865 @return flag indicating a correct type 913 @return flag indicating a correct type
866 @rtype bool 914 @rtype bool
867 """ 915 """
868 return isinstance( 916 return isinstance(
869 item, (BrowserDirectoryItem, BrowserFileItem, BrowserSysPathItem)) 917 item, (BrowserDirectoryItem, BrowserFileItem, BrowserSysPathItem)
870 918 )
919
871 @pyqtSlot() 920 @pyqtSlot()
872 def _newDirectory(self): 921 def _newDirectory(self):
873 """ 922 """
874 Protected slot to create a new directory. 923 Protected slot to create a new directory.
875 """ 924 """
878 dname = self.model().item(index).dirName() 927 dname = self.model().item(index).dirName()
879 newName, ok = QInputDialog.getText( 928 newName, ok = QInputDialog.getText(
880 self, 929 self,
881 self.tr("New Directory"), 930 self.tr("New Directory"),
882 self.tr("Name for new directory:"), 931 self.tr("Name for new directory:"),
883 QLineEdit.EchoMode.Normal) 932 QLineEdit.EchoMode.Normal,
933 )
884 if ok and bool(newName): 934 if ok and bool(newName):
885 dirpath = os.path.join(dname, newName) 935 dirpath = os.path.join(dname, newName)
886 if os.path.exists(dirpath): 936 if os.path.exists(dirpath):
887 EricMessageBox.warning( 937 EricMessageBox.warning(
888 self, 938 self,
889 self.tr("New Directory"), 939 self.tr("New Directory"),
890 self.tr("A file or directory named <b>{0}</b> exists" 940 self.tr(
891 " already. Aborting...") 941 "A file or directory named <b>{0}</b> exists"
892 .format(newName)) 942 " already. Aborting..."
943 ).format(newName),
944 )
893 else: 945 else:
894 try: 946 try:
895 os.mkdir(dirpath, mode=0o751) 947 os.mkdir(dirpath, mode=0o751)
896 except OSError as err: 948 except OSError as err:
897 EricMessageBox.critical( 949 EricMessageBox.critical(
898 self, 950 self,
899 self.tr("New Directory"), 951 self.tr("New Directory"),
900 self.tr("<p>The directory <b>{0}</b> could not be" 952 self.tr(
901 " created.</p><p>Reason: {1}</p>") 953 "<p>The directory <b>{0}</b> could not be"
902 .format(newName, str(err))) 954 " created.</p><p>Reason: {1}</p>"
903 955 ).format(newName, str(err)),
956 )
957
904 @pyqtSlot() 958 @pyqtSlot()
905 def _newFile(self): 959 def _newFile(self):
906 """ 960 """
907 Protected slot to create a new file. 961 Protected slot to create a new file.
908 """ 962 """
911 dname = self.model().item(index).dirName() 965 dname = self.model().item(index).dirName()
912 fname, ok = QInputDialog.getText( 966 fname, ok = QInputDialog.getText(
913 self, 967 self,
914 self.tr("New File"), 968 self.tr("New File"),
915 self.tr("Name for new file:"), 969 self.tr("Name for new file:"),
916 QLineEdit.EchoMode.Normal) 970 QLineEdit.EchoMode.Normal,
971 )
917 if ok and bool(fname): 972 if ok and bool(fname):
918 filepath = os.path.join(dname, fname) 973 filepath = os.path.join(dname, fname)
919 if os.path.exists(filepath): 974 if os.path.exists(filepath):
920 EricMessageBox.warning( 975 EricMessageBox.warning(
921 self, 976 self,
922 self.tr("New File"), 977 self.tr("New File"),
923 self.tr("A file or directory named <b>{0}</b> exists" 978 self.tr(
924 " already. Aborting...") 979 "A file or directory named <b>{0}</b> exists"
925 .format(fname)) 980 " already. Aborting..."
981 ).format(fname),
982 )
926 else: 983 else:
927 try: 984 try:
928 with open(filepath, "w"): 985 with open(filepath, "w"):
929 pass 986 pass
930 except OSError as err: 987 except OSError as err:
931 EricMessageBox.critical( 988 EricMessageBox.critical(
932 self, 989 self,
933 self.tr("New File"), 990 self.tr("New File"),
934 self.tr("<p>The file <b>{0}</b> could not be" 991 self.tr(
935 " created.</p><p>Reason: {1}</p>") 992 "<p>The file <b>{0}</b> could not be"
936 .format(fname, str(err))) 993 " created.</p><p>Reason: {1}</p>"
937 994 ).format(fname, str(err)),
995 )
996
938 @pyqtSlot() 997 @pyqtSlot()
939 def _deleteFileOrDirectory(self): 998 def _deleteFileOrDirectory(self):
940 """ 999 """
941 Protected slot to delete a directory or file. 1000 Protected slot to delete a directory or file.
942 """ 1001 """
945 itm = self.model().item(index) 1004 itm = self.model().item(index)
946 if itm.type() == BrowserItemDirectory: 1005 if itm.type() == BrowserItemDirectory:
947 self.__deleteDirectory(itm.dirName()) 1006 self.__deleteDirectory(itm.dirName())
948 else: 1007 else:
949 self.__deleteFile(itm.fileName()) 1008 self.__deleteFile(itm.fileName())
950 1009
951 def __deleteFile(self, fn): 1010 def __deleteFile(self, fn):
952 """ 1011 """
953 Private method to delete a file. 1012 Private method to delete a file.
954 1013
955 @param fn filename to be deleted 1014 @param fn filename to be deleted
956 @type str 1015 @type str
957 """ 1016 """
958 try: 1017 try:
959 from send2trash import send2trash as s2t 1018 from send2trash import send2trash as s2t
960 trashMsg = self.tr("Do you really want to move this file to the" 1019
961 " trash?") 1020 trashMsg = self.tr("Do you really want to move this file to the" " trash?")
962 except ImportError: 1021 except ImportError:
963 s2t = os.remove 1022 s2t = os.remove
964 trashMsg = self.tr("Do you really want to delete this file?") 1023 trashMsg = self.tr("Do you really want to delete this file?")
965 1024
966 from UI.DeleteFilesConfirmationDialog import ( 1025 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
967 DeleteFilesConfirmationDialog 1026
968 )
969 dlg = DeleteFilesConfirmationDialog( 1027 dlg = DeleteFilesConfirmationDialog(
970 self.parent(), 1028 self.parent(), self.tr("Delete File"), trashMsg, [fn]
971 self.tr("Delete File"), 1029 )
972 trashMsg,
973 [fn])
974 if dlg.exec() == QDialog.DialogCode.Accepted: 1030 if dlg.exec() == QDialog.DialogCode.Accepted:
975 try: 1031 try:
976 s2t(fn) 1032 s2t(fn)
977 except OSError as err: 1033 except OSError as err:
978 EricMessageBox.critical( 1034 EricMessageBox.critical(
979 self.ui, 1035 self.ui,
980 self.tr("Delete File"), 1036 self.tr("Delete File"),
981 self.tr( 1037 self.tr(
982 "<p>The selected file <b>{0}</b> could not be" 1038 "<p>The selected file <b>{0}</b> could not be"
983 " deleted.</p><p>Reason: {1}</p>") 1039 " deleted.</p><p>Reason: {1}</p>"
984 .format(fn, str(err)) 1040 ).format(fn, str(err)),
985 ) 1041 )
986 1042
987 def __deleteDirectory(self, dn): 1043 def __deleteDirectory(self, dn):
988 """ 1044 """
989 Private method to delete a directory. 1045 Private method to delete a directory.
990 1046
991 @param dn directory name to be removed from the project 1047 @param dn directory name to be removed from the project
992 @type str 1048 @type str
993 """ 1049 """
994 try: 1050 try:
995 from send2trash import send2trash 1051 from send2trash import send2trash
1052
996 s2tAvailable = True 1053 s2tAvailable = True
997 trashMsg = self.tr("Do you really want to move this directory to" 1054 trashMsg = self.tr(
998 " the trash?") 1055 "Do you really want to move this directory to" " the trash?"
1056 )
999 except ImportError: 1057 except ImportError:
1000 s2tAvailable = False 1058 s2tAvailable = False
1001 trashMsg = self.tr("Do you really want to delete this directory?") 1059 trashMsg = self.tr("Do you really want to delete this directory?")
1002 1060
1003 from UI.DeleteFilesConfirmationDialog import ( 1061 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
1004 DeleteFilesConfirmationDialog 1062
1005 )
1006 dlg = DeleteFilesConfirmationDialog( 1063 dlg = DeleteFilesConfirmationDialog(
1007 self.parent(), 1064 self.parent(), self.tr("Delete Directory"), trashMsg, [dn]
1008 self.tr("Delete Directory"), 1065 )
1009 trashMsg,
1010 [dn])
1011 if dlg.exec() == QDialog.DialogCode.Accepted: 1066 if dlg.exec() == QDialog.DialogCode.Accepted:
1012 try: 1067 try:
1013 if s2tAvailable: 1068 if s2tAvailable:
1014 send2trash(dn) 1069 send2trash(dn)
1015 else: 1070 else:
1018 EricMessageBox.critical( 1073 EricMessageBox.critical(
1019 self.ui, 1074 self.ui,
1020 self.tr("Delete Directory"), 1075 self.tr("Delete Directory"),
1021 self.tr( 1076 self.tr(
1022 "<p>The selected directory <b>{0}</b> could not be" 1077 "<p>The selected directory <b>{0}</b> could not be"
1023 " deleted.</p><p>Reason: {1}</p>") 1078 " deleted.</p><p>Reason: {1}</p>"
1024 .format(dn, str(err)) 1079 ).format(dn, str(err)),
1025 ) 1080 )
1026 1081
1027 @pyqtSlot() 1082 @pyqtSlot()
1028 def __deleteMultiple(self): 1083 def __deleteMultiple(self):
1029 """ 1084 """
1030 Private slot to delete multiple directories and files. 1085 Private slot to delete multiple directories and files.
1031 1086
1032 Note: The context menu for multi selection is only shown for file 1087 Note: The context menu for multi selection is only shown for file
1033 items. 1088 items.
1034 """ 1089 """
1035 fileNames = [] 1090 fileNames = []
1036 for itm in self.getSelectedItems(): 1091 for itm in self.getSelectedItems():
1037 fileNames.append(itm.fileName()) 1092 fileNames.append(itm.fileName())
1038 1093
1039 try: 1094 try:
1040 from send2trash import send2trash as s2t 1095 from send2trash import send2trash as s2t
1041 trashMsg = self.tr("Do you really want to move these files to the" 1096
1042 " trash?") 1097 trashMsg = self.tr(
1098 "Do you really want to move these files to the" " trash?"
1099 )
1043 except ImportError: 1100 except ImportError:
1044 s2t = os.remove 1101 s2t = os.remove
1045 trashMsg = self.tr("Do you really want to delete these files?") 1102 trashMsg = self.tr("Do you really want to delete these files?")
1046 1103
1047 from UI.DeleteFilesConfirmationDialog import ( 1104 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
1048 DeleteFilesConfirmationDialog 1105
1049 )
1050 dlg = DeleteFilesConfirmationDialog( 1106 dlg = DeleteFilesConfirmationDialog(
1051 self.parent(), 1107 self.parent(), self.tr("Delete Files"), trashMsg, sorted(fileNames)
1052 self.tr("Delete Files"),
1053 trashMsg,
1054 sorted(fileNames)
1055 ) 1108 )
1056 if dlg.exec() == QDialog.DialogCode.Accepted: 1109 if dlg.exec() == QDialog.DialogCode.Accepted:
1057 for fn in fileNames: 1110 for fn in fileNames:
1058 try: 1111 try:
1059 s2t(fn) 1112 s2t(fn)
1061 EricMessageBox.critical( 1114 EricMessageBox.critical(
1062 self.ui, 1115 self.ui,
1063 self.tr("Delete File"), 1116 self.tr("Delete File"),
1064 self.tr( 1117 self.tr(
1065 "<p>The selected file <b>{0}</b> could not be" 1118 "<p>The selected file <b>{0}</b> could not be"
1066 " deleted.</p><p>Reason: {1}</p>") 1119 " deleted.</p><p>Reason: {1}</p>"
1067 .format(fn, str(err)) 1120 ).format(fn, str(err)),
1068 ) 1121 )

eric ide

mercurial