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()) |
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") |
|