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