src/eric7/Project/ProjectBaseBrowser.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
9 9
10 import os 10 import os
11 import contextlib 11 import contextlib
12 12
13 from PyQt6.QtCore import ( 13 from PyQt6.QtCore import (
14 QModelIndex, pyqtSignal, Qt, QCoreApplication, QItemSelectionModel, 14 QModelIndex,
15 QItemSelection, QElapsedTimer 15 pyqtSignal,
16 Qt,
17 QCoreApplication,
18 QItemSelectionModel,
19 QItemSelection,
20 QElapsedTimer,
16 ) 21 )
17 from PyQt6.QtWidgets import ( 22 from PyQt6.QtWidgets import QTreeView, QApplication, QMenu, QDialog, QAbstractItemView
18 QTreeView, QApplication, QMenu, QDialog, QAbstractItemView
19 )
20 23
21 from EricWidgets.EricApplication import ericApp 24 from EricWidgets.EricApplication import ericApp
22 from EricWidgets import EricMessageBox 25 from EricWidgets import EricMessageBox
23 from EricGui.EricOverrideCursor import EricOverrideCursor 26 from EricGui.EricOverrideCursor import EricOverrideCursor
24 27
25 from UI.Browser import Browser 28 from UI.Browser import Browser
26 from UI.BrowserModel import BrowserDirectoryItem, BrowserFileItem 29 from UI.BrowserModel import BrowserDirectoryItem, BrowserFileItem
27 30
28 from .ProjectBrowserModel import ( 31 from .ProjectBrowserModel import (
29 ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem, 32 ProjectBrowserSimpleDirectoryItem,
30 ProjectBrowserFileItem 33 ProjectBrowserDirectoryItem,
34 ProjectBrowserFileItem,
31 ) 35 )
32 from .ProjectBrowserSortFilterProxyModel import ( 36 from .ProjectBrowserSortFilterProxyModel import ProjectBrowserSortFilterProxyModel
33 ProjectBrowserSortFilterProxyModel
34 )
35 37
36 38
37 class ProjectBaseBrowser(Browser): 39 class ProjectBaseBrowser(Browser):
38 """ 40 """
39 Baseclass implementing common functionality for the various project 41 Baseclass implementing common functionality for the various project
40 browsers. 42 browsers.
41 43
42 @signal closeSourceWindow(str) emitted to close a source file 44 @signal closeSourceWindow(str) emitted to close a source file
43 """ 45 """
46
44 closeSourceWindow = pyqtSignal(str) 47 closeSourceWindow = pyqtSignal(str)
45 48
46 def __init__(self, project, type_, parent=None): 49 def __init__(self, project, type_, parent=None):
47 """ 50 """
48 Constructor 51 Constructor
49 52
50 @param project reference to the project object 53 @param project reference to the project object
51 @param type_ project browser type (string) 54 @param type_ project browser type (string)
52 @param parent parent widget of this browser 55 @param parent parent widget of this browser
53 """ 56 """
54 QTreeView.__init__(self, parent) 57 QTreeView.__init__(self, parent)
55 58
56 self.project = project 59 self.project = project
57 60
58 self._model = project.getModel() 61 self._model = project.getModel()
59 self._sortModel = ProjectBrowserSortFilterProxyModel(type_) 62 self._sortModel = ProjectBrowserSortFilterProxyModel(type_)
60 self._sortModel.setSourceModel(self._model) 63 self._sortModel.setSourceModel(self._model)
61 self.setModel(self._sortModel) 64 self.setModel(self._sortModel)
62 65
63 self.selectedItemsFilter = [ProjectBrowserFileItem] 66 self.selectedItemsFilter = [ProjectBrowserFileItem]
64 67
65 # contains codes for special menu entries 68 # contains codes for special menu entries
66 # 1 = specials for Others browser 69 # 1 = specials for Others browser
67 self.specialMenuEntries = [] 70 self.specialMenuEntries = []
68 self.isTranslationsBrowser = False 71 self.isTranslationsBrowser = False
69 self.expandedNames = [] 72 self.expandedNames = []
70 73
71 self.SelectFlags = ( 74 self.SelectFlags = (
72 QItemSelectionModel.SelectionFlag.Select | 75 QItemSelectionModel.SelectionFlag.Select
73 QItemSelectionModel.SelectionFlag.Rows 76 | QItemSelectionModel.SelectionFlag.Rows
74 ) 77 )
75 self.DeselectFlags = ( 78 self.DeselectFlags = (
76 QItemSelectionModel.SelectionFlag.Deselect | 79 QItemSelectionModel.SelectionFlag.Deselect
77 QItemSelectionModel.SelectionFlag.Rows 80 | QItemSelectionModel.SelectionFlag.Rows
78 ) 81 )
79 82
80 self._activating = False 83 self._activating = False
81 84
82 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) 85 self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
83 self.customContextMenuRequested.connect(self._contextMenuRequested) 86 self.customContextMenuRequested.connect(self._contextMenuRequested)
84 self.activated.connect(self._openItem) 87 self.activated.connect(self._openItem)
85 self._model.rowsInserted.connect(self.__modelRowsInserted) 88 self._model.rowsInserted.connect(self.__modelRowsInserted)
86 self._connectExpandedCollapsed() 89 self._connectExpandedCollapsed()
87 90
88 self._createPopupMenus() 91 self._createPopupMenus()
89 92
90 self.currentItemName = None 93 self.currentItemName = None
91 94
92 self._init() # perform common initialization tasks 95 self._init() # perform common initialization tasks
93 96
94 self._keyboardSearchString = "" 97 self._keyboardSearchString = ""
95 self._keyboardSearchTimer = QElapsedTimer() 98 self._keyboardSearchTimer = QElapsedTimer()
96 self._keyboardSearchTimer.invalidate() 99 self._keyboardSearchTimer.invalidate()
97 100
98 self._initHookMethods() # perform initialization of the hooks 101 self._initHookMethods() # perform initialization of the hooks
99 self.hooksMenuEntries = {} 102 self.hooksMenuEntries = {}
100 103
101 def _connectExpandedCollapsed(self): 104 def _connectExpandedCollapsed(self):
102 """ 105 """
103 Protected method to connect the expanded and collapsed signals. 106 Protected method to connect the expanded and collapsed signals.
104 """ 107 """
105 self.expanded.connect(self._resizeColumns) 108 self.expanded.connect(self._resizeColumns)
106 self.collapsed.connect(self._resizeColumns) 109 self.collapsed.connect(self._resizeColumns)
107 110
108 def _disconnectExpandedCollapsed(self): 111 def _disconnectExpandedCollapsed(self):
109 """ 112 """
110 Protected method to disconnect the expanded and collapsed signals. 113 Protected method to disconnect the expanded and collapsed signals.
111 """ 114 """
112 self.expanded.disconnect(self._resizeColumns) 115 self.expanded.disconnect(self._resizeColumns)
113 self.collapsed.disconnect(self._resizeColumns) 116 self.collapsed.disconnect(self._resizeColumns)
114 117
115 def _createPopupMenus(self): 118 def _createPopupMenus(self):
116 """ 119 """
117 Protected overloaded method to generate the popup menus. 120 Protected overloaded method to generate the popup menus.
118 """ 121 """
119 # create the popup menu for source files 122 # create the popup menu for source files
120 self.sourceMenu = QMenu(self) 123 self.sourceMenu = QMenu(self)
121 self.sourceMenu.addAction( 124 self.sourceMenu.addAction(
122 QCoreApplication.translate('ProjectBaseBrowser', 'Open'), 125 QCoreApplication.translate("ProjectBaseBrowser", "Open"), self._openItem
123 self._openItem) 126 )
124 127
125 # create the popup menu for general use 128 # create the popup menu for general use
126 self.menu = QMenu(self) 129 self.menu = QMenu(self)
127 self.menu.addAction( 130 self.menu.addAction(
128 QCoreApplication.translate('ProjectBaseBrowser', 'Open'), 131 QCoreApplication.translate("ProjectBaseBrowser", "Open"), self._openItem
129 self._openItem) 132 )
130 133
131 # create the menu for multiple selected files 134 # create the menu for multiple selected files
132 self.multiMenu = QMenu(self) 135 self.multiMenu = QMenu(self)
133 self.multiMenu.addAction( 136 self.multiMenu.addAction(
134 QCoreApplication.translate('ProjectBaseBrowser', 'Open'), 137 QCoreApplication.translate("ProjectBaseBrowser", "Open"), self._openItem
135 self._openItem) 138 )
136 139
137 # create the background menu 140 # create the background menu
138 self.backMenu = None 141 self.backMenu = None
139 142
140 # create the directories menu 143 # create the directories menu
141 self.dirMenu = None 144 self.dirMenu = None
142 145
143 # create the directory for multiple selected directories 146 # create the directory for multiple selected directories
144 self.dirMultiMenu = None 147 self.dirMultiMenu = None
145 148
146 self.menuActions = [] 149 self.menuActions = []
147 self.multiMenuActions = [] 150 self.multiMenuActions = []
148 self.dirMenuActions = [] 151 self.dirMenuActions = []
149 self.dirMultiMenuActions = [] 152 self.dirMultiMenuActions = []
150 153
151 self.mainMenu = None 154 self.mainMenu = None
152 155
153 def _contextMenuRequested(self, coord): 156 def _contextMenuRequested(self, coord):
154 """ 157 """
155 Protected slot to show the context menu. 158 Protected slot to show the context menu.
156 159
157 @param coord the position of the mouse pointer (QPoint) 160 @param coord the position of the mouse pointer (QPoint)
158 """ 161 """
159 if not self.project.isOpen(): 162 if not self.project.isOpen():
160 return 163 return
161 164
162 cnt = self.getSelectedItemsCount() 165 cnt = self.getSelectedItemsCount()
163 if cnt > 1: 166 if cnt > 1:
164 self.multiMenu.popup(self.mapToGlobal(coord)) 167 self.multiMenu.popup(self.mapToGlobal(coord))
165 else: 168 else:
166 index = self.indexAt(coord) 169 index = self.indexAt(coord)
167 170
168 if index.isValid(): 171 if index.isValid():
169 self.menu.popup(self.mapToGlobal(coord)) 172 self.menu.popup(self.mapToGlobal(coord))
170 else: 173 else:
171 self.backMenu and self.backMenu.popup(self.mapToGlobal(coord)) 174 self.backMenu and self.backMenu.popup(self.mapToGlobal(coord))
172 175
173 def _selectSingleItem(self, index): 176 def _selectSingleItem(self, index):
174 """ 177 """
175 Protected method to select a single item. 178 Protected method to select a single item.
176 179
177 @param index index of item to be selected (QModelIndex) 180 @param index index of item to be selected (QModelIndex)
178 """ 181 """
179 if index.isValid(): 182 if index.isValid():
180 self.setCurrentIndex(index) 183 self.setCurrentIndex(index)
181 self.selectionModel().select( 184 self.selectionModel().select(
182 index, 185 index,
183 QItemSelectionModel.SelectionFlag.ClearAndSelect | 186 QItemSelectionModel.SelectionFlag.ClearAndSelect
184 QItemSelectionModel.SelectionFlag.Rows 187 | QItemSelectionModel.SelectionFlag.Rows,
185 ) 188 )
186 189
187 def _setItemSelected(self, index, selected): 190 def _setItemSelected(self, index, selected):
188 """ 191 """
189 Protected method to set the selection status of an item. 192 Protected method to set the selection status of an item.
190 193
191 @param index index of item to set (QModelIndex) 194 @param index index of item to set (QModelIndex)
192 @param selected flag giving the new selection status (boolean) 195 @param selected flag giving the new selection status (boolean)
193 """ 196 """
194 if index.isValid(): 197 if index.isValid():
195 self.selectionModel().select( 198 self.selectionModel().select(
196 index, selected and self.SelectFlags or self.DeselectFlags) 199 index, selected and self.SelectFlags or self.DeselectFlags
197 200 )
201
198 def _setItemRangeSelected(self, startIndex, endIndex, selected): 202 def _setItemRangeSelected(self, startIndex, endIndex, selected):
199 """ 203 """
200 Protected method to set the selection status of a range of items. 204 Protected method to set the selection status of a range of items.
201 205
202 @param startIndex start index of range of items to set (QModelIndex) 206 @param startIndex start index of range of items to set (QModelIndex)
203 @param endIndex end index of range of items to set (QModelIndex) 207 @param endIndex end index of range of items to set (QModelIndex)
204 @param selected flag giving the new selection status (boolean) 208 @param selected flag giving the new selection status (boolean)
205 """ 209 """
206 selection = QItemSelection(startIndex, endIndex) 210 selection = QItemSelection(startIndex, endIndex)
207 self.selectionModel().select( 211 self.selectionModel().select(
208 selection, selected and self.SelectFlags or self.DeselectFlags) 212 selection, selected and self.SelectFlags or self.DeselectFlags
209 213 )
214
210 def __modelRowsInserted(self, parent, start, end): 215 def __modelRowsInserted(self, parent, start, end):
211 """ 216 """
212 Private slot called after rows have been inserted into the model. 217 Private slot called after rows have been inserted into the model.
213 218
214 @param parent parent index of inserted rows (QModelIndex) 219 @param parent parent index of inserted rows (QModelIndex)
215 @param start start row number (integer) 220 @param start start row number (integer)
216 @param end end row number (integer) 221 @param end end row number (integer)
217 """ 222 """
218 self._resizeColumns(parent) 223 self._resizeColumns(parent)
219 224
220 def _projectClosed(self): 225 def _projectClosed(self):
221 """ 226 """
222 Protected slot to handle the projectClosed signal. 227 Protected slot to handle the projectClosed signal.
223 """ 228 """
224 self.layoutDisplay() 229 self.layoutDisplay()
225 if self.backMenu is not None: 230 if self.backMenu is not None:
226 self.backMenu.setEnabled(False) 231 self.backMenu.setEnabled(False)
227 232
228 self._createPopupMenus() 233 self._createPopupMenus()
229 234
230 def _projectOpened(self): 235 def _projectOpened(self):
231 """ 236 """
232 Protected slot to handle the projectOpened signal. 237 Protected slot to handle the projectOpened signal.
233 """ 238 """
234 self.layoutDisplay() 239 self.layoutDisplay()
235 self.sortByColumn(0, Qt.SortOrder.DescendingOrder) 240 self.sortByColumn(0, Qt.SortOrder.DescendingOrder)
236 self.sortByColumn(0, Qt.SortOrder.AscendingOrder) 241 self.sortByColumn(0, Qt.SortOrder.AscendingOrder)
237 self._initMenusAndVcs() 242 self._initMenusAndVcs()
238 243
239 def _initMenusAndVcs(self): 244 def _initMenusAndVcs(self):
240 """ 245 """
241 Protected slot to initialize the menus and the Vcs interface. 246 Protected slot to initialize the menus and the Vcs interface.
242 """ 247 """
243 self._createPopupMenus() 248 self._createPopupMenus()
244 249
245 if self.backMenu is not None: 250 if self.backMenu is not None:
246 self.backMenu.setEnabled(True) 251 self.backMenu.setEnabled(True)
247 252
248 if self.project.vcs is not None: 253 if self.project.vcs is not None:
249 self.vcsHelper = self.project.vcs.vcsGetProjectBrowserHelper( 254 self.vcsHelper = self.project.vcs.vcsGetProjectBrowserHelper(
250 self, self.project, self.isTranslationsBrowser) 255 self, self.project, self.isTranslationsBrowser
256 )
251 self.vcsHelper.addVCSMenus( 257 self.vcsHelper.addVCSMenus(
252 self.mainMenu, self.multiMenu, self.backMenu, 258 self.mainMenu,
253 self.dirMenu, self.dirMultiMenu) 259 self.multiMenu,
254 260 self.backMenu,
261 self.dirMenu,
262 self.dirMultiMenu,
263 )
264
255 def _newProject(self): 265 def _newProject(self):
256 """ 266 """
257 Protected slot to handle the newProject signal. 267 Protected slot to handle the newProject signal.
258 """ 268 """
259 # default to perform same actions as opening a project 269 # default to perform same actions as opening a project
260 self._projectOpened() 270 self._projectOpened()
261 271
262 def _removeFile(self): 272 def _removeFile(self):
263 """ 273 """
264 Protected method to remove a file or files from the project. 274 Protected method to remove a file or files from the project.
265 """ 275 """
266 itmList = self.getSelectedItems() 276 itmList = self.getSelectedItems()
267 277
268 for itm in itmList[:]: 278 for itm in itmList[:]:
269 fn = itm.fileName() 279 fn = itm.fileName()
270 self.closeSourceWindow.emit(fn) 280 self.closeSourceWindow.emit(fn)
271 self.project.removeFile(fn) 281 self.project.removeFile(fn)
272 282
273 def _removeDir(self): 283 def _removeDir(self):
274 """ 284 """
275 Protected method to remove a (single) directory from the project. 285 Protected method to remove a (single) directory from the project.
276 """ 286 """
277 itmList = self.getSelectedItems( 287 itmList = self.getSelectedItems(
278 [ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem]) 288 [ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem]
289 )
279 for itm in itmList[:]: 290 for itm in itmList[:]:
280 dn = itm.dirName() 291 dn = itm.dirName()
281 self.project.removeDirectory(dn) 292 self.project.removeDirectory(dn)
282 293
283 def _deleteDirectory(self): 294 def _deleteDirectory(self):
284 """ 295 """
285 Protected method to delete the selected directory from the project 296 Protected method to delete the selected directory from the project
286 data area. 297 data area.
287 """ 298 """
288 itmList = self.getSelectedItems() 299 itmList = self.getSelectedItems()
289 300
290 dirs = [] 301 dirs = []
291 fullNames = [] 302 fullNames = []
292 for itm in itmList: 303 for itm in itmList:
293 dn = itm.dirName() 304 dn = itm.dirName()
294 fullNames.append(dn) 305 fullNames.append(dn)
295 dn = self.project.getRelativePath(dn) 306 dn = self.project.getRelativePath(dn)
296 dirs.append(dn) 307 dirs.append(dn)
297 308
298 from UI.DeleteFilesConfirmationDialog import ( 309 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
299 DeleteFilesConfirmationDialog 310
300 )
301 dlg = DeleteFilesConfirmationDialog( 311 dlg = DeleteFilesConfirmationDialog(
302 self.parent(), 312 self.parent(),
303 QCoreApplication.translate( 313 QCoreApplication.translate("ProjectBaseBrowser", "Delete directories"),
304 "ProjectBaseBrowser", "Delete directories"),
305 QCoreApplication.translate( 314 QCoreApplication.translate(
306 "ProjectBaseBrowser", 315 "ProjectBaseBrowser",
307 "Do you really want to delete these directories from" 316 "Do you really want to delete these directories from" " the project?",
308 " the project?"), 317 ),
309 dirs) 318 dirs,
310 319 )
320
311 if dlg.exec() == QDialog.DialogCode.Accepted: 321 if dlg.exec() == QDialog.DialogCode.Accepted:
312 for dn in fullNames: 322 for dn in fullNames:
313 self.project.deleteDirectory(dn) 323 self.project.deleteDirectory(dn)
314 324
315 def _renameFile(self): 325 def _renameFile(self):
316 """ 326 """
317 Protected method to rename a file of the project. 327 Protected method to rename a file of the project.
318 """ 328 """
319 itm = self.model().item(self.currentIndex()) 329 itm = self.model().item(self.currentIndex())
320 fn = itm.fileName() 330 fn = itm.fileName()
321 self.project.renameFile(fn) 331 self.project.renameFile(fn)
322 332
323 def _copyToClipboard(self): 333 def _copyToClipboard(self):
324 """ 334 """
325 Protected method to copy the path of an entry to the clipboard. 335 Protected method to copy the path of an entry to the clipboard.
326 """ 336 """
327 itm = self.model().item(self.currentIndex()) 337 itm = self.model().item(self.currentIndex())
330 except AttributeError: 340 except AttributeError:
331 try: 341 try:
332 fn = itm.dirName() 342 fn = itm.dirName()
333 except AttributeError: 343 except AttributeError:
334 fn = "" 344 fn = ""
335 345
336 cb = QApplication.clipboard() 346 cb = QApplication.clipboard()
337 cb.setText(fn) 347 cb.setText(fn)
338 348
339 def selectFile(self, fn): 349 def selectFile(self, fn):
340 """ 350 """
341 Public method to highlight a node given its filename. 351 Public method to highlight a node given its filename.
342 352
343 @param fn filename of file to be highlighted (string) 353 @param fn filename of file to be highlighted (string)
344 """ 354 """
345 newfn = os.path.abspath(fn) 355 newfn = os.path.abspath(fn)
346 newfn = self.project.getRelativePath(newfn) 356 newfn = self.project.getRelativePath(newfn)
347 sindex = self._model.itemIndexByName(newfn) 357 sindex = self._model.itemIndexByName(newfn)
348 if sindex.isValid(): 358 if sindex.isValid():
349 index = self.model().mapFromSource(sindex) 359 index = self.model().mapFromSource(sindex)
350 if index.isValid(): 360 if index.isValid():
351 self._selectSingleItem(index) 361 self._selectSingleItem(index)
352 self.scrollTo(index, 362 self.scrollTo(index, QAbstractItemView.ScrollHint.PositionAtTop)
353 QAbstractItemView.ScrollHint.PositionAtTop) 363
354
355 def selectFileLine(self, fn, lineno): 364 def selectFileLine(self, fn, lineno):
356 """ 365 """
357 Public method to highlight a node given its filename. 366 Public method to highlight a node given its filename.
358 367
359 @param fn filename of file to be highlighted (string) 368 @param fn filename of file to be highlighted (string)
360 @param lineno one based line number of the item (integer) 369 @param lineno one based line number of the item (integer)
361 """ 370 """
362 newfn = os.path.abspath(fn) 371 newfn = os.path.abspath(fn)
363 newfn = self.project.getRelativePath(newfn) 372 newfn = self.project.getRelativePath(newfn)
365 if sindex.isValid(): 374 if sindex.isValid():
366 index = self.model().mapFromSource(sindex) 375 index = self.model().mapFromSource(sindex)
367 if index.isValid(): 376 if index.isValid():
368 self._selectSingleItem(index) 377 self._selectSingleItem(index)
369 self.scrollTo(index) 378 self.scrollTo(index)
370 379
371 def _expandAllDirs(self): 380 def _expandAllDirs(self):
372 """ 381 """
373 Protected slot to handle the 'Expand all directories' menu action. 382 Protected slot to handle the 'Expand all directories' menu action.
374 """ 383 """
375 self._disconnectExpandedCollapsed() 384 self._disconnectExpandedCollapsed()
376 with EricOverrideCursor(): 385 with EricOverrideCursor():
377 index = self.model().index(0, 0) 386 index = self.model().index(0, 0)
378 while index.isValid(): 387 while index.isValid():
379 itm = self.model().item(index) 388 itm = self.model().item(index)
380 if ( 389 if isinstance(
381 isinstance( 390 itm,
382 itm, 391 (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem),
383 (ProjectBrowserSimpleDirectoryItem, 392 ) and not self.isExpanded(index):
384 ProjectBrowserDirectoryItem)) and
385 not self.isExpanded(index)
386 ):
387 self.expand(index) 393 self.expand(index)
388 index = self.indexBelow(index) 394 index = self.indexBelow(index)
389 self.layoutDisplay() 395 self.layoutDisplay()
390 self._connectExpandedCollapsed() 396 self._connectExpandedCollapsed()
391 397
392 def _collapseAllDirs(self): 398 def _collapseAllDirs(self):
393 """ 399 """
394 Protected slot to handle the 'Collapse all directories' menu action. 400 Protected slot to handle the 'Collapse all directories' menu action.
395 """ 401 """
396 self._disconnectExpandedCollapsed() 402 self._disconnectExpandedCollapsed()
399 vindex = QModelIndex() 405 vindex = QModelIndex()
400 index = self.model().index(0, 0) 406 index = self.model().index(0, 0)
401 while index.isValid(): 407 while index.isValid():
402 vindex = index 408 vindex = index
403 index = self.indexBelow(index) 409 index = self.indexBelow(index)
404 410
405 # step 2: go up collapsing all directory items 411 # step 2: go up collapsing all directory items
406 index = vindex 412 index = vindex
407 while index.isValid(): 413 while index.isValid():
408 itm = self.model().item(index) 414 itm = self.model().item(index)
409 if ( 415 if isinstance(
410 isinstance( 416 itm,
411 itm, 417 (ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem),
412 (ProjectBrowserSimpleDirectoryItem, 418 ) and self.isExpanded(index):
413 ProjectBrowserDirectoryItem)) and
414 self.isExpanded(index)
415 ):
416 self.collapse(index) 419 self.collapse(index)
417 index = self.indexAbove(index) 420 index = self.indexAbove(index)
418 self.layoutDisplay() 421 self.layoutDisplay()
419 self._connectExpandedCollapsed() 422 self._connectExpandedCollapsed()
420 423
421 def _showContextMenu(self, menu): 424 def _showContextMenu(self, menu):
422 """ 425 """
423 Protected slot called before the context menu is shown. 426 Protected slot called before the context menu is shown.
424 427
425 It enables/disables the VCS menu entries depending on the overall 428 It enables/disables the VCS menu entries depending on the overall
426 VCS status and the file status. 429 VCS status and the file status.
427 430
428 @param menu reference to the menu to be shown (QMenu) 431 @param menu reference to the menu to be shown (QMenu)
429 """ 432 """
430 if self.project.vcs is None: 433 if self.project.vcs is None:
431 for act in self.menuActions: 434 for act in self.menuActions:
432 act.setEnabled(True) 435 act.setEnabled(True)
433 else: 436 else:
434 self.vcsHelper.showContextMenu(menu, self.menuActions) 437 self.vcsHelper.showContextMenu(menu, self.menuActions)
435 438
436 def _showContextMenuMulti(self, menu): 439 def _showContextMenuMulti(self, menu):
437 """ 440 """
438 Protected slot called before the context menu (multiple selections) is 441 Protected slot called before the context menu (multiple selections) is
439 shown. 442 shown.
440 443
441 It enables/disables the VCS menu entries depending on the overall 444 It enables/disables the VCS menu entries depending on the overall
442 VCS status and the files status. 445 VCS status and the files status.
443 446
444 @param menu reference to the menu to be shown (QMenu) 447 @param menu reference to the menu to be shown (QMenu)
445 """ 448 """
446 if self.project.vcs is None: 449 if self.project.vcs is None:
447 for act in self.multiMenuActions: 450 for act in self.multiMenuActions:
448 act.setEnabled(True) 451 act.setEnabled(True)
449 else: 452 else:
450 self.vcsHelper.showContextMenuMulti(menu, self.multiMenuActions) 453 self.vcsHelper.showContextMenuMulti(menu, self.multiMenuActions)
451 454
452 def _showContextMenuDir(self, menu): 455 def _showContextMenuDir(self, menu):
453 """ 456 """
454 Protected slot called before the context menu is shown. 457 Protected slot called before the context menu is shown.
455 458
456 It enables/disables the VCS menu entries depending on the overall 459 It enables/disables the VCS menu entries depending on the overall
457 VCS status and the directory status. 460 VCS status and the directory status.
458 461
459 @param menu reference to the menu to be shown (QMenu) 462 @param menu reference to the menu to be shown (QMenu)
460 """ 463 """
461 if self.project.vcs is None: 464 if self.project.vcs is None:
462 for act in self.dirMenuActions: 465 for act in self.dirMenuActions:
463 act.setEnabled(True) 466 act.setEnabled(True)
464 else: 467 else:
465 self.vcsHelper.showContextMenuDir(menu, self.dirMenuActions) 468 self.vcsHelper.showContextMenuDir(menu, self.dirMenuActions)
466 469
467 def _showContextMenuDirMulti(self, menu): 470 def _showContextMenuDirMulti(self, menu):
468 """ 471 """
469 Protected slot called before the context menu is shown. 472 Protected slot called before the context menu is shown.
470 473
471 It enables/disables the VCS menu entries depending on the overall 474 It enables/disables the VCS menu entries depending on the overall
472 VCS status and the directory status. 475 VCS status and the directory status.
473 476
474 @param menu reference to the menu to be shown (QMenu) 477 @param menu reference to the menu to be shown (QMenu)
475 """ 478 """
476 if self.project.vcs is None: 479 if self.project.vcs is None:
477 for act in self.dirMultiMenuActions: 480 for act in self.dirMultiMenuActions:
478 act.setEnabled(True) 481 act.setEnabled(True)
479 else: 482 else:
480 self.vcsHelper.showContextMenuDirMulti( 483 self.vcsHelper.showContextMenuDirMulti(menu, self.dirMultiMenuActions)
481 menu, self.dirMultiMenuActions) 484
482
483 def _showContextMenuBack(self, menu): 485 def _showContextMenuBack(self, menu):
484 """ 486 """
485 Protected slot called before the context menu is shown. 487 Protected slot called before the context menu is shown.
486 488
487 @param menu reference to the menu to be shown (QMenu) 489 @param menu reference to the menu to be shown (QMenu)
488 """ 490 """
489 # nothing to do for now 491 # nothing to do for now
490 return 492 return
491 493
492 def _selectEntries(self, local=True, filterList=None): 494 def _selectEntries(self, local=True, filterList=None):
493 """ 495 """
494 Protected method to select entries based on their VCS status. 496 Protected method to select entries based on their VCS status.
495 497
496 @param local flag indicating local (i.e. non VCS controlled) 498 @param local flag indicating local (i.e. non VCS controlled)
497 file/directory entries should be selected (boolean) 499 file/directory entries should be selected (boolean)
498 @param filterList list of classes to check against 500 @param filterList list of classes to check against
499 """ 501 """
500 if self.project.vcs is None: 502 if self.project.vcs is None:
501 return 503 return
502 504
503 compareString = ( 505 compareString = (
504 QCoreApplication.translate('ProjectBaseBrowser', "local") 506 QCoreApplication.translate("ProjectBaseBrowser", "local")
505 if local else 507 if local
506 self.project.vcs.vcsName() 508 else self.project.vcs.vcsName()
507 ) 509 )
508 510
509 # expand all directories in order to iterate over all entries 511 # expand all directories in order to iterate over all entries
510 self._expandAllDirs() 512 self._expandAllDirs()
511 513
512 self.selectionModel().clear() 514 self.selectionModel().clear()
513 515
514 with EricOverrideCursor(): 516 with EricOverrideCursor():
515 # now iterate over all entries 517 # now iterate over all entries
516 startIndex = None 518 startIndex = None
517 endIndex = None 519 endIndex = None
518 selectedEntries = 0 520 selectedEntries = 0
519 index = self.model().index(0, 0) 521 index = self.model().index(0, 0)
520 while index.isValid(): 522 while index.isValid():
521 itm = self.model().item(index) 523 itm = self.model().item(index)
522 if ( 524 if self.wantedItem(itm, filterList) and compareString == itm.data(1):
523 self.wantedItem(itm, filterList) and 525 if startIndex is not None and startIndex.parent() != index.parent():
524 compareString == itm.data(1)
525 ):
526 if (
527 startIndex is not None and
528 startIndex.parent() != index.parent()
529 ):
530 self._setItemRangeSelected(startIndex, endIndex, True) 526 self._setItemRangeSelected(startIndex, endIndex, True)
531 startIndex = None 527 startIndex = None
532 selectedEntries += 1 528 selectedEntries += 1
533 if startIndex is None: 529 if startIndex is None:
534 startIndex = index 530 startIndex = index
538 self._setItemRangeSelected(startIndex, endIndex, True) 534 self._setItemRangeSelected(startIndex, endIndex, True)
539 startIndex = None 535 startIndex = None
540 index = self.indexBelow(index) 536 index = self.indexBelow(index)
541 if startIndex is not None: 537 if startIndex is not None:
542 self._setItemRangeSelected(startIndex, endIndex, True) 538 self._setItemRangeSelected(startIndex, endIndex, True)
543 539
544 if selectedEntries == 0: 540 if selectedEntries == 0:
545 EricMessageBox.information( 541 EricMessageBox.information(
546 self, 542 self,
543 QCoreApplication.translate("ProjectBaseBrowser", "Select entries"),
547 QCoreApplication.translate( 544 QCoreApplication.translate(
548 'ProjectBaseBrowser', "Select entries"), 545 "ProjectBaseBrowser", """There were no matching entries found."""
549 QCoreApplication.translate( 546 ),
550 'ProjectBaseBrowser', 547 )
551 """There were no matching entries found.""")) 548
552
553 def selectLocalEntries(self): 549 def selectLocalEntries(self):
554 """ 550 """
555 Public slot to handle the select local files context menu entries. 551 Public slot to handle the select local files context menu entries.
556 """ 552 """
557 self._selectEntries(local=True, filterList=[ProjectBrowserFileItem]) 553 self._selectEntries(local=True, filterList=[ProjectBrowserFileItem])
558 554
559 def selectVCSEntries(self): 555 def selectVCSEntries(self):
560 """ 556 """
561 Public slot to handle the select VCS files context menu entries. 557 Public slot to handle the select VCS files context menu entries.
562 """ 558 """
563 self._selectEntries(local=False, filterList=[ProjectBrowserFileItem]) 559 self._selectEntries(local=False, filterList=[ProjectBrowserFileItem])
564 560
565 def selectLocalDirEntries(self): 561 def selectLocalDirEntries(self):
566 """ 562 """
567 Public slot to handle the select local directories context menu 563 Public slot to handle the select local directories context menu
568 entries. 564 entries.
569 """ 565 """
570 self._selectEntries( 566 self._selectEntries(
571 local=True, 567 local=True,
572 filterList=[ProjectBrowserSimpleDirectoryItem, 568 filterList=[ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem],
573 ProjectBrowserDirectoryItem]) 569 )
574 570
575 def selectVCSDirEntries(self): 571 def selectVCSDirEntries(self):
576 """ 572 """
577 Public slot to handle the select VCS directories context menu entries. 573 Public slot to handle the select VCS directories context menu entries.
578 """ 574 """
579 self._selectEntries( 575 self._selectEntries(
580 local=False, 576 local=False,
581 filterList=[ProjectBrowserSimpleDirectoryItem, 577 filterList=[ProjectBrowserSimpleDirectoryItem, ProjectBrowserDirectoryItem],
582 ProjectBrowserDirectoryItem]) 578 )
583 579
584 def getExpandedItemNames(self): 580 def getExpandedItemNames(self):
585 """ 581 """
586 Public method to get the file/directory names of all expanded items. 582 Public method to get the file/directory names of all expanded items.
587 583
588 @return list of expanded items names (list of string) 584 @return list of expanded items names (list of string)
589 """ 585 """
590 expandedNames = [] 586 expandedNames = []
591 587
592 childIndex = self.model().index(0, 0) 588 childIndex = self.model().index(0, 0)
593 while childIndex.isValid(): 589 while childIndex.isValid():
594 if self.isExpanded(childIndex): 590 if self.isExpanded(childIndex):
595 with contextlib.suppress(AttributeError): 591 with contextlib.suppress(AttributeError):
596 expandedNames.append( 592 expandedNames.append(self.model().item(childIndex).name())
597 self.model().item(childIndex).name())
598 # only items defining the name() method are returned 593 # only items defining the name() method are returned
599 childIndex = self.indexBelow(childIndex) 594 childIndex = self.indexBelow(childIndex)
600 595
601 return expandedNames 596 return expandedNames
602 597
603 def expandItemsByName(self, names): 598 def expandItemsByName(self, names):
604 """ 599 """
605 Public method to expand items given their names. 600 Public method to expand items given their names.
606 601
607 @param names list of item names to be expanded (list of string) 602 @param names list of item names to be expanded (list of string)
608 """ 603 """
609 model = self.model() 604 model = self.model()
610 for name in names: 605 for name in names:
611 childIndex = model.index(0, 0) 606 childIndex = model.index(0, 0)
614 if model.item(childIndex).name() == name: 609 if model.item(childIndex).name() == name:
615 self.setExpanded(childIndex, True) 610 self.setExpanded(childIndex, True)
616 break 611 break
617 # ignore items not supporting this method 612 # ignore items not supporting this method
618 childIndex = self.indexBelow(childIndex) 613 childIndex = self.indexBelow(childIndex)
619 614
620 def _prepareRepopulateItem(self, name): 615 def _prepareRepopulateItem(self, name):
621 """ 616 """
622 Protected slot to handle the prepareRepopulateItem signal. 617 Protected slot to handle the prepareRepopulateItem signal.
623 618
624 @param name relative name of file item to be repopulated (string) 619 @param name relative name of file item to be repopulated (string)
625 """ 620 """
626 itm = self.currentItem() 621 itm = self.currentItem()
627 if itm is not None: 622 if itm is not None:
628 self.currentItemName = itm.data(0) 623 self.currentItemName = itm.data(0)
629 self.expandedNames = [] 624 self.expandedNames = []
630 sindex = self._model.itemIndexByName(name) 625 sindex = self._model.itemIndexByName(name)
631 if not sindex.isValid(): 626 if not sindex.isValid():
632 return 627 return
633 628
634 index = self.model().mapFromSource(sindex) 629 index = self.model().mapFromSource(sindex)
635 if not index.isValid(): 630 if not index.isValid():
636 return 631 return
637 632
638 childIndex = self.indexBelow(index) 633 childIndex = self.indexBelow(index)
639 while childIndex.isValid(): 634 while childIndex.isValid():
640 if childIndex.parent() == index.parent(): 635 if childIndex.parent() == index.parent():
641 break 636 break
642 if self.isExpanded(childIndex): 637 if self.isExpanded(childIndex):
643 self.expandedNames.append( 638 self.expandedNames.append(self.model().item(childIndex).data(0))
644 self.model().item(childIndex).data(0))
645 childIndex = self.indexBelow(childIndex) 639 childIndex = self.indexBelow(childIndex)
646 640
647 def _completeRepopulateItem(self, name): 641 def _completeRepopulateItem(self, name):
648 """ 642 """
649 Protected slot to handle the completeRepopulateItem signal. 643 Protected slot to handle the completeRepopulateItem signal.
650 644
651 @param name relative name of file item to be repopulated (string) 645 @param name relative name of file item to be repopulated (string)
652 """ 646 """
653 sindex = self._model.itemIndexByName(name) 647 sindex = self._model.itemIndexByName(name)
654 if sindex.isValid(): 648 if sindex.isValid():
655 index = self.model().mapFromSource(sindex) 649 index = self.model().mapFromSource(sindex)
656 if index.isValid(): 650 if index.isValid():
657 if self.isExpanded(index): 651 if self.isExpanded(index):
658 childIndex = self.indexBelow(index) 652 childIndex = self.indexBelow(index)
659 while childIndex.isValid(): 653 while childIndex.isValid():
660 if ( 654 if (
661 not childIndex.isValid() or 655 not childIndex.isValid()
662 childIndex.parent() == index.parent() 656 or childIndex.parent() == index.parent()
663 ): 657 ):
664 break 658 break
665 itm = self.model().item(childIndex) 659 itm = self.model().item(childIndex)
666 if itm is not None: 660 if itm is not None:
667 itemData = itm.data(0) 661 itemData = itm.data(0)
668 if ( 662 if (
669 self.currentItemName and 663 self.currentItemName
670 self.currentItemName == itemData 664 and self.currentItemName == itemData
671 ): 665 ):
672 self._selectSingleItem(childIndex) 666 self._selectSingleItem(childIndex)
673 if itemData in self.expandedNames: 667 if itemData in self.expandedNames:
674 self.setExpanded(childIndex, True) 668 self.setExpanded(childIndex, True)
675 childIndex = self.indexBelow(childIndex) 669 childIndex = self.indexBelow(childIndex)
676 else: 670 else:
677 self._selectSingleItem(index) 671 self._selectSingleItem(index)
678 self.expandedNames = [] 672 self.expandedNames = []
679 self.currentItemName = None 673 self.currentItemName = None
680 self._resort() 674 self._resort()
681 675
682 def currentItem(self): 676 def currentItem(self):
683 """ 677 """
684 Public method to get a reference to the current item. 678 Public method to get a reference to the current item.
685 679
686 @return reference to the current item 680 @return reference to the current item
687 """ 681 """
688 itm = self.model().item(self.currentIndex()) 682 itm = self.model().item(self.currentIndex())
689 return itm 683 return itm
690 684
691 def _keyboardSearchType(self, item): 685 def _keyboardSearchType(self, item):
692 """ 686 """
693 Protected method to check, if the item is of the correct type. 687 Protected method to check, if the item is of the correct type.
694 688
695 @param item reference to the item 689 @param item reference to the item
696 @type BrowserItem 690 @type BrowserItem
697 @return flag indicating a correct type 691 @return flag indicating a correct type
698 @rtype bool 692 @rtype bool
699 """ 693 """
700 return isinstance( 694 return isinstance(
701 item, (BrowserDirectoryItem, BrowserFileItem, 695 item,
702 ProjectBrowserSimpleDirectoryItem, 696 (
703 ProjectBrowserDirectoryItem, ProjectBrowserFileItem)) 697 BrowserDirectoryItem,
704 698 BrowserFileItem,
699 ProjectBrowserSimpleDirectoryItem,
700 ProjectBrowserDirectoryItem,
701 ProjectBrowserFileItem,
702 ),
703 )
704
705 ########################################################################### 705 ###########################################################################
706 ## Support for hooks below 706 ## Support for hooks below
707 ########################################################################### 707 ###########################################################################
708 708
709 def _initHookMethods(self): 709 def _initHookMethods(self):
710 """ 710 """
711 Protected method to initialize the hooks dictionary. 711 Protected method to initialize the hooks dictionary.
712 712
713 This method should be overridden by subclasses. All supported 713 This method should be overridden by subclasses. All supported
714 hook methods should be initialized with a None value. The keys 714 hook methods should be initialized with a None value. The keys
715 must be strings. 715 must be strings.
716 """ 716 """
717 self.hooks = {} 717 self.hooks = {}
718 718
719 def __checkHookKey(self, key): 719 def __checkHookKey(self, key):
720 """ 720 """
721 Private method to check a hook key. 721 Private method to check a hook key.
722 722
723 @param key key of the hook to check (string) 723 @param key key of the hook to check (string)
724 @exception KeyError raised to indicate an invalid hook 724 @exception KeyError raised to indicate an invalid hook
725 """ 725 """
726 if len(self.hooks) == 0: 726 if len(self.hooks) == 0:
727 raise KeyError("Hooks are not initialized.") 727 raise KeyError("Hooks are not initialized.")
728 728
729 if key not in self.hooks: 729 if key not in self.hooks:
730 raise KeyError(key) 730 raise KeyError(key)
731 731
732 def addHookMethod(self, key, method): 732 def addHookMethod(self, key, method):
733 """ 733 """
734 Public method to add a hook method to the dictionary. 734 Public method to add a hook method to the dictionary.
735 735
736 @param key for the hook method (string) 736 @param key for the hook method (string)
737 @param method reference to the hook method (method object) 737 @param method reference to the hook method (method object)
738 """ 738 """
739 self.__checkHookKey(key) 739 self.__checkHookKey(key)
740 self.hooks[key] = method 740 self.hooks[key] = method
741 741
742 def addHookMethodAndMenuEntry(self, key, method, menuEntry): 742 def addHookMethodAndMenuEntry(self, key, method, menuEntry):
743 """ 743 """
744 Public method to add a hook method to the dictionary. 744 Public method to add a hook method to the dictionary.
745 745
746 @param key for the hook method (string) 746 @param key for the hook method (string)
747 @param method reference to the hook method (method object) 747 @param method reference to the hook method (method object)
748 @param menuEntry entry to be shown in the context menu (string) 748 @param menuEntry entry to be shown in the context menu (string)
749 """ 749 """
750 self.addHookMethod(key, method) 750 self.addHookMethod(key, method)
751 self.hooksMenuEntries[key] = menuEntry 751 self.hooksMenuEntries[key] = menuEntry
752 752
753 def removeHookMethod(self, key): 753 def removeHookMethod(self, key):
754 """ 754 """
755 Public method to remove a hook method from the dictionary. 755 Public method to remove a hook method from the dictionary.
756 756
757 @param key for the hook method (string) 757 @param key for the hook method (string)
758 """ 758 """
759 self.__checkHookKey(key) 759 self.__checkHookKey(key)
760 self.hooks[key] = None 760 self.hooks[key] = None
761 if key in self.hooksMenuEntries: 761 if key in self.hooksMenuEntries:
762 del self.hooksMenuEntries[key] 762 del self.hooksMenuEntries[key]
763 763
764 ################################################################## 764 ##################################################################
765 ## Configure method below 765 ## Configure method below
766 ################################################################## 766 ##################################################################
767 767
768 def _configure(self): 768 def _configure(self):
769 """ 769 """
770 Protected method to open the configuration dialog. 770 Protected method to open the configuration dialog.
771 """ 771 """
772 ericApp().getObject("UserInterface").showPreferences( 772 ericApp().getObject("UserInterface").showPreferences("projectBrowserPage")
773 "projectBrowserPage")

eric ide

mercurial