10 |
10 |
11 import os |
11 import os |
12 import fnmatch |
12 import fnmatch |
13 import json |
13 import json |
14 |
14 |
15 from PyQt5.QtCore import QDir, QModelIndex, QAbstractItemModel, \ |
15 from PyQt5.QtCore import ( |
16 QFileSystemWatcher, Qt, QProcess, QCoreApplication |
16 QDir, QModelIndex, QAbstractItemModel, QFileSystemWatcher, Qt, QProcess, |
|
17 QCoreApplication |
|
18 ) |
17 from PyQt5.QtGui import QImageReader, QFont |
19 from PyQt5.QtGui import QImageReader, QFont |
18 from PyQt5.QtWidgets import QApplication |
20 from PyQt5.QtWidgets import QApplication |
19 |
21 |
20 import UI.PixmapCache |
22 import UI.PixmapCache |
21 import Preferences |
23 import Preferences |
92 |
94 |
93 if role == Qt.DisplayRole: |
95 if role == Qt.DisplayRole: |
94 item = index.internalPointer() |
96 item = index.internalPointer() |
95 if index.column() < item.columnCount(): |
97 if index.column() < item.columnCount(): |
96 return item.data(index.column()) |
98 return item.data(index.column()) |
97 elif index.column() == item.columnCount() and \ |
99 elif ( |
98 index.column() < self.columnCount(self.parent(index)): |
100 index.column() == item.columnCount() and |
|
101 index.column() < self.columnCount(self.parent(index)) |
|
102 ): |
99 # This is for the case where an item under a multi-column |
103 # This is for the case where an item under a multi-column |
100 # parent doesn't have a value for all the columns |
104 # parent doesn't have a value for all the columns |
101 return "" |
105 return "" |
102 elif role == Qt.DecorationRole: |
106 elif role == Qt.DecorationRole: |
103 if index.column() == 0: |
107 if index.column() == 0: |
153 parent = QModelIndex() |
157 parent = QModelIndex() |
154 |
158 |
155 # The model/view framework considers negative values out-of-bounds, |
159 # The model/view framework considers negative values out-of-bounds, |
156 # however in python they work when indexing into lists. So make sure |
160 # however in python they work when indexing into lists. So make sure |
157 # we return an invalid index for out-of-bounds row/col |
161 # we return an invalid index for out-of-bounds row/col |
158 if row < 0 or column < 0 or \ |
162 if ( |
159 row >= self.rowCount(parent) or column >= self.columnCount(parent): |
163 row < 0 or |
|
164 column < 0 or |
|
165 row >= self.rowCount(parent) or |
|
166 column >= self.columnCount(parent) |
|
167 ): |
160 return QModelIndex() |
168 return QModelIndex() |
161 |
169 |
162 if not parent.isValid(): |
170 if not parent.isValid(): |
163 parentItem = self.rootItem |
171 parentItem = self.rootItem |
164 else: |
172 else: |
267 |
275 |
268 @param itm item to be watched (BrowserDirectoryItem) |
276 @param itm item to be watched (BrowserDirectoryItem) |
269 """ |
277 """ |
270 if isinstance(itm, BrowserDirectoryItem): |
278 if isinstance(itm, BrowserDirectoryItem): |
271 dirName = itm.dirName() |
279 dirName = itm.dirName() |
272 if dirName != "" and \ |
280 if ( |
273 not dirName.startswith("//") and \ |
281 dirName != "" and |
274 not dirName.startswith("\\\\"): |
282 not dirName.startswith("//") and |
|
283 not dirName.startswith("\\\\") |
|
284 ): |
275 if dirName not in self.watcher.directories(): |
285 if dirName not in self.watcher.directories(): |
276 self.watcher.addPath(dirName) |
286 self.watcher.addPath(dirName) |
277 if dirName in self.watchedItems: |
287 if dirName in self.watchedItems: |
278 if itm not in self.watchedItems[dirName]: |
288 if itm not in self.watchedItems[dirName]: |
279 self.watchedItems[dirName].append(itm) |
289 self.watchedItems[dirName].append(itm) |
557 node = BrowserDirectoryItem( |
567 node = BrowserDirectoryItem( |
558 parentItem, |
568 parentItem, |
559 Utilities.toNativeSeparators(f.absoluteFilePath()), |
569 Utilities.toNativeSeparators(f.absoluteFilePath()), |
560 False) |
570 False) |
561 else: |
571 else: |
562 fileFilters = \ |
572 fileFilters = Preferences.getUI( |
563 Preferences.getUI("BrowsersFileFilters").split(";") |
573 "BrowsersFileFilters").split(";") |
564 if fileFilters: |
574 if fileFilters: |
565 fn = f.fileName() |
575 fn = f.fileName() |
566 if any(fnmatch.fnmatch(fn, ff.strip()) |
576 if any(fnmatch.fnmatch(fn, ff.strip()) |
567 for ff in fileFilters): |
577 for ff in fileFilters): |
568 continue |
578 continue |
656 node = BrowserImportsItem( |
666 node = BrowserImportsItem( |
657 parentItem, |
667 parentItem, |
658 QCoreApplication.translate("BrowserModel", "Imports")) |
668 QCoreApplication.translate("BrowserModel", "Imports")) |
659 self._addItem(node, parentItem) |
669 self._addItem(node, parentItem) |
660 if "@@Import@@" in keys: |
670 if "@@Import@@" in keys: |
661 for importedModule in \ |
671 for importedModule in ( |
662 dictionary["@@Import@@"].getImports().values(): |
672 dictionary["@@Import@@"].getImports().values() |
|
673 ): |
663 m_node = BrowserImportItem( |
674 m_node = BrowserImportItem( |
664 node, |
675 node, |
665 importedModule.importedModuleName, |
676 importedModule.importedModuleName, |
666 importedModule.file, |
677 importedModule.file, |
667 importedModule.linenos) |
678 importedModule.linenos) |
668 self._addItem(m_node, node) |
679 self._addItem(m_node, node) |
669 for importedName, linenos in \ |
680 for importedName, linenos in ( |
670 importedModule.importedNames.items(): |
681 importedModule.importedNames.items() |
|
682 ): |
671 mn_node = BrowserImportItem( |
683 mn_node = BrowserImportItem( |
672 m_node, |
684 m_node, |
673 importedName, |
685 importedName, |
674 importedModule.file, |
686 importedModule.file, |
675 linenos, |
687 linenos, |
985 dn = os.path.basename(self._dirName) |
997 dn = os.path.basename(self._dirName) |
986 |
998 |
987 BrowserItem.__init__(self, parent, dn) |
999 BrowserItem.__init__(self, parent, dn) |
988 |
1000 |
989 self.type_ = BrowserItemDirectory |
1001 self.type_ = BrowserItemDirectory |
990 if not Utilities.isDrive(self._dirName) and \ |
1002 if ( |
991 os.path.lexists(self._dirName) and os.path.islink(self._dirName): |
1003 not Utilities.isDrive(self._dirName) and |
|
1004 os.path.lexists(self._dirName) and |
|
1005 os.path.islink(self._dirName) |
|
1006 ): |
992 self.symlink = True |
1007 self.symlink = True |
993 self.icon = UI.PixmapCache.getSymlinkIcon("dirClosed.png") |
1008 self.icon = UI.PixmapCache.getSymlinkIcon("dirClosed.png") |
994 else: |
1009 else: |
995 self.icon = UI.PixmapCache.getIcon("dirClosed.png") |
1010 self.icon = UI.PixmapCache.getIcon("dirClosed.png") |
996 self._populated = False |
1011 self._populated = False |
1161 @param finfo the string for the file (string) |
1176 @param finfo the string for the file (string) |
1162 @param full flag indicating full pathname should be displayed (boolean) |
1177 @param full flag indicating full pathname should be displayed (boolean) |
1163 """ |
1178 """ |
1164 self._filename = os.path.abspath(finfo) |
1179 self._filename = os.path.abspath(finfo) |
1165 self.itemData[0] = os.path.basename(finfo) |
1180 self.itemData[0] = os.path.basename(finfo) |
1166 if self.isPython2File() or self.isPython3File() or \ |
1181 if ( |
1167 self.isRubyFile() or self.isIdlFile() or \ |
1182 self.isPython2File() or |
1168 self.isProtobufFile(): |
1183 self.isPython3File() or |
|
1184 self.isRubyFile() or |
|
1185 self.isIdlFile() or |
|
1186 self.isProtobufFile() |
|
1187 ): |
1169 self._dirName = os.path.dirname(finfo) |
1188 self._dirName = os.path.dirname(finfo) |
1170 self._moduleName = os.path.basename(finfo) |
1189 self._moduleName = os.path.basename(finfo) |
1171 |
1190 |
1172 def fileName(self): |
1191 def fileName(self): |
1173 """ |
1192 """ |
1213 """ |
1232 """ |
1214 Public method to check, if this file is a Python script. |
1233 Public method to check, if this file is a Python script. |
1215 |
1234 |
1216 @return flag indicating a Python file (boolean) |
1235 @return flag indicating a Python file (boolean) |
1217 """ |
1236 """ |
1218 return self.fileext in Preferences.getPython("PythonExtensions") or \ |
1237 return ( |
|
1238 self.fileext in Preferences.getPython("PythonExtensions") or |
1219 (self.fileext == "" and |
1239 (self.fileext == "" and |
1220 self.sourceLanguage in ["Python", "Python2"]) |
1240 self.sourceLanguage in ["Python", "Python2"]) |
|
1241 ) |
1221 |
1242 |
1222 def isPython3File(self): |
1243 def isPython3File(self): |
1223 """ |
1244 """ |
1224 Public method to check, if this file is a Python3 script. |
1245 Public method to check, if this file is a Python3 script. |
1225 |
1246 |
1226 @return flag indicating a Python file (boolean) |
1247 @return flag indicating a Python file (boolean) |
1227 """ |
1248 """ |
1228 return self.fileext in Preferences.getPython("Python3Extensions") or \ |
1249 return ( |
|
1250 self.fileext in Preferences.getPython("Python3Extensions") or |
1229 (self.fileext == "" and self.sourceLanguage == "Python3") |
1251 (self.fileext == "" and self.sourceLanguage == "Python3") |
|
1252 ) |
1230 |
1253 |
1231 def isRubyFile(self): |
1254 def isRubyFile(self): |
1232 """ |
1255 """ |
1233 Public method to check, if this file is a Ruby script. |
1256 Public method to check, if this file is a Ruby script. |
1234 |
1257 |
1235 @return flag indicating a Ruby file (boolean) |
1258 @return flag indicating a Ruby file (boolean) |
1236 """ |
1259 """ |
1237 return self.fileext == '.rb' or \ |
1260 return ( |
|
1261 self.fileext == '.rb' or |
1238 (self.fileext == "" and self.sourceLanguage == "Ruby") |
1262 (self.fileext == "" and self.sourceLanguage == "Ruby") |
|
1263 ) |
1239 |
1264 |
1240 def isDesignerFile(self): |
1265 def isDesignerFile(self): |
1241 """ |
1266 """ |
1242 Public method to check, if this file is a Qt-Designer file. |
1267 Public method to check, if this file is a Qt-Designer file. |
1243 |
1268 |
1322 """ |
1347 """ |
1323 Public method to check, if this file is a D file. |
1348 Public method to check, if this file is a D file. |
1324 |
1349 |
1325 @return flag indicating a D file (boolean) |
1350 @return flag indicating a D file (boolean) |
1326 """ |
1351 """ |
1327 return self.fileext in ['.d', '.di'] or \ |
1352 return ( |
|
1353 self.fileext in ['.d', '.di'] or |
1328 (self.fileext == "" and self.sourceLanguage == "D") |
1354 (self.fileext == "" and self.sourceLanguage == "D") |
|
1355 ) |
1329 |
1356 |
1330 def lessThan(self, other, column, order): |
1357 def lessThan(self, other, column, order): |
1331 """ |
1358 """ |
1332 Public method to check, if the item is less than the other one. |
1359 Public method to check, if the item is less than the other one. |
1333 |
1360 |
1340 if Preferences.getUI("BrowsersListFoldersFirst"): |
1367 if Preferences.getUI("BrowsersListFoldersFirst"): |
1341 return order == Qt.DescendingOrder |
1368 return order == Qt.DescendingOrder |
1342 |
1369 |
1343 if issubclass(other.__class__, BrowserFileItem): |
1370 if issubclass(other.__class__, BrowserFileItem): |
1344 sinit = os.path.basename(self._filename).startswith('__init__.py') |
1371 sinit = os.path.basename(self._filename).startswith('__init__.py') |
1345 oinit = \ |
1372 oinit = os.path.basename(other.fileName()).startswith( |
1346 os.path.basename(other.fileName()).startswith('__init__.py') |
1373 '__init__.py') |
1347 if sinit and not oinit: |
1374 if sinit and not oinit: |
1348 return order == Qt.AscendingOrder |
1375 return order == Qt.AscendingOrder |
1349 if not sinit and oinit: |
1376 if not sinit and oinit: |
1350 return order == Qt.DescendingOrder |
1377 return order == Qt.DescendingOrder |
1351 |
1378 |
1419 self.icon = UI.PixmapCache.getIcon("class_private.png") |
1446 self.icon = UI.PixmapCache.getIcon("class_private.png") |
1420 elif cl.isProtected(): |
1447 elif cl.isProtected(): |
1421 self.icon = UI.PixmapCache.getIcon("class_protected.png") |
1448 self.icon = UI.PixmapCache.getIcon("class_protected.png") |
1422 else: |
1449 else: |
1423 self.icon = UI.PixmapCache.getIcon("class.png") |
1450 self.icon = UI.PixmapCache.getIcon("class.png") |
1424 if self._classObject and \ |
1451 if ( |
1425 (self._classObject.methods or |
1452 self._classObject and |
1426 self._classObject.classes or |
1453 (self._classObject.methods or |
1427 self._classObject.attributes): |
1454 self._classObject.classes or |
|
1455 self._classObject.attributes) |
|
1456 ): |
1428 self._populated = False |
1457 self._populated = False |
1429 self._lazyPopulation = True |
1458 self._lazyPopulation = True |
1430 |
1459 |
1431 def name(self): |
1460 def name(self): |
1432 """ |
1461 """ |
1475 @param other reference to item to compare against (BrowserItem) |
1504 @param other reference to item to compare against (BrowserItem) |
1476 @param column column number to use for the comparison (integer) |
1505 @param column column number to use for the comparison (integer) |
1477 @param order sort order (Qt.SortOrder) (for special sorting) |
1506 @param order sort order (Qt.SortOrder) (for special sorting) |
1478 @return true, if this item is less than other (boolean) |
1507 @return true, if this item is less than other (boolean) |
1479 """ |
1508 """ |
1480 if issubclass(other.__class__, BrowserCodingItem) or \ |
1509 if issubclass( |
1481 issubclass(other.__class__, BrowserClassAttributesItem): |
1510 other.__class__, |
|
1511 (BrowserCodingItem, BrowserClassAttributesItem) |
|
1512 ): |
1482 return order == Qt.DescendingOrder |
1513 return order == Qt.DescendingOrder |
1483 |
1514 |
1484 if Preferences.getUI("BrowsersListContentsByOccurrence") and \ |
1515 if ( |
1485 column == 0: |
1516 Preferences.getUI("BrowsersListContentsByOccurrence") and |
|
1517 column == 0 |
|
1518 ): |
1486 if order == Qt.AscendingOrder: |
1519 if order == Qt.AscendingOrder: |
1487 return self.lineno() < other.lineno() |
1520 return self.lineno() < other.lineno() |
1488 else: |
1521 else: |
1489 return self.lineno() > other.lineno() |
1522 return self.lineno() > other.lineno() |
1490 |
1523 |
1517 import Utilities.ClassBrowsers.ClbrBaseClasses |
1550 import Utilities.ClassBrowsers.ClbrBaseClasses |
1518 self.type_ = BrowserItemMethod |
1551 self.type_ = BrowserItemMethod |
1519 self._name = name |
1552 self._name = name |
1520 self._functionObject = fn |
1553 self._functionObject = fn |
1521 self._filename = filename |
1554 self._filename = filename |
1522 if self._functionObject.modifier == \ |
1555 if ( |
1523 Utilities.ClassBrowsers.ClbrBaseClasses.Function.Static: |
1556 self._functionObject.modifier == |
|
1557 Utilities.ClassBrowsers.ClbrBaseClasses.Function.Static |
|
1558 ): |
1524 self.icon = UI.PixmapCache.getIcon("method_static.png") |
1559 self.icon = UI.PixmapCache.getIcon("method_static.png") |
1525 elif self._functionObject.modifier == \ |
1560 elif ( |
1526 Utilities.ClassBrowsers.ClbrBaseClasses.Function.Class: |
1561 self._functionObject.modifier == |
|
1562 Utilities.ClassBrowsers.ClbrBaseClasses.Function.Class |
|
1563 ): |
1527 self.icon = UI.PixmapCache.getIcon("method_class.png") |
1564 self.icon = UI.PixmapCache.getIcon("method_class.png") |
1528 elif self._functionObject.isPrivate(): |
1565 elif self._functionObject.isPrivate(): |
1529 self.icon = UI.PixmapCache.getIcon("method_private.png") |
1566 self.icon = UI.PixmapCache.getIcon("method_private.png") |
1530 elif self._functionObject.isProtected(): |
1567 elif self._functionObject.isProtected(): |
1531 self.icon = UI.PixmapCache.getIcon("method_protected.png") |
1568 self.icon = UI.PixmapCache.getIcon("method_protected.png") |
1538 self.itemData[0], self._functionObject.annotation) |
1575 self.itemData[0], self._functionObject.annotation) |
1539 # if no defaults are wanted |
1576 # if no defaults are wanted |
1540 # ....format(name, |
1577 # ....format(name, |
1541 # ", ".join([e.split('=')[0].strip() |
1578 # ", ".join([e.split('=')[0].strip() |
1542 # for e in self._functionObject.parameters])) |
1579 # for e in self._functionObject.parameters])) |
1543 if self._functionObject and \ |
1580 if ( |
1544 (self._functionObject.methods or self._functionObject.classes): |
1581 self._functionObject and |
|
1582 (self._functionObject.methods or self._functionObject.classes) |
|
1583 ): |
1545 self._populated = False |
1584 self._populated = False |
1546 self._lazyPopulation = True |
1585 self._lazyPopulation = True |
1547 |
1586 |
1548 def name(self): |
1587 def name(self): |
1549 """ |
1588 """ |
1600 if other._name.startswith('__init__'): |
1639 if other._name.startswith('__init__'): |
1601 return order == Qt.DescendingOrder |
1640 return order == Qt.DescendingOrder |
1602 elif issubclass(other.__class__, BrowserClassAttributesItem): |
1641 elif issubclass(other.__class__, BrowserClassAttributesItem): |
1603 return order == Qt.DescendingOrder |
1642 return order == Qt.DescendingOrder |
1604 |
1643 |
1605 if Preferences.getUI("BrowsersListContentsByOccurrence") and \ |
1644 if ( |
1606 column == 0: |
1645 Preferences.getUI("BrowsersListContentsByOccurrence") and |
|
1646 column == 0 |
|
1647 ): |
1607 if order == Qt.AscendingOrder: |
1648 if order == Qt.AscendingOrder: |
1608 return self.lineno() < other.lineno() |
1649 return self.lineno() < other.lineno() |
1609 else: |
1650 else: |
1610 return self.lineno() > other.lineno() |
1651 return self.lineno() > other.lineno() |
1611 |
1652 |
1678 @param order sort order (Qt.SortOrder) (for special sorting) |
1719 @param order sort order (Qt.SortOrder) (for special sorting) |
1679 @return true, if this item is less than other (boolean) |
1720 @return true, if this item is less than other (boolean) |
1680 """ |
1721 """ |
1681 if issubclass(other.__class__, BrowserCodingItem): |
1722 if issubclass(other.__class__, BrowserCodingItem): |
1682 return order == Qt.DescendingOrder |
1723 return order == Qt.DescendingOrder |
1683 elif issubclass(other.__class__, BrowserClassItem) or \ |
1724 elif issubclass( |
1684 issubclass(other.__class__, BrowserMethodItem): |
1725 other.__class__, |
|
1726 (BrowserClassItem, BrowserMethodItem) |
|
1727 ): |
1685 return order == Qt.AscendingOrder |
1728 return order == Qt.AscendingOrder |
1686 |
1729 |
1687 return BrowserItem.lessThan(self, other, column, order) |
1730 return BrowserItem.lessThan(self, other, column, order) |
1688 |
1731 |
1689 |
1732 |
1760 @param other reference to item to compare against (BrowserItem) |
1803 @param other reference to item to compare against (BrowserItem) |
1761 @param column column number to use for the comparison (integer) |
1804 @param column column number to use for the comparison (integer) |
1762 @param order sort order (Qt.SortOrder) (for special sorting) |
1805 @param order sort order (Qt.SortOrder) (for special sorting) |
1763 @return true, if this item is less than other (boolean) |
1806 @return true, if this item is less than other (boolean) |
1764 """ |
1807 """ |
1765 if Preferences.getUI("BrowsersListContentsByOccurrence") and \ |
1808 if ( |
1766 column == 0: |
1809 Preferences.getUI("BrowsersListContentsByOccurrence") and |
|
1810 column == 0 |
|
1811 ): |
1767 if order == Qt.AscendingOrder: |
1812 if order == Qt.AscendingOrder: |
1768 return self.lineno() < other.lineno() |
1813 return self.lineno() < other.lineno() |
1769 else: |
1814 else: |
1770 return self.lineno() > other.lineno() |
1815 return self.lineno() > other.lineno() |
1771 |
1816 |
1810 @param other reference to item to compare against (BrowserItem) |
1855 @param other reference to item to compare against (BrowserItem) |
1811 @param column column number to use for the comparison (integer) |
1856 @param column column number to use for the comparison (integer) |
1812 @param order sort order (Qt.SortOrder) (for special sorting) |
1857 @param order sort order (Qt.SortOrder) (for special sorting) |
1813 @return true, if this item is less than other (boolean) |
1858 @return true, if this item is less than other (boolean) |
1814 """ |
1859 """ |
1815 if issubclass(other.__class__, BrowserClassItem) or \ |
1860 if issubclass( |
1816 issubclass(other.__class__, BrowserClassAttributesItem) or \ |
1861 other.__class__, |
1817 issubclass(other.__class__, BrowserImportItem): |
1862 (BrowserClassItem, BrowserClassAttributesItem, BrowserImportItem) |
|
1863 ): |
1818 return order == Qt.AscendingOrder |
1864 return order == Qt.AscendingOrder |
1819 |
1865 |
1820 return BrowserItem.lessThan(self, other, column, order) |
1866 return BrowserItem.lessThan(self, other, column, order) |
1821 |
1867 |
1822 |
1868 |
1843 @param other reference to item to compare against (BrowserItem) |
1889 @param other reference to item to compare against (BrowserItem) |
1844 @param column column number to use for the comparison (integer) |
1890 @param column column number to use for the comparison (integer) |
1845 @param order sort order (Qt.SortOrder) (for special sorting) |
1891 @param order sort order (Qt.SortOrder) (for special sorting) |
1846 @return true, if this item is less than other (boolean) |
1892 @return true, if this item is less than other (boolean) |
1847 """ |
1893 """ |
1848 if issubclass(other.__class__, BrowserClassItem) or \ |
1894 if issubclass( |
1849 issubclass(other.__class__, BrowserClassAttributesItem): |
1895 other.__class__, |
|
1896 (BrowserClassItem, BrowserClassAttributesItem) |
|
1897 ): |
1850 return order == Qt.AscendingOrder |
1898 return order == Qt.AscendingOrder |
1851 |
1899 |
1852 return BrowserItem.lessThan(self, other, column, order) |
1900 return BrowserItem.lessThan(self, other, column, order) |
1853 |
1901 |
1854 |
1902 |
1910 @param other reference to item to compare against (BrowserItem) |
1958 @param other reference to item to compare against (BrowserItem) |
1911 @param column column number to use for the comparison (integer) |
1959 @param column column number to use for the comparison (integer) |
1912 @param order sort order (Qt.SortOrder) (for special sorting) |
1960 @param order sort order (Qt.SortOrder) (for special sorting) |
1913 @return true, if this item is less than other (boolean) |
1961 @return true, if this item is less than other (boolean) |
1914 """ |
1962 """ |
1915 if Preferences.getUI("BrowsersListContentsByOccurrence") and \ |
1963 if ( |
1916 column == 0: |
1964 Preferences.getUI("BrowsersListContentsByOccurrence") and |
|
1965 column == 0 |
|
1966 ): |
1917 if order == Qt.AscendingOrder: |
1967 if order == Qt.AscendingOrder: |
1918 return self.lineno() < other.lineno() |
1968 return self.lineno() < other.lineno() |
1919 else: |
1969 else: |
1920 return self.lineno() > other.lineno() |
1970 return self.lineno() > other.lineno() |
1921 |
1971 |