7 Module implementing the browser model. |
7 Module implementing the browser model. |
8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
11 import re |
11 import re |
|
12 import contextlib |
12 |
13 |
13 from PyQt5.QtCore import QDir, QModelIndex, pyqtSignal, QFileSystemWatcher, Qt |
14 from PyQt5.QtCore import QDir, QModelIndex, pyqtSignal, QFileSystemWatcher, Qt |
14 from PyQt5.QtGui import QColor |
15 from PyQt5.QtGui import QColor |
15 |
16 |
16 from UI.BrowserModel import ( |
17 from UI.BrowserModel import ( |
35 ProjectBrowserOthersType = 5 |
36 ProjectBrowserOthersType = 5 |
36 ProjectBrowserResourceType = 6 |
37 ProjectBrowserResourceType = 6 |
37 ProjectBrowserProtocolsType = 7 |
38 ProjectBrowserProtocolsType = 7 |
38 |
39 |
39 |
40 |
40 class ProjectBrowserItemMixin(object): |
41 class ProjectBrowserItemMixin: |
41 """ |
42 """ |
42 Class implementing common methods of project browser items. |
43 Class implementing common methods of project browser items. |
43 |
44 |
44 It is meant to be used as a mixin class. |
45 It is meant to be used as a mixin class. |
45 """ |
46 """ |
167 @param other reference to item to compare against (BrowserItem) |
168 @param other reference to item to compare against (BrowserItem) |
168 @param column column number to use for the comparison (integer) |
169 @param column column number to use for the comparison (integer) |
169 @param order sort order (Qt.SortOrder) (for special sorting) |
170 @param order sort order (Qt.SortOrder) (for special sorting) |
170 @return true, if this item is less than other (boolean) |
171 @return true, if this item is less than other (boolean) |
171 """ |
172 """ |
172 if issubclass(other.__class__, BrowserFileItem): |
173 if ( |
173 if Preferences.getUI("BrowsersListFoldersFirst"): |
174 issubclass(other.__class__, BrowserFileItem) and |
174 return order == Qt.SortOrder.AscendingOrder |
175 Preferences.getUI("BrowsersListFoldersFirst") |
|
176 ): |
|
177 return order == Qt.SortOrder.AscendingOrder |
175 |
178 |
176 return BrowserItem.lessThan(self, other, column, order) |
179 return BrowserItem.lessThan(self, other, column, order) |
177 |
180 |
178 |
181 |
179 class ProjectBrowserDirectoryItem(BrowserDirectoryItem, |
182 class ProjectBrowserDirectoryItem(BrowserDirectoryItem, |
231 """ |
234 """ |
232 Constructor |
235 Constructor |
233 |
236 |
234 @param parent reference to parent object (Project.Project) |
237 @param parent reference to parent object (Project.Project) |
235 """ |
238 """ |
236 super(ProjectBrowserModel, self).__init__(parent, nopopulate=True) |
239 super().__init__(parent, nopopulate=True) |
237 |
240 |
238 rootData = self.tr("Name") |
241 rootData = self.tr("Name") |
239 self.rootItem = BrowserItem(None, rootData) |
242 self.rootItem = BrowserItem(None, rootData) |
240 self.rootItem.itemData.append(self.tr("VCS Status")) |
243 self.rootItem.itemData.append(self.tr("VCS Status")) |
241 |
244 |
340 """ |
343 """ |
341 self._addWatchedItem(parentItem) |
344 self._addWatchedItem(parentItem) |
342 |
345 |
343 qdir = QDir(parentItem.dirName()) |
346 qdir = QDir(parentItem.dirName()) |
344 |
347 |
345 if Preferences.getProject("BrowsersListHiddenFiles"): |
348 fileFilter = ( |
346 fileFilter = QDir.Filters( |
349 QDir.Filters( |
347 QDir.Filter.AllEntries | |
350 QDir.Filter.AllEntries | |
348 QDir.Filter.Hidden | |
351 QDir.Filter.Hidden | |
349 QDir.Filter.NoDotAndDotDot |
352 QDir.Filter.NoDotAndDotDot |
350 ) |
353 ) |
351 else: |
354 if Preferences.getProject("BrowsersListHiddenFiles") else |
352 fileFilter = QDir.Filters( |
355 QDir.Filters(QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot) |
353 QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot) |
356 ) |
354 entryInfoList = qdir.entryInfoList(fileFilter) |
357 entryInfoList = qdir.entryInfoList(fileFilter) |
355 |
358 |
356 if len(entryInfoList) > 0: |
359 if len(entryInfoList) > 0: |
357 if repopulate: |
360 if repopulate: |
358 self.beginInsertRows(self.createIndex( |
361 self.beginInsertRows(self.createIndex( |
366 dname = parentItem.dirName() |
369 dname = parentItem.dirName() |
367 self.project.vcs.clearStatusCache() |
370 self.project.vcs.clearStatusCache() |
368 states = self.project.vcs.vcsAllRegisteredStates(states, dname) |
371 states = self.project.vcs.vcsAllRegisteredStates(states, dname) |
369 |
372 |
370 for f in entryInfoList: |
373 for f in entryInfoList: |
371 if f.isDir(): |
374 node = ( |
372 node = ProjectBrowserDirectoryItem( |
375 ProjectBrowserDirectoryItem( |
373 parentItem, |
376 parentItem, |
374 Utilities.toNativeSeparators(f.absoluteFilePath()), |
377 Utilities.toNativeSeparators(f.absoluteFilePath()), |
375 parentItem.getProjectTypes()[0], False) |
378 parentItem.getProjectTypes()[0], False) |
376 else: |
379 if f.isDir() else |
377 node = ProjectBrowserFileItem( |
380 ProjectBrowserFileItem( |
378 parentItem, |
381 parentItem, |
379 Utilities.toNativeSeparators(f.absoluteFilePath()), |
382 Utilities.toNativeSeparators(f.absoluteFilePath()), |
380 parentItem.getProjectTypes()[0]) |
383 parentItem.getProjectTypes()[0]) |
|
384 ) |
381 if self.project.vcs is not None: |
385 if self.project.vcs is not None: |
382 fname = f.absoluteFilePath() |
386 fname = f.absoluteFilePath() |
383 if ( |
387 if ( |
384 states[os.path.normcase(fname)] == |
388 states[os.path.normcase(fname)] == |
385 self.project.vcs.canBeCommitted |
389 self.project.vcs.canBeCommitted |
434 |
438 |
435 self.inRefresh = True |
439 self.inRefresh = True |
436 for key in keys: |
440 for key in keys: |
437 # Show the entry in bold in the others browser to make it more |
441 # Show the entry in bold in the others browser to make it more |
438 # distinguishable |
442 # distinguishable |
439 if key == "OTHERS": |
443 bold = key == "OTHERS" |
440 bold = True |
444 sourceLanguage = ( |
441 else: |
445 self.project.getProjectLanguage() if key == "SOURCES" else "" |
442 bold = False |
446 ) |
443 |
|
444 if key == "SOURCES": |
|
445 sourceLanguage = self.project.getProjectLanguage() |
|
446 else: |
|
447 sourceLanguage = "" |
|
448 |
447 |
449 for fn in self.project.pdata[key]: |
448 for fn in self.project.pdata[key]: |
450 fname = os.path.join(self.project.ppath, fn) |
449 fname = os.path.join(self.project.ppath, fn) |
451 parentItem, dt = self.findParentItemByName( |
450 parentItem, dt = self.findParentItemByName( |
452 self.projectBrowserTypes[key], fn) |
451 self.projectBrowserTypes[key], fn) |
453 if os.path.isdir(fname): |
452 itm = ( |
454 itm = ProjectBrowserDirectoryItem( |
453 ProjectBrowserDirectoryItem( |
455 parentItem, fname, self.projectBrowserTypes[key], |
454 parentItem, fname, self.projectBrowserTypes[key], |
456 False, bold) |
455 False, bold) |
457 else: |
456 if os.path.isdir(fname) else |
458 itm = ProjectBrowserFileItem( |
457 ProjectBrowserFileItem( |
459 parentItem, fname, self.projectBrowserTypes[key], |
458 parentItem, fname, self.projectBrowserTypes[key], |
460 False, bold, sourceLanguage=sourceLanguage) |
459 False, bold, sourceLanguage=sourceLanguage) |
|
460 ) |
461 self._addItem(itm, parentItem) |
461 self._addItem(itm, parentItem) |
462 if self.project.vcs is not None: |
462 if self.project.vcs is not None: |
463 if ( |
463 if ( |
464 states[os.path.normcase(fname)] == |
464 states[os.path.normcase(fname)] == |
465 self.project.vcs.canBeCommitted |
465 self.project.vcs.canBeCommitted |
547 @param name name of the new item (string) |
547 @param name name of the new item (string) |
548 @param additionalTypeStrings names of additional types (list of string) |
548 @param additionalTypeStrings names of additional types (list of string) |
549 """ |
549 """ |
550 # Show the entry in bold in the others browser to make it more |
550 # Show the entry in bold in the others browser to make it more |
551 # distinguishable |
551 # distinguishable |
552 if typeString == "OTHERS": |
552 bold = typeString == "OTHERS" |
553 bold = True |
|
554 else: |
|
555 bold = False |
|
556 |
553 |
557 fname = os.path.join(self.project.ppath, name) |
554 fname = os.path.join(self.project.ppath, name) |
558 parentItem, dt = self.findParentItemByName( |
555 parentItem, dt = self.findParentItemByName( |
559 self.projectBrowserTypes[typeString], name) |
556 self.projectBrowserTypes[typeString], name) |
560 if parentItem == self.rootItem: |
557 parentIndex = ( |
561 parentIndex = QModelIndex() |
558 QModelIndex() |
562 else: |
559 if parentItem == self.rootItem else |
563 parentIndex = self.createIndex(parentItem.row(), 0, parentItem) |
560 self.createIndex(parentItem.row(), 0, parentItem) |
|
561 ) |
564 if os.path.isdir(fname): |
562 if os.path.isdir(fname): |
565 itm = ProjectBrowserDirectoryItem( |
563 itm = ProjectBrowserDirectoryItem( |
566 parentItem, fname, self.projectBrowserTypes[typeString], |
564 parentItem, fname, self.projectBrowserTypes[typeString], |
567 False, bold) |
565 False, bold) |
568 else: |
566 else: |
623 |
621 |
624 @param name name of the item (string) |
622 @param name name of the item (string) |
625 @return index of the item found (QModelIndex) |
623 @return index of the item found (QModelIndex) |
626 """ |
624 """ |
627 itm = self.findItem(name) |
625 itm = self.findItem(name) |
628 if itm is None: |
626 index = self.createIndex(itm.row(), 0, itm) if itm else QModelIndex() |
629 index = QModelIndex() |
|
630 else: |
|
631 index = self.createIndex(itm.row(), 0, itm) |
|
632 return index |
627 return index |
633 |
628 |
634 def itemIndexByNameAndLine(self, name, lineno): |
629 def itemIndexByNameAndLine(self, name, lineno): |
635 """ |
630 """ |
636 Public method to find an item's index given its name. |
631 Public method to find an item's index given its name. |
652 if itm.isLazyPopulated() and autoPopulate: |
647 if itm.isLazyPopulated() and autoPopulate: |
653 self.populateItem(itm) |
648 self.populateItem(itm) |
654 else: |
649 else: |
655 break |
650 break |
656 for child in itm.children(): |
651 for child in itm.children(): |
657 try: |
652 with contextlib.suppress(AttributeError): |
658 start, end = child.boundaries() |
653 start, end = child.boundaries() |
659 if end == -1: |
654 if end == -1: |
660 end = 1000000 # assume end of file |
655 end = 1000000 # assume end of file |
661 if start <= lineno <= end: |
656 if start <= lineno <= end: |
662 itm = child |
657 itm = child |
663 break |
658 break |
664 except AttributeError: |
|
665 pass |
|
666 else: |
659 else: |
667 itm = None |
660 itm = None |
668 if itm: |
661 if itm: |
669 olditem = itm |
662 olditem = itm |
670 index = self.createIndex(olditem.row(), 0, olditem) |
663 index = self.createIndex(olditem.row(), 0, olditem) |
694 |
687 |
695 if path not in self.watchedItems: |
688 if path not in self.watchedItems: |
696 # just ignore the situation we don't have a reference to the item |
689 # just ignore the situation we don't have a reference to the item |
697 return |
690 return |
698 |
691 |
699 if Preferences.getProject("BrowsersListHiddenFiles"): |
692 fileFilter = ( |
700 fileFilter = QDir.Filters( |
693 QDir.Filters( |
701 QDir.Filter.AllEntries | |
694 QDir.Filter.AllEntries | |
702 QDir.Filter.Hidden | |
695 QDir.Filter.Hidden | |
703 QDir.Filter.NoDotAndDotDot |
696 QDir.Filter.NoDotAndDotDot |
704 ) |
697 ) |
705 else: |
698 if Preferences.getProject("BrowsersListHiddenFiles") else |
706 fileFilter = QDir.Filters( |
699 QDir.Filters(QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot) |
707 QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot) |
700 ) |
708 |
701 |
709 for itm in self.watchedItems[path]: |
702 for itm in self.watchedItems[path]: |
710 oldCnt = itm.childCount() |
703 oldCnt = itm.childCount() |
711 |
704 |
712 qdir = QDir(itm.dirName()) |
705 qdir = QDir(itm.dirName()) |
727 continue |
720 continue |
728 |
721 |
729 cnt = itm.childCount() |
722 cnt = itm.childCount() |
730 self.beginInsertRows( |
723 self.beginInsertRows( |
731 self.createIndex(itm.row(), 0, itm), cnt, cnt) |
724 self.createIndex(itm.row(), 0, itm), cnt, cnt) |
732 if f.isDir(): |
725 node = ( |
733 node = ProjectBrowserDirectoryItem( |
726 ProjectBrowserDirectoryItem( |
734 itm, |
727 itm, |
735 Utilities.toNativeSeparators(f.absoluteFilePath()), |
728 Utilities.toNativeSeparators(f.absoluteFilePath()), |
736 itm.getProjectTypes()[0], |
729 itm.getProjectTypes()[0], |
737 False) |
730 False) |
738 else: |
731 if f.isDir() else |
739 node = ProjectBrowserFileItem( |
732 ProjectBrowserFileItem( |
740 itm, |
733 itm, |
741 Utilities.toNativeSeparators(f.absoluteFilePath()), |
734 Utilities.toNativeSeparators(f.absoluteFilePath()), |
742 itm.getProjectTypes()[0]) |
735 itm.getProjectTypes()[0]) |
|
736 ) |
743 self._addItem(node, itm) |
737 self._addItem(node, itm) |
744 if self.project.vcs is not None: |
738 if self.project.vcs is not None: |
745 self.project.vcs.clearStatusCache() |
739 self.project.vcs.clearStatusCache() |
746 state = self.project.vcs.vcsRegisteredState(node.name()) |
740 state = self.project.vcs.vcsRegisteredState(node.name()) |
747 if state == self.project.vcs.canBeCommitted: |
741 if state == self.project.vcs.canBeCommitted: |
829 |
823 |
830 @param name file or directory name of the item (string). |
824 @param name file or directory name of the item (string). |
831 """ |
825 """ |
832 fname = os.path.basename(name) |
826 fname = os.path.basename(name) |
833 parentItem = self.findParentItemByName(0, name)[0] |
827 parentItem = self.findParentItemByName(0, name)[0] |
834 if parentItem == self.rootItem: |
828 parentIndex = ( |
835 parentIndex = QModelIndex() |
829 QModelIndex() |
836 else: |
830 if parentItem == self.rootItem else |
837 parentIndex = self.createIndex(parentItem.row(), 0, parentItem) |
831 self.createIndex(parentItem.row(), 0, parentItem) |
|
832 ) |
838 childItem = self.findChildItem(fname, 0, parentItem) |
833 childItem = self.findChildItem(fname, 0, parentItem) |
839 if childItem is not None: |
834 if childItem is not None: |
840 self.beginRemoveRows(parentIndex, childItem.row(), childItem.row()) |
835 self.beginRemoveRows(parentIndex, childItem.row(), childItem.row()) |
841 parentItem.removeChild(childItem) |
836 parentItem.removeChild(childItem) |
842 self.endRemoveRows() |
837 self.endRemoveRows() |