eric6/UI/Browser.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 6987
3371a03ed0a7
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a browser with class browsing capabilities.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 from PyQt5.QtCore import QModelIndex, pyqtSignal, QUrl, Qt, QCoreApplication, \
15 QItemSelectionModel, QElapsedTimer
16 from PyQt5.QtGui import QDesktopServices
17 from PyQt5.QtWidgets import QTreeView, QApplication, QMenu, QAbstractItemView
18
19 from E5Gui.E5Application import e5App
20 from E5Gui import E5FileDialog, E5MessageBox
21
22 from Project.ProjectBrowserModel import ProjectBrowserSimpleDirectoryItem
23 from .BrowserModel import BrowserModel, BrowserDirectoryItem, \
24 BrowserFileItem, BrowserClassItem, BrowserMethodItem, \
25 BrowserClassAttributeItem, BrowserImportItem, BrowserImportsItem, \
26 BrowserSysPathItem, BrowserGlobalsItem
27 from .BrowserSortFilterProxyModel import BrowserSortFilterProxyModel
28
29 import UI.PixmapCache
30 import Preferences
31 import Utilities
32 import Utilities.MimeTypes
33 from Globals import qVersionTuple
34
35
36 class Browser(QTreeView):
37 """
38 Class used to display a file system tree.
39
40 Via the context menu that
41 is displayed by a right click the user can select various actions on
42 the selected file.
43
44 @signal sourceFile(filename) emitted to open a Python file at a line (str)
45 @signal sourceFile(filename, lineno) emitted to open a Python file at a
46 line (str, int)
47 @signal sourceFile(filename, lineno, type) emitted to open a Python file
48 at a line giving an explicit file type (str, int, str)
49 @signal sourceFile(filename, linenos) emitted to open a Python file giving
50 a list of lines(str, list)
51 @signal designerFile(filename) emitted to open a Qt-Designer file (str)
52 @signal linguistFile(filename) emitted to open a Qt-Linguist (*.ts)
53 file (str)
54 @signal trpreview(filenames) emitted to preview Qt-Linguist (*.qm)
55 files (list of str)
56 @signal trpreview(filenames, ignore) emitted to preview Qt-Linguist (*.qm)
57 files indicating whether non-existent files shall be ignored
58 (list of str, bool)
59 @signal projectFile(filename) emitted to open an eric project file (str)
60 @signal multiProjectFile(filename) emitted to open an eric multi project
61 file (str)
62 @signal pixmapFile(filename) emitted to open a pixmap file (str)
63 @signal pixmapEditFile(filename) emitted to edit a pixmap file (str)
64 @signal svgFile(filename) emitted to open a SVG file (str)
65 @signal binaryFile(filename) emitted to open a file as binary (str)
66 @signal unittestOpen(filename) emitted to open a Python file for a
67 unit test (str)
68 """
69 sourceFile = pyqtSignal((str, ), (str, int), (str, list), (str, int, str))
70 designerFile = pyqtSignal(str)
71 linguistFile = pyqtSignal(str)
72 trpreview = pyqtSignal((list, ), (list, bool))
73 projectFile = pyqtSignal(str)
74 multiProjectFile = pyqtSignal(str)
75 pixmapFile = pyqtSignal(str)
76 pixmapEditFile = pyqtSignal(str)
77 svgFile = pyqtSignal(str)
78 binaryFile = pyqtSignal(str)
79 unittestOpen = pyqtSignal(str)
80
81 def __init__(self, parent=None):
82 """
83 Constructor
84
85 @param parent parent widget (QWidget)
86 """
87 super(Browser, self).__init__(parent)
88
89 self.setWindowTitle(QCoreApplication.translate('Browser',
90 'File-Browser'))
91 self.setWindowIcon(UI.PixmapCache.getIcon("eric.png"))
92
93 self.__embeddedBrowser = Preferences.getUI("LayoutFileBrowserEmbedded")
94
95 self.__model = BrowserModel()
96 self.__sortModel = BrowserSortFilterProxyModel()
97 self.__sortModel.setSourceModel(self.__model)
98 self.setModel(self.__sortModel)
99
100 self.selectedItemsFilter = [BrowserFileItem]
101
102 self._activating = False
103
104 self.setContextMenuPolicy(Qt.CustomContextMenu)
105 self.customContextMenuRequested.connect(self._contextMenuRequested)
106 self.activated.connect(self._openItem)
107 self.expanded.connect(self._resizeColumns)
108 self.collapsed.connect(self._resizeColumns)
109
110 self.setWhatsThis(QCoreApplication.translate(
111 'Browser',
112 """<b>The Browser Window</b>"""
113 """<p>This allows you to easily navigate the hierarchy of"""
114 """ directories and files on your system, identify the Python"""
115 """ programs and open them up in a Source Viewer window. The"""
116 """ window displays several separate hierarchies.</p>"""
117 """<p>The first hierarchy is only shown if you have opened a"""
118 """ program for debugging and its root is the directory"""
119 """ containing that program. Usually all of the separate files"""
120 """ that make up a Python application are held in the same"""
121 """ directory, so this hierarchy gives you easy access to most"""
122 """ of what you will need.</p>"""
123 """<p>The next hierarchy is used to easily navigate the"""
124 """ directories that are specified in the Python"""
125 """ <tt>sys.path</tt> variable.</p>"""
126 """<p>The remaining hierarchies allow you navigate your system"""
127 """ as a whole. On a UNIX system there will be a hierarchy with"""
128 """ <tt>/</tt> at its root and another with the user home"""
129 """ directory. On a Windows system there will be a hierarchy for"""
130 """ each drive on the"""
131 """ system.</p>"""
132 """<p>Python programs (i.e. those with a <tt>.py</tt> file name"""
133 """ suffix) are identified in the hierarchies with a Python"""
134 """ icon. The right mouse button will popup a menu which lets"""
135 """ you open the file in a Source Viewer window, open the file"""
136 """ for debugging or use it for a unittest run.</p>"""
137 """<p>The context menu of a class, function or method allows you"""
138 """ to open the file defining this class, function or method and"""
139 """ will ensure, that the correct source line is visible.</p>"""
140 """<p>Qt-Designer files (i.e. those with a <tt>.ui</tt> file"""
141 """ name suffix) are shown with a Designer icon. The context"""
142 """ menu of these files allows you to start Qt-Designer with"""
143 """ that file.</p>"""
144 """<p>Qt-Linguist files (i.e. those with a <tt>.ts</tt> file"""
145 """ name suffix) are shown with a Linguist icon. The context"""
146 """ menu of these files allows you to start Qt-Linguist with"""
147 """ that file.</p>"""
148 ))
149
150 self.__createPopupMenus()
151
152 self._init() # perform common initialization tasks
153
154 self._keyboardSearchString = ""
155 self._keyboardSearchTimer = QElapsedTimer()
156 self._keyboardSearchTimer.invalidate()
157
158 def _init(self):
159 """
160 Protected method to perform initialization tasks common to this
161 base class and all derived classes.
162 """
163 self.setRootIsDecorated(True)
164 self.setAlternatingRowColors(True)
165
166 header = self.header()
167 header.setSortIndicator(0, Qt.AscendingOrder)
168 header.setSortIndicatorShown(True)
169 if qVersionTuple() >= (5, 0, 0):
170 header.setSectionsClickable(True)
171 else:
172 header.setClickable(True)
173
174 self.setSortingEnabled(True)
175
176 self.setSelectionMode(QAbstractItemView.ExtendedSelection)
177 self.setSelectionBehavior(QAbstractItemView.SelectRows)
178
179 self.header().setStretchLastSection(True)
180 self.headerSize0 = 0
181 self.layoutDisplay()
182
183 def layoutDisplay(self):
184 """
185 Public slot to perform a layout operation.
186 """
187 self._resizeColumns(QModelIndex())
188 self._resort()
189
190 def _resizeColumns(self, index):
191 """
192 Protected slot to resize the view when items get expanded or collapsed.
193
194 @param index index of item (QModelIndex)
195 """
196 w = max(100, self.sizeHintForColumn(0))
197 if w != self.headerSize0:
198 self.header().resizeSection(0, w)
199 self.headerSize0 = w
200
201 def _resort(self):
202 """
203 Protected slot to resort the tree.
204 """
205 self.model().sort(self.header().sortIndicatorSection(),
206 self.header().sortIndicatorOrder())
207
208 def __createPopupMenus(self):
209 """
210 Private method to generate the various popup menus.
211 """
212 # create the popup menu for source files
213 self.sourceMenu = QMenu(self)
214 self.sourceMenu.addAction(
215 QCoreApplication.translate('Browser', 'Open'), self._openItem)
216 self.unittestAct = self.sourceMenu.addAction(
217 QCoreApplication.translate('Browser', 'Run unittest...'),
218 self.handleUnittest)
219 self.sourceMenu.addSeparator()
220 self.mimeTypeAct = self.sourceMenu.addAction(
221 QCoreApplication.translate('Browser', 'Show Mime-Type'),
222 self.__showMimeType)
223 self.sourceMenu.addSeparator()
224 self.sourceMenu.addAction(
225 QCoreApplication.translate('Browser', 'Copy Path to Clipboard'),
226 self._copyToClipboard)
227
228 # create the popup menu for general use
229 self.menu = QMenu(self)
230 self.menu.addAction(
231 QCoreApplication.translate('Browser', 'Open'), self._openItem)
232 self.menu.addAction(
233 QCoreApplication.translate('Browser', 'Open in Hex Editor'),
234 self._openHexEditor)
235 self.editPixmapAct = self.menu.addAction(
236 QCoreApplication.translate('Browser', 'Open in Icon Editor'),
237 self._editPixmap)
238 self.menu.addSeparator()
239 self.mimeTypeAct = self.menu.addAction(
240 QCoreApplication.translate('Browser', 'Show Mime-Type'),
241 self.__showMimeType)
242 self.menu.addSeparator()
243 self.menu.addAction(
244 QCoreApplication.translate('Browser', 'Copy Path to Clipboard'),
245 self._copyToClipboard)
246 if self.__embeddedBrowser in [1, 2]:
247 self.menu.addSeparator()
248 self.menu.addAction(
249 QCoreApplication.translate('Browser', 'Configure...'),
250 self.__configure)
251
252 # create the menu for multiple selected files
253 self.multiMenu = QMenu(self)
254 self.multiMenu.addAction(
255 QCoreApplication.translate('Browser', 'Open'), self._openItem)
256 if self.__embeddedBrowser in [1, 2]:
257 self.multiMenu.addSeparator()
258 self.multiMenu.addAction(
259 QCoreApplication.translate('Browser', 'Configure...'),
260 self.__configure)
261
262 # create the directory menu
263 self.dirMenu = QMenu(self)
264 self.dirMenu.addAction(
265 QCoreApplication.translate('Browser', 'New toplevel directory...'),
266 self.__newToplevelDir)
267 self.addAsTopLevelAct = self.dirMenu.addAction(
268 QCoreApplication.translate('Browser', 'Add as toplevel directory'),
269 self.__addAsToplevelDir)
270 self.removeFromToplevelAct = self.dirMenu.addAction(
271 QCoreApplication.translate('Browser', 'Remove from toplevel'),
272 self.__removeToplevel)
273 self.dirMenu.addSeparator()
274 self.dirMenu.addAction(
275 QCoreApplication.translate('Browser', 'Refresh directory'),
276 self.__refreshDirectory)
277 self.dirMenu.addSeparator()
278 self.dirMenu.addAction(
279 QCoreApplication.translate('Browser', 'Find in this directory'),
280 self.__findInDirectory)
281 self.dirMenu.addAction(
282 QCoreApplication.translate(
283 'Browser', 'Find&&Replace in this directory'),
284 self.__replaceInDirectory)
285 self.dirMenu.addAction(
286 QCoreApplication.translate('Browser', 'Copy Path to Clipboard'),
287 self._copyToClipboard)
288 if self.__embeddedBrowser in [1, 2]:
289 self.dirMenu.addSeparator()
290 self.dirMenu.addAction(
291 QCoreApplication.translate('Browser', 'Configure...'),
292 self.__configure)
293
294 # create the attribute menu
295 self.gotoMenu = QMenu(QCoreApplication.translate('Browser', "Goto"),
296 self)
297 self.gotoMenu.aboutToShow.connect(self._showGotoMenu)
298 self.gotoMenu.triggered.connect(self._gotoAttribute)
299
300 self.attributeMenu = QMenu(self)
301 self.attributeMenu.addAction(
302 QCoreApplication.translate('Browser', 'New toplevel directory...'),
303 self.__newToplevelDir)
304 self.attributeMenu.addSeparator()
305 self.attributeMenu.addMenu(self.gotoMenu)
306 if self.__embeddedBrowser in [1, 2]:
307 self.attributeMenu.addSeparator()
308 self.attributeMenu.addAction(
309 QCoreApplication.translate('Browser', 'Configure...'),
310 self.__configure)
311
312 # create the background menu
313 self.backMenu = QMenu(self)
314 self.backMenu.addAction(
315 QCoreApplication.translate('Browser', 'New toplevel directory...'),
316 self.__newToplevelDir)
317 if self.__embeddedBrowser in [1, 2]:
318 self.backMenu.addSeparator()
319 self.backMenu.addAction(
320 QCoreApplication.translate('Browser', 'Configure...'),
321 self.__configure)
322
323 def mouseDoubleClickEvent(self, mouseEvent):
324 """
325 Protected method of QAbstractItemView.
326
327 Reimplemented to disable expanding/collapsing of items when
328 double-clicking. Instead the double-clicked entry is opened.
329
330 @param mouseEvent the mouse event (QMouseEvent)
331 """
332 index = self.indexAt(mouseEvent.pos())
333 if index.isValid():
334 itm = self.model().item(index)
335 if isinstance(itm, (
336 BrowserDirectoryItem, BrowserImportsItem,
337 ProjectBrowserSimpleDirectoryItem, BrowserSysPathItem,
338 BrowserGlobalsItem)):
339 self.setExpanded(index, not self.isExpanded(index))
340 else:
341 self._openItem()
342
343 def _contextMenuRequested(self, coord):
344 """
345 Protected slot to show the context menu of the listview.
346
347 @param coord the position of the mouse pointer (QPoint)
348 """
349 categories = self.getSelectedItemsCountCategorized(
350 [BrowserDirectoryItem, BrowserFileItem,
351 BrowserClassItem, BrowserMethodItem])
352 cnt = categories["sum"]
353 bfcnt = categories[str(BrowserFileItem)]
354 if cnt > 1 and cnt == bfcnt:
355 self.multiMenu.popup(self.mapToGlobal(coord))
356 else:
357 index = self.indexAt(coord)
358
359 if index.isValid():
360 self.setCurrentIndex(index)
361 flags = QItemSelectionModel.SelectionFlags(
362 QItemSelectionModel.ClearAndSelect |
363 QItemSelectionModel.Rows)
364 self.selectionModel().select(index, flags)
365
366 itm = self.model().item(index)
367 coord = self.mapToGlobal(coord)
368 if isinstance(itm, BrowserFileItem):
369 if itm.isPython3File():
370 if itm.fileName().endswith('.py'):
371 self.unittestAct.setEnabled(True)
372 else:
373 self.unittestAct.setEnabled(False)
374 self.sourceMenu.popup(coord)
375 else:
376 self.editPixmapAct.setVisible(itm.isPixmapFile())
377 self.menu.popup(coord)
378 elif isinstance(itm, BrowserClassItem) or \
379 isinstance(itm, BrowserMethodItem) or \
380 isinstance(itm, BrowserImportItem):
381 self.editPixmapAct.setVisible(False)
382 self.menu.popup(coord)
383 elif isinstance(itm, BrowserClassAttributeItem):
384 self.attributeMenu.popup(coord)
385 elif isinstance(itm, BrowserDirectoryItem):
386 if not index.parent().isValid():
387 self.removeFromToplevelAct.setEnabled(True)
388 self.addAsTopLevelAct.setEnabled(False)
389 else:
390 self.removeFromToplevelAct.setEnabled(False)
391 self.addAsTopLevelAct.setEnabled(True)
392 self.dirMenu.popup(coord)
393 else:
394 self.backMenu.popup(coord)
395 else:
396 self.backMenu.popup(self.mapToGlobal(coord))
397
398 def _showGotoMenu(self):
399 """
400 Protected slot to prepare the goto submenu of the attribute menu.
401 """
402 self.gotoMenu.clear()
403
404 itm = self.model().item(self.currentIndex())
405 linenos = itm.linenos()
406 fileName = itm.fileName()
407
408 for lineno in sorted(linenos):
409 act = self.gotoMenu.addAction(
410 QCoreApplication.translate(
411 'Browser', "Line {0}").format(lineno))
412 act.setData([fileName, lineno])
413
414 def _gotoAttribute(self, act):
415 """
416 Protected slot to handle the selection of the goto menu.
417
418 @param act reference to the action (E5Action)
419 """
420 fileName, lineno = act.data()
421 self.sourceFile[str, int].emit(fileName, lineno)
422
423 def handlePreferencesChanged(self):
424 """
425 Public slot used to handle the preferencesChanged signal.
426 """
427 self.model().preferencesChanged()
428 self._resort()
429
430 def _openItem(self):
431 """
432 Protected slot to handle the open popup menu entry.
433 """
434 itmList = self.getSelectedItems(
435 [BrowserFileItem, BrowserClassItem,
436 BrowserMethodItem, BrowserClassAttributeItem,
437 BrowserImportItem])
438
439 if not self._activating:
440 self._activating = True
441 for itm in itmList:
442 if isinstance(itm, BrowserFileItem):
443 if itm.isPython2File():
444 self.sourceFile[str].emit(itm.fileName())
445 elif itm.isPython3File():
446 self.sourceFile[str].emit(itm.fileName())
447 elif itm.isRubyFile():
448 self.sourceFile[str, int, str].emit(
449 itm.fileName(), -1, "Ruby")
450 elif itm.isDFile():
451 self.sourceFile[str, int, str].emit(
452 itm.fileName(), -1, "D")
453 elif itm.isDesignerFile():
454 self.designerFile.emit(itm.fileName())
455 elif itm.isLinguistFile():
456 if itm.fileExt() == '.ts':
457 self.linguistFile.emit(itm.fileName())
458 else:
459 self.trpreview.emit([itm.fileName()])
460 elif itm.isProjectFile():
461 self.projectFile.emit(itm.fileName())
462 elif itm.isMultiProjectFile():
463 self.multiProjectFile.emit(itm.fileName())
464 elif itm.isIdlFile():
465 self.sourceFile[str].emit(itm.fileName())
466 elif itm.isProtobufFile():
467 self.sourceFile[str].emit(itm.fileName())
468 elif itm.isResourcesFile():
469 self.sourceFile[str].emit(itm.fileName())
470 elif itm.isSvgFile():
471 self.svgFile.emit(itm.fileName())
472 elif itm.isPixmapFile():
473 self.pixmapFile.emit(itm.fileName())
474 else:
475 if Utilities.MimeTypes.isTextFile(itm.fileName()):
476 self.sourceFile[str].emit(itm.fileName())
477 else:
478 QDesktopServices.openUrl(QUrl(itm.fileName()))
479 elif isinstance(itm, BrowserClassItem):
480 self.sourceFile[str, int].emit(
481 itm.fileName(), itm.classObject().lineno)
482 elif isinstance(itm, BrowserMethodItem):
483 self.sourceFile[str, int].emit(
484 itm.fileName(), itm.functionObject().lineno)
485 elif isinstance(itm, BrowserClassAttributeItem):
486 self.sourceFile[str, int].emit(
487 itm.fileName(), itm.attributeObject().lineno)
488 elif isinstance(itm, BrowserImportItem):
489 self.sourceFile[str, list].emit(
490 itm.fileName(), itm.linenos())
491 self._activating = False
492
493 def __showMimeType(self):
494 """
495 Private slot to show the mime type of the selected entry.
496 """
497 itmList = self.getSelectedItems(
498 [BrowserFileItem, BrowserClassItem,
499 BrowserMethodItem, BrowserClassAttributeItem,
500 BrowserImportItem])
501 if itmList:
502 mimetype = Utilities.MimeTypes.mimeType(itmList[0].fileName())
503 if mimetype is None:
504 E5MessageBox.warning(
505 self,
506 QCoreApplication.translate('Browser', "Show Mime-Type"),
507 QCoreApplication.translate(
508 'Browser',
509 """The mime type of the file could not be"""
510 """ determined."""))
511 elif mimetype.split("/")[0] == "text":
512 E5MessageBox.information(
513 self,
514 QCoreApplication.translate('Browser', "Show Mime-Type"),
515 QCoreApplication.translate(
516 'Browser',
517 """The file has the mime type <b>{0}</b>.""")
518 .format(mimetype))
519 else:
520 textMimeTypesList = Preferences.getUI("TextMimeTypes")
521 if mimetype in textMimeTypesList:
522 E5MessageBox.information(
523 self,
524 QCoreApplication.translate(
525 'Browser', "Show Mime-Type"),
526 QCoreApplication.translate(
527 'Browser',
528 """The file has the mime type <b>{0}</b>.""")
529 .format(mimetype))
530 else:
531 ok = E5MessageBox.yesNo(
532 self,
533 QCoreApplication.translate(
534 'Browser', "Show Mime-Type"),
535 QCoreApplication.translate(
536 'Browser',
537 """The file has the mime type <b>{0}</b>."""
538 """<br/> Shall it be added to the list of"""
539 """ text mime types?""").format(mimetype))
540 if ok:
541 textMimeTypesList.append(mimetype)
542 Preferences.setUI("TextMimeTypes", textMimeTypesList)
543
544 def _editPixmap(self):
545 """
546 Protected slot to handle the open in icon editor popup menu entry.
547 """
548 itmList = self.getSelectedItems([BrowserFileItem])
549
550 for itm in itmList:
551 if isinstance(itm, BrowserFileItem):
552 if itm.isPixmapFile():
553 self.pixmapEditFile.emit(itm.fileName())
554
555 def _openHexEditor(self):
556 """
557 Protected slot to handle the open in hex editor popup menu entry.
558 """
559 itmList = self.getSelectedItems([BrowserFileItem])
560
561 for itm in itmList:
562 if isinstance(itm, BrowserFileItem):
563 self.binaryFile.emit(itm.fileName())
564
565 def _copyToClipboard(self):
566 """
567 Protected method to copy the text shown for an entry to the clipboard.
568 """
569 itm = self.model().item(self.currentIndex())
570 try:
571 fn = itm.fileName()
572 except AttributeError:
573 try:
574 fn = itm.dirName()
575 except AttributeError:
576 fn = ""
577
578 if fn:
579 cb = QApplication.clipboard()
580 cb.setText(fn)
581
582 def handleUnittest(self):
583 """
584 Public slot to handle the unittest popup menu entry.
585 """
586 try:
587 index = self.currentIndex()
588 itm = self.model().item(index)
589 pyfn = itm.fileName()
590 except AttributeError:
591 pyfn = None
592
593 if pyfn is not None:
594 self.unittestOpen.emit(pyfn)
595
596 def __newToplevelDir(self):
597 """
598 Private slot to handle the New toplevel directory popup menu entry.
599 """
600 dname = E5FileDialog.getExistingDirectory(
601 None,
602 QCoreApplication.translate('Browser', "New toplevel directory"),
603 "",
604 E5FileDialog.Options(E5FileDialog.ShowDirsOnly))
605 if dname:
606 dname = os.path.abspath(Utilities.toNativeSeparators(dname))
607 self.__model.addTopLevelDir(dname)
608
609 def __removeToplevel(self):
610 """
611 Private slot to handle the Remove from toplevel popup menu entry.
612 """
613 index = self.currentIndex()
614 sindex = self.model().mapToSource(index)
615 self.__model.removeToplevelDir(sindex)
616
617 def __addAsToplevelDir(self):
618 """
619 Private slot to handle the Add as toplevel directory popup menu entry.
620 """
621 index = self.currentIndex()
622 dname = self.model().item(index).dirName()
623 self.__model.addTopLevelDir(dname)
624
625 def __refreshDirectory(self):
626 """
627 Private slot to refresh a directory entry.
628 """
629 index = self.currentIndex()
630 refreshDir = self.model().item(index).dirName()
631 self.__model.directoryChanged(refreshDir)
632
633 def __findInDirectory(self):
634 """
635 Private slot to handle the Find in directory popup menu entry.
636 """
637 index = self.currentIndex()
638 searchDir = self.model().item(index).dirName()
639
640 e5App().getObject("UserInterface")\
641 .showFindFilesDialog(searchDir=searchDir)
642
643 def __replaceInDirectory(self):
644 """
645 Private slot to handle the Find&Replace in directory popup menu entry.
646 """
647 index = self.currentIndex()
648 searchDir = self.model().item(index).dirName()
649
650 e5App().getObject("UserInterface")\
651 .showReplaceFilesDialog(searchDir=searchDir)
652
653 def handleProgramChange(self, fn):
654 """
655 Public slot to handle the programChange signal.
656
657 @param fn file name (string)
658 """
659 self.__model.programChange(os.path.dirname(fn))
660
661 def handleInterpreterChanged(self, interpreter):
662 """
663 Public slot to handle a change of the debug client's interpreter.
664
665 @param interpreter interpreter of the debug client (string)
666 """
667 self.__model.interpreterChanged(interpreter)
668
669 def wantedItem(self, itm, filterList=None):
670 """
671 Public method to check type of an item.
672
673 @param itm the item to check (BrowserItem)
674 @param filterList list of classes to check against
675 @return flag indicating item is a valid type (boolean)
676 """
677 if filterList is None:
678 filterList = self.selectedItemsFilter
679 for typ in filterList:
680 if isinstance(itm, typ):
681 return True
682 return False
683
684 def getSelectedItems(self, filterList=None):
685 """
686 Public method to get the selected items.
687
688 @param filterList list of classes to check against
689 @return list of selected items (list of BroweserItem)
690 """
691 selectedItems = []
692 indexes = self.selectedIndexes()
693 for index in indexes:
694 if index.column() == 0:
695 itm = self.model().item(index)
696 if self.wantedItem(itm, filterList):
697 selectedItems.append(itm)
698 return selectedItems
699
700 def getSelectedItemsCount(self, filterList=None):
701 """
702 Public method to get the count of items selected.
703
704 @param filterList list of classes to check against
705 @return count of items selected (integer)
706 """
707 count = 0
708 indexes = self.selectedIndexes()
709 for index in indexes:
710 if index.column() == 0:
711 itm = self.model().item(index)
712 if self.wantedItem(itm, filterList):
713 count += 1
714 return count
715
716 def getSelectedItemsCountCategorized(self, filterList=None):
717 """
718 Public method to get a categorized count of selected items.
719
720 @param filterList list of classes to check against
721 @return a dictionary containing the counts of items belonging
722 to the individual filter classes. The keys of the dictionary
723 are the string representation of the classes given in the
724 filter (i.e. str(filterClass)). The dictionary contains
725 an additional entry with key "sum", that stores the sum of
726 all selected entries fulfilling the filter criteria.
727 """
728 if filterList is None:
729 filterList = self.selectedItemsFilter
730 categories = {}
731 categories["sum"] = 0
732 for typ in filterList:
733 categories[str(typ)] = 0
734
735 indexes = self.selectedIndexes()
736 for index in indexes:
737 if index.column() == 0:
738 itm = self.model().item(index)
739 for typ in filterList:
740 if isinstance(itm, typ):
741 categories["sum"] += 1
742 categories[str(typ)] += 1
743
744 return categories
745
746 def saveToplevelDirs(self):
747 """
748 Public slot to save the toplevel directories.
749 """
750 self.__model.saveToplevelDirs()
751
752 def __configure(self):
753 """
754 Private method to open the configuration dialog.
755 """
756 if self.__embeddedBrowser == 1:
757 e5App().getObject("UserInterface")\
758 .showPreferences("debuggerGeneralPage")
759 elif self.__embeddedBrowser == 2:
760 e5App().getObject("UserInterface")\
761 .showPreferences("projectBrowserPage")
762
763 def keyboardSearch(self, search):
764 """
765 Public function to search the tree via the keyboard.
766
767 @param search the character entered via the keyboard
768 @type str
769 """
770 if self.model().rowCount() == 0:
771 return
772
773 if self.currentIndex().isValid():
774 startIndex = self.currentIndex()
775 else:
776 startIndex = self.model().index(0, 0)
777
778 keyboardSearchTimeWasValid = self._keyboardSearchTimer.isValid()
779 keyboardSearchTimeElapsed = self._keyboardSearchTimer.restart()
780 if not search or not keyboardSearchTimeWasValid or \
781 keyboardSearchTimeElapsed > \
782 QApplication.keyboardInputInterval():
783 self._keyboardSearchString = search.lower()
784 else:
785 self._keyboardSearchString += search.lower()
786
787 index = startIndex
788 found = False
789 while True:
790 name = self.model().data(index)
791 if name.lower().startswith(self._keyboardSearchString) and \
792 self._keyboardSearchType(self.model().item(index)):
793 found = True
794 break
795
796 index = self.indexBelow(index)
797 if not index.isValid():
798 index = self.model().index(0, 0)
799 if index == startIndex:
800 break
801
802 if found:
803 self.setCurrentIndex(index)
804
805 def _keyboardSearchType(self, item):
806 """
807 Protected method to check, if the item is of the correct type.
808
809 @param item reference to the item
810 @type BrowserItem
811 @return flag indicating a correct type
812 @rtype bool
813 """
814 return isinstance(
815 item, (BrowserDirectoryItem, BrowserFileItem, BrowserSysPathItem))

eric ide

mercurial