eric7/Project/ProjectBrowserModel.py

branch
eric7
changeset 8312
800c432b34c8
parent 8259
2bbec88047dd
child 8318
962bce857696
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2006 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the browser model.
8 """
9
10 import os
11 import re
12 import contextlib
13
14 from PyQt5.QtCore import QDir, QModelIndex, pyqtSignal, QFileSystemWatcher, Qt
15 from PyQt5.QtGui import QColor
16
17 from UI.BrowserModel import (
18 BrowserModel, BrowserItem, BrowserDirectoryItem, BrowserFileItem
19 )
20
21 import UI.PixmapCache
22 import Preferences
23 import Utilities
24
25 import Utilities.ModuleParser
26
27 ProjectBrowserItemSimpleDirectory = 100
28 ProjectBrowserItemDirectory = 101
29 ProjectBrowserItemFile = 102
30
31 ProjectBrowserNoType = 0
32 ProjectBrowserSourceType = 1
33 ProjectBrowserFormType = 2
34 ProjectBrowserInterfaceType = 3
35 ProjectBrowserTranslationType = 4
36 ProjectBrowserOthersType = 5
37 ProjectBrowserResourceType = 6
38 ProjectBrowserProtocolsType = 7
39
40
41 class ProjectBrowserItemMixin:
42 """
43 Class implementing common methods of project browser items.
44
45 It is meant to be used as a mixin class.
46 """
47 def __init__(self, type_, bold=False):
48 """
49 Constructor
50
51 @param type_ type of file/directory in the project
52 @param bold flag indicating a highlighted font
53 """
54 self._projectTypes = [type_]
55 self.bold = bold
56 self.vcsState = " "
57
58 def getTextColor(self):
59 """
60 Public method to get the items text color.
61
62 @return text color (QColor)
63 """
64 if self.bold:
65 return Preferences.getProjectBrowserColour("Highlighted")
66 else:
67 return None
68
69 def setVcsState(self, state):
70 """
71 Public method to set the items VCS state.
72
73 @param state VCS state (one of A, C, M, U or " ") (string)
74 """
75 self.vcsState = state
76
77 def addVcsStatus(self, vcsStatus):
78 """
79 Public method to add the VCS status.
80
81 @param vcsStatus VCS status text (string)
82 """
83 self.itemData.append(vcsStatus)
84
85 def setVcsStatus(self, vcsStatus):
86 """
87 Public method to set the VCS status.
88
89 @param vcsStatus VCS status text (string)
90 """
91 self.itemData[1] = vcsStatus
92
93 def getProjectTypes(self):
94 """
95 Public method to get the project type.
96
97 @return project type
98 """
99 return self._projectTypes[:]
100
101 def addProjectType(self, type_):
102 """
103 Public method to add a type to the list.
104
105 @param type_ type to add to the list
106 """
107 self._projectTypes.append(type_)
108
109
110 class ProjectBrowserSimpleDirectoryItem(BrowserItem, ProjectBrowserItemMixin):
111 """
112 Class implementing the data structure for project browser simple directory
113 items.
114 """
115 def __init__(self, parent, projectType, text, path=""):
116 """
117 Constructor
118
119 @param parent parent item
120 @param projectType type of file/directory in the project
121 @param text text to be displayed (string)
122 @param path path of the directory (string)
123 """
124 BrowserItem.__init__(self, parent, text)
125 ProjectBrowserItemMixin.__init__(self, projectType)
126
127 self._dirName = path
128 if not os.path.isdir(self._dirName):
129 self._dirName = os.path.dirname(self._dirName)
130
131 self.type_ = ProjectBrowserItemSimpleDirectory
132 if os.path.lexists(self._dirName) and os.path.islink(self._dirName):
133 self.symlink = True
134 self.icon = UI.PixmapCache.getSymlinkIcon("dirClosed")
135 else:
136 self.icon = UI.PixmapCache.getIcon("dirClosed")
137
138 def setName(self, dinfo, full=True):
139 """
140 Public method to set the directory name.
141
142 @param dinfo dinfo is the string for the directory (string)
143 @param full flag indicating full pathname should be displayed (boolean)
144 """
145 self._dirName = os.path.abspath(dinfo)
146 self.itemData[0] = os.path.basename(self._dirName)
147
148 def dirName(self):
149 """
150 Public method returning the directory name.
151
152 @return directory name (string)
153 """
154 return self._dirName
155
156 def name(self):
157 """
158 Public method to return the name of the item.
159
160 @return name of the item (string)
161 """
162 return self._dirName
163
164 def lessThan(self, other, column, order):
165 """
166 Public method to check, if the item is less than the other one.
167
168 @param other reference to item to compare against (BrowserItem)
169 @param column column number to use for the comparison (integer)
170 @param order sort order (Qt.SortOrder) (for special sorting)
171 @return true, if this item is less than other (boolean)
172 """
173 if (
174 issubclass(other.__class__, BrowserFileItem) and
175 Preferences.getUI("BrowsersListFoldersFirst")
176 ):
177 return order == Qt.SortOrder.AscendingOrder
178
179 return BrowserItem.lessThan(self, other, column, order)
180
181
182 class ProjectBrowserDirectoryItem(BrowserDirectoryItem,
183 ProjectBrowserItemMixin):
184 """
185 Class implementing the data structure for project browser directory items.
186 """
187 def __init__(self, parent, dinfo, projectType, full=True, bold=False):
188 """
189 Constructor
190
191 @param parent parent item
192 @param dinfo dinfo is the string for the directory (string)
193 @param projectType type of file/directory in the project
194 @param full flag indicating full pathname should be displayed (boolean)
195 @param bold flag indicating a highlighted font (boolean)
196 """
197 BrowserDirectoryItem.__init__(self, parent, dinfo, full)
198 ProjectBrowserItemMixin.__init__(self, projectType, bold)
199
200 self.type_ = ProjectBrowserItemDirectory
201
202
203 class ProjectBrowserFileItem(BrowserFileItem, ProjectBrowserItemMixin):
204 """
205 Class implementing the data structure for project browser file items.
206 """
207 def __init__(self, parent, finfo, projectType, full=True, bold=False,
208 sourceLanguage=""):
209 """
210 Constructor
211
212 @param parent parent item
213 @param finfo the string for the file (string)
214 @param projectType type of file/directory in the project
215 @param full flag indicating full pathname should be displayed (boolean)
216 @param bold flag indicating a highlighted font (boolean)
217 @param sourceLanguage source code language of the project (string)
218 """
219 BrowserFileItem.__init__(self, parent, finfo, full, sourceLanguage)
220 ProjectBrowserItemMixin.__init__(self, projectType, bold)
221
222 self.type_ = ProjectBrowserItemFile
223
224
225 class ProjectBrowserModel(BrowserModel):
226 """
227 Class implementing the project browser model.
228
229 @signal vcsStateChanged(str) emitted after the VCS state has changed
230 """
231 vcsStateChanged = pyqtSignal(str)
232
233 def __init__(self, parent):
234 """
235 Constructor
236
237 @param parent reference to parent object (Project.Project)
238 """
239 super().__init__(parent, nopopulate=True)
240
241 rootData = self.tr("Name")
242 self.rootItem = BrowserItem(None, rootData)
243 self.rootItem.itemData.append(self.tr("VCS Status"))
244
245 self.progDir = None
246 self.project = parent
247
248 self.watchedItems = {}
249 self.__watcherActive = True
250 self.watcher = QFileSystemWatcher(self)
251 self.watcher.directoryChanged.connect(self.directoryChanged)
252
253 self.inRefresh = False
254
255 self.projectBrowserTypes = {
256 "SOURCES": ProjectBrowserSourceType,
257 "FORMS": ProjectBrowserFormType,
258 "RESOURCES": ProjectBrowserResourceType,
259 "INTERFACES": ProjectBrowserInterfaceType,
260 "PROTOCOLS": ProjectBrowserProtocolsType,
261 "TRANSLATIONS": ProjectBrowserTranslationType,
262 "OTHERS": ProjectBrowserOthersType,
263 }
264
265 self.colorNames = {
266 "A": "VcsAdded",
267 "M": "VcsModified",
268 "O": "VcsRemoved",
269 "R": "VcsReplaced",
270 "U": "VcsUpdate",
271 "Z": "VcsConflict",
272 }
273 self.itemBackgroundColors = {
274 " ": QColor(),
275 "A": Preferences.getProjectBrowserColour(self.colorNames["A"]),
276 "M": Preferences.getProjectBrowserColour(self.colorNames["M"]),
277 "O": Preferences.getProjectBrowserColour(self.colorNames["O"]),
278 "R": Preferences.getProjectBrowserColour(self.colorNames["R"]),
279 "U": Preferences.getProjectBrowserColour(self.colorNames["U"]),
280 "Z": Preferences.getProjectBrowserColour(self.colorNames["Z"]),
281 }
282
283 self.highLightColor = Preferences.getProjectBrowserColour(
284 "Highlighted")
285 # needed by preferencesChanged()
286
287 self.vcsStatusReport = {}
288
289 def data(self, index, role):
290 """
291 Public method to get data of an item.
292
293 @param index index of the data to retrieve (QModelIndex)
294 @param role role of data (Qt.ItemDataRole)
295 @return requested data
296 """
297 if not index.isValid():
298 return None
299
300 if role == Qt.ItemDataRole.TextColorRole:
301 if index.column() == 0:
302 try:
303 return index.internalPointer().getTextColor()
304 except AttributeError:
305 return None
306 elif role == Qt.ItemDataRole.BackgroundColorRole:
307 try:
308 col = self.itemBackgroundColors[
309 index.internalPointer().vcsState]
310 if col.isValid():
311 return col
312 else:
313 return None
314 except AttributeError:
315 return None
316 except KeyError:
317 return None
318
319 return BrowserModel.data(self, index, role)
320
321 def populateItem(self, parentItem, repopulate=False):
322 """
323 Public method to populate an item's subtree.
324
325 @param parentItem reference to the item to be populated
326 @param repopulate flag indicating a repopulation (boolean)
327 """
328 if parentItem.type() == ProjectBrowserItemSimpleDirectory:
329 return # nothing to do
330 elif parentItem.type() == ProjectBrowserItemDirectory:
331 self.populateProjectDirectoryItem(parentItem, repopulate)
332 elif parentItem.type() == ProjectBrowserItemFile:
333 self.populateFileItem(parentItem, repopulate)
334 else:
335 BrowserModel.populateItem(self, parentItem, repopulate)
336
337 def populateProjectDirectoryItem(self, parentItem, repopulate=False):
338 """
339 Public method to populate a directory item's subtree.
340
341 @param parentItem reference to the directory item to be populated
342 @param repopulate flag indicating a repopulation (boolean)
343 """
344 self._addWatchedItem(parentItem)
345
346 qdir = QDir(parentItem.dirName())
347
348 fileFilter = (
349 QDir.Filters(
350 QDir.Filter.AllEntries |
351 QDir.Filter.Hidden |
352 QDir.Filter.NoDotAndDotDot
353 )
354 if Preferences.getProject("BrowsersListHiddenFiles") else
355 QDir.Filters(QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot)
356 )
357 entryInfoList = qdir.entryInfoList(fileFilter)
358
359 if len(entryInfoList) > 0:
360 if repopulate:
361 self.beginInsertRows(self.createIndex(
362 parentItem.row(), 0, parentItem),
363 0, len(entryInfoList) - 1)
364 states = {}
365 if self.project.vcs is not None:
366 for f in entryInfoList:
367 fname = f.absoluteFilePath()
368 states[os.path.normcase(fname)] = 0
369 dname = parentItem.dirName()
370 self.project.vcs.clearStatusCache()
371 states = self.project.vcs.vcsAllRegisteredStates(states, dname)
372
373 for f in entryInfoList:
374 node = (
375 ProjectBrowserDirectoryItem(
376 parentItem,
377 Utilities.toNativeSeparators(f.absoluteFilePath()),
378 parentItem.getProjectTypes()[0], False)
379 if f.isDir() else
380 ProjectBrowserFileItem(
381 parentItem,
382 Utilities.toNativeSeparators(f.absoluteFilePath()),
383 parentItem.getProjectTypes()[0])
384 )
385 if self.project.vcs is not None:
386 fname = f.absoluteFilePath()
387 if (
388 states[os.path.normcase(fname)] ==
389 self.project.vcs.canBeCommitted
390 ):
391 node.addVcsStatus(self.project.vcs.vcsName())
392 self.project.clearStatusMonitorCachedState(
393 f.absoluteFilePath())
394 else:
395 node.addVcsStatus(self.tr("local"))
396 else:
397 node.addVcsStatus("")
398 self._addItem(node, parentItem)
399 if repopulate:
400 self.endInsertRows()
401
402 def projectClosed(self):
403 """
404 Public method called after a project has been closed.
405 """
406 self.__vcsStatus = {}
407
408 self.watchedItems = {}
409 watchedDirs = self.watcher.directories()
410 if watchedDirs:
411 self.watcher.removePaths(watchedDirs)
412
413 self.rootItem.removeChildren()
414 self.beginResetModel()
415 self.endResetModel()
416
417 # reset the module parser cache
418 Utilities.ModuleParser.resetParsedModules()
419
420 def projectOpened(self):
421 """
422 Public method used to populate the model after a project has been
423 opened.
424 """
425 self.__vcsStatus = {}
426 states = {}
427 keys = list(self.projectBrowserTypes.keys())[:]
428
429 if self.project.vcs is not None:
430 for key in keys:
431 for fn in self.project.pdata[key]:
432 states[os.path.normcase(
433 os.path.join(self.project.ppath, fn))] = 0
434
435 self.project.vcs.clearStatusCache()
436 states = self.project.vcs.vcsAllRegisteredStates(
437 states, self.project.ppath)
438
439 self.inRefresh = True
440 for key in keys:
441 # Show the entry in bold in the others browser to make it more
442 # distinguishable
443 bold = key == "OTHERS"
444 sourceLanguage = (
445 self.project.getProjectLanguage() if key == "SOURCES" else ""
446 )
447
448 for fn in self.project.pdata[key]:
449 fname = os.path.join(self.project.ppath, fn)
450 parentItem, dt = self.findParentItemByName(
451 self.projectBrowserTypes[key], fn)
452 itm = (
453 ProjectBrowserDirectoryItem(
454 parentItem, fname, self.projectBrowserTypes[key],
455 False, bold)
456 if os.path.isdir(fname) else
457 ProjectBrowserFileItem(
458 parentItem, fname, self.projectBrowserTypes[key],
459 False, bold, sourceLanguage=sourceLanguage)
460 )
461 self._addItem(itm, parentItem)
462 if self.project.vcs is not None:
463 if (
464 states[os.path.normcase(fname)] ==
465 self.project.vcs.canBeCommitted
466 ):
467 itm.addVcsStatus(self.project.vcs.vcsName())
468 else:
469 itm.addVcsStatus(self.tr("local"))
470 else:
471 itm.addVcsStatus("")
472 self.inRefresh = False
473 self.beginResetModel()
474 self.endResetModel()
475
476 def findParentItemByName(self, type_, name, dontSplit=False):
477 """
478 Public method to find an item given its name.
479
480 <b>Note</b>: This method creates all necessary parent items, if they
481 don't exist.
482
483 @param type_ type of the item
484 @param name name of the item (string)
485 @param dontSplit flag indicating the name should not be split (boolean)
486 @return reference to the item found and the new display name (string)
487 """
488 if dontSplit:
489 pathlist = []
490 pathlist.append(name)
491 pathlist.append("ignore_me")
492 else:
493 pathlist = re.split(r'/|\\', name)
494
495 if len(pathlist) > 1:
496 olditem = self.rootItem
497 path = self.project.ppath
498 for p in pathlist[:-1]:
499 itm = self.findChildItem(p, 0, olditem)
500 path = os.path.join(path, p)
501 if itm is None:
502 itm = ProjectBrowserSimpleDirectoryItem(
503 olditem, type_, p, path)
504 self.__addVCSStatus(itm, path)
505 if self.inRefresh:
506 self._addItem(itm, olditem)
507 else:
508 if olditem == self.rootItem:
509 oldindex = QModelIndex()
510 else:
511 oldindex = self.createIndex(
512 olditem.row(), 0, olditem)
513 self.addItem(itm, oldindex)
514 else:
515 if type_ and type_ not in itm.getProjectTypes():
516 itm.addProjectType(type_)
517 index = self.createIndex(itm.row(), 0, itm)
518 self.dataChanged.emit(index, index)
519 olditem = itm
520 return (itm, pathlist[-1])
521 else:
522 return (self.rootItem, name)
523
524 def findChildItem(self, text, column, parentItem=None):
525 """
526 Public method to find a child item given some text.
527
528 @param text text to search for (string)
529 @param column column to search in (integer)
530 @param parentItem reference to parent item
531 @return reference to the item found
532 """
533 if parentItem is None:
534 parentItem = self.rootItem
535
536 for itm in parentItem.children():
537 if itm.data(column) == text:
538 return itm
539
540 return None
541
542 def addNewItem(self, typeString, name, additionalTypeStrings=None):
543 """
544 Public method to add a new item to the model.
545
546 @param typeString string denoting the type of the new item (string)
547 @param name name of the new item (string)
548 @param additionalTypeStrings names of additional types (list of string)
549 """
550 # Show the entry in bold in the others browser to make it more
551 # distinguishable
552 bold = typeString == "OTHERS"
553
554 fname = os.path.join(self.project.ppath, name)
555 parentItem, dt = self.findParentItemByName(
556 self.projectBrowserTypes[typeString], name)
557 parentIndex = (
558 QModelIndex()
559 if parentItem == self.rootItem else
560 self.createIndex(parentItem.row(), 0, parentItem)
561 )
562 if os.path.isdir(fname):
563 itm = ProjectBrowserDirectoryItem(
564 parentItem, fname, self.projectBrowserTypes[typeString],
565 False, bold)
566 else:
567 if typeString == "SOURCES":
568 sourceLanguage = self.project.getProjectLanguage()
569 else:
570 sourceLanguage = ""
571 itm = ProjectBrowserFileItem(
572 parentItem, fname, self.projectBrowserTypes[typeString],
573 False, bold, sourceLanguage=sourceLanguage)
574 self.__addVCSStatus(itm, fname)
575 if additionalTypeStrings:
576 for additionalTypeString in additionalTypeStrings:
577 type_ = self.projectBrowserTypes[additionalTypeString]
578 itm.addProjectType(type_)
579 self.addItem(itm, parentIndex)
580
581 def renameItem(self, name, newFilename):
582 """
583 Public method to rename an item.
584
585 @param name the old display name (string)
586 @param newFilename new filename of the item (string)
587 """
588 itm = self.findItem(name)
589 if itm is None:
590 return
591
592 index = self.createIndex(itm.row(), 0, itm)
593 itm.setName(newFilename, full=False)
594 self.dataChanged.emit(index, index)
595 self.repopulateItem(newFilename)
596
597 def findItem(self, name):
598 """
599 Public method to find an item given its name.
600
601 @param name name of the item (string)
602 @return reference to the item found
603 """
604 if QDir.isAbsolutePath(name):
605 name = self.project.getRelativePath(name)
606 pathlist = re.split(r'/|\\', name)
607 if len(pathlist) > 0:
608 olditem = self.rootItem
609 for p in pathlist:
610 itm = self.findChildItem(p, 0, olditem)
611 if itm is None:
612 return None
613 olditem = itm
614 return itm
615 else:
616 return None
617
618 def itemIndexByName(self, name):
619 """
620 Public method to find an item's index given its name.
621
622 @param name name of the item (string)
623 @return index of the item found (QModelIndex)
624 """
625 itm = self.findItem(name)
626 index = self.createIndex(itm.row(), 0, itm) if itm else QModelIndex()
627 return index
628
629 def itemIndexByNameAndLine(self, name, lineno):
630 """
631 Public method to find an item's index given its name.
632
633 @param name name of the item (string)
634 @param lineno one based line number of the item (integer)
635 @return index of the item found (QModelIndex)
636 """
637 index = QModelIndex()
638 itm = self.findItem(name)
639 if (
640 itm is not None and
641 isinstance(itm, ProjectBrowserFileItem)
642 ):
643 olditem = itm
644 autoPopulate = Preferences.getProject("AutoPopulateItems")
645 while itm is not None:
646 if not itm.isPopulated():
647 if itm.isLazyPopulated() and autoPopulate:
648 self.populateItem(itm)
649 else:
650 break
651 for child in itm.children():
652 with contextlib.suppress(AttributeError):
653 start, end = child.boundaries()
654 if end == -1:
655 end = 1000000 # assume end of file
656 if start <= lineno <= end:
657 itm = child
658 break
659 else:
660 itm = None
661 if itm:
662 olditem = itm
663 index = self.createIndex(olditem.row(), 0, olditem)
664
665 return index
666
667 def startFileSystemMonitoring(self):
668 """
669 Public method to (re)start monitoring the project file system.
670 """
671 self.__watcherActive = True
672
673 def stopFileSystemMonitoring(self):
674 """
675 Public method to stop monitoring the project file system.
676 """
677 self.__watcherActive = False
678
679 def directoryChanged(self, path):
680 """
681 Public slot to handle the directoryChanged signal of the watcher.
682
683 @param path path of the directory (string)
684 """
685 if not self.__watcherActive:
686 return
687
688 if path not in self.watchedItems:
689 # just ignore the situation we don't have a reference to the item
690 return
691
692 fileFilter = (
693 QDir.Filters(
694 QDir.Filter.AllEntries |
695 QDir.Filter.Hidden |
696 QDir.Filter.NoDotAndDotDot
697 )
698 if Preferences.getProject("BrowsersListHiddenFiles") else
699 QDir.Filters(QDir.Filter.AllEntries | QDir.Filter.NoDotAndDotDot)
700 )
701
702 for itm in self.watchedItems[path]:
703 oldCnt = itm.childCount()
704
705 qdir = QDir(itm.dirName())
706
707 entryInfoList = qdir.entryInfoList(fileFilter)
708
709 # step 1: check for new entries
710 children = itm.children()
711 for f in entryInfoList:
712 fpath = Utilities.toNativeSeparators(f.absoluteFilePath())
713 childFound = False
714 for child in children:
715 if child.name() == fpath:
716 childFound = True
717 children.remove(child)
718 break
719 if childFound:
720 continue
721
722 cnt = itm.childCount()
723 self.beginInsertRows(
724 self.createIndex(itm.row(), 0, itm), cnt, cnt)
725 node = (
726 ProjectBrowserDirectoryItem(
727 itm,
728 Utilities.toNativeSeparators(f.absoluteFilePath()),
729 itm.getProjectTypes()[0],
730 False)
731 if f.isDir() else
732 ProjectBrowserFileItem(
733 itm,
734 Utilities.toNativeSeparators(f.absoluteFilePath()),
735 itm.getProjectTypes()[0])
736 )
737 self._addItem(node, itm)
738 if self.project.vcs is not None:
739 self.project.vcs.clearStatusCache()
740 state = self.project.vcs.vcsRegisteredState(node.name())
741 if state == self.project.vcs.canBeCommitted:
742 node.addVcsStatus(self.project.vcs.vcsName())
743 else:
744 node.addVcsStatus(self.tr("local"))
745 self.endInsertRows()
746
747 # step 2: check for removed entries
748 if len(entryInfoList) != itm.childCount():
749 for row in range(oldCnt - 1, -1, -1):
750 child = itm.child(row)
751 childname = Utilities.fromNativeSeparators(child.name())
752 entryFound = False
753 for f in entryInfoList:
754 if f.absoluteFilePath() == childname:
755 entryFound = True
756 entryInfoList.remove(f)
757 break
758 if entryFound:
759 continue
760
761 self._removeWatchedItem(child)
762 self.beginRemoveRows(
763 self.createIndex(itm.row(), 0, itm), row, row)
764 itm.removeChild(child)
765 self.endRemoveRows()
766
767 def __addVCSStatus(self, item, name):
768 """
769 Private method used to set the vcs status of a node.
770
771 @param item item to work on
772 @param name filename belonging to this item (string)
773 """
774 if self.project.vcs is not None:
775 state = self.project.vcs.vcsRegisteredState(name)
776 if state == self.project.vcs.canBeCommitted:
777 item.addVcsStatus(self.project.vcs.vcsName())
778 else:
779 item.addVcsStatus(self.tr("local"))
780 else:
781 item.addVcsStatus("")
782
783 def __updateVCSStatus(self, item, name, recursive=True):
784 """
785 Private method used to update the vcs status of a node.
786
787 @param item item to work on
788 @param name filename belonging to this item (string)
789 @param recursive flag indicating a recursive update (boolean)
790 """
791 if self.project.vcs is not None:
792 self.project.vcs.clearStatusCache()
793 state = self.project.vcs.vcsRegisteredState(name)
794 if state == self.project.vcs.canBeCommitted:
795 item.setVcsStatus(self.project.vcs.vcsName())
796 else:
797 item.setVcsStatus(self.tr("local"))
798 if recursive:
799 name = os.path.dirname(name)
800 parentItem = item.parent()
801 if name and parentItem is not self.rootItem:
802 self.__updateVCSStatus(parentItem, name, recursive)
803 else:
804 item.setVcsStatus("")
805
806 index = self.createIndex(item.row(), 0, item)
807 self.dataChanged.emit(index, index)
808
809 def updateVCSStatus(self, name, recursive=True):
810 """
811 Public method used to update the vcs status of a node.
812
813 @param name filename belonging to this item (string)
814 @param recursive flag indicating a recursive update (boolean)
815 """
816 item = self.findItem(name)
817 if item:
818 self.__updateVCSStatus(item, name, recursive)
819
820 def removeItem(self, name):
821 """
822 Public method to remove a named item.
823
824 @param name file or directory name of the item (string).
825 """
826 fname = os.path.basename(name)
827 parentItem = self.findParentItemByName(0, name)[0]
828 parentIndex = (
829 QModelIndex()
830 if parentItem == self.rootItem else
831 self.createIndex(parentItem.row(), 0, parentItem)
832 )
833 childItem = self.findChildItem(fname, 0, parentItem)
834 if childItem is not None:
835 self.beginRemoveRows(parentIndex, childItem.row(), childItem.row())
836 parentItem.removeChild(childItem)
837 self.endRemoveRows()
838
839 def repopulateItem(self, name):
840 """
841 Public method to repopulate an item.
842
843 @param name name of the file relative to the project root (string)
844 """
845 itm = self.findItem(name)
846 if itm is None:
847 return
848
849 if itm.isLazyPopulated():
850 if not itm.isPopulated():
851 # item is not populated yet, nothing to do
852 return
853
854 if itm.childCount():
855 index = self.createIndex(itm.row(), 0, itm)
856 self.beginRemoveRows(index, 0, itm.childCount() - 1)
857 itm.removeChildren()
858 self.endRemoveRows()
859 Utilities.ModuleParser.resetParsedModule(
860 os.path.join(self.project.ppath, name))
861
862 self.populateItem(itm, True)
863
864 def projectPropertiesChanged(self):
865 """
866 Public method to react on a change of the project properties.
867 """
868 # nothing to do for now
869 return
870
871 def changeVCSStates(self, statesList):
872 """
873 Public slot to record the (non normal) VCS states.
874
875 @param statesList list of VCS state entries (list of strings) giving
876 the states in the first column and the path relative to the project
877 directory starting with the third column. The allowed status flags
878 are:
879 <ul>
880 <li>"A" path was added but not yet comitted</li>
881 <li>"M" path has local changes</li>
882 <li>"O" path was removed</li>
883 <li>"R" path was deleted and then re-added</li>
884 <li>"U" path needs an update</li>
885 <li>"Z" path contains a conflict</li>
886 <li>" " path is back at normal</li>
887 </ul>
888 """
889 statesList.sort()
890 lastHead = ""
891 itemCache = {}
892 if len(statesList) == 1 and statesList[0] == '--RESET--':
893 statesList = []
894 for name in list(self.__vcsStatus.keys()):
895 statesList.append(" {0}".format(name))
896
897 for name in statesList:
898 state = name[0]
899 name = name[1:].strip()
900 if state == ' ':
901 if name in self.__vcsStatus:
902 del self.__vcsStatus[name]
903 else:
904 self.__vcsStatus[name] = state
905
906 try:
907 itm = itemCache[name]
908 except KeyError:
909 itm = self.findItem(name)
910 if itm:
911 itemCache[name] = itm
912 if itm:
913 itm.setVcsState(state)
914 itm.setVcsStatus(self.project.vcs.vcsName())
915 index1 = self.createIndex(itm.row(), 0, itm)
916 index2 = self.createIndex(
917 itm.row(), self.rootItem.columnCount(), itm)
918 self.dataChanged.emit(index1, index2)
919
920 head, tail = os.path.split(name)
921 if head != lastHead:
922 if lastHead:
923 self.__changeParentsVCSState(lastHead, itemCache)
924 lastHead = head
925 if lastHead:
926 self.__changeParentsVCSState(lastHead, itemCache)
927 try:
928 globalVcsStatus = sorted(self.__vcsStatus.values())[-1]
929 except IndexError:
930 globalVcsStatus = ' '
931 self.vcsStateChanged.emit(globalVcsStatus)
932
933 def __changeParentsVCSState(self, path, itemCache):
934 """
935 Private method to recursively change the parents VCS state.
936
937 @param path pathname of parent item (string)
938 @param itemCache reference to the item cache used to store
939 references to named items
940 """
941 while path:
942 try:
943 itm = itemCache[path]
944 except KeyError:
945 itm = self.findItem(path)
946 if itm:
947 itemCache[path] = itm
948 if itm:
949 state = " "
950 for id_ in itm.children():
951 if state < id_.vcsState:
952 state = id_.vcsState
953 if state != itm.vcsState:
954 itm.setVcsState(state)
955 index1 = self.createIndex(itm.row(), 0, itm)
956 index2 = self.createIndex(
957 itm.row(), self.rootItem.columnCount(), itm)
958 self.dataChanged.emit(index1, index2)
959 path, tail = os.path.split(path)
960
961 def preferencesChanged(self):
962 """
963 Public method used to handle a change in preferences.
964 """
965 for code in list(self.colorNames.keys()):
966 color = Preferences.getProjectBrowserColour(self.colorNames[code])
967 if color.name() == self.itemBackgroundColors[code].name():
968 continue
969
970 self.itemBackgroundColors[code] = color
971
972 color = Preferences.getProjectBrowserColour("Highlighted")
973 if self.highLightColor.name() != color.name():
974 self.highLightColor = color

eric ide

mercurial