16 import os |
16 import os |
17 import re |
17 import re |
18 import collections |
18 import collections |
19 |
19 |
20 from PyQt5.QtCore import pyqtSlot, Qt, QDate, QProcess, QTimer, QRegExp, \ |
20 from PyQt5.QtCore import pyqtSlot, Qt, QDate, QProcess, QTimer, QRegExp, \ |
21 QSize, QPoint, QUrl |
21 QSize, QPoint, QFileInfo |
22 from PyQt5.QtGui import QCursor, QColor, QPixmap, QPainter, QPen, QBrush, QIcon |
22 from PyQt5.QtGui import QCursor, QColor, QPixmap, QPainter, QPen, QBrush, \ |
|
23 QIcon, QTextCursor |
23 from PyQt5.QtWidgets import QWidget, QDialogButtonBox, QHeaderView, \ |
24 from PyQt5.QtWidgets import QWidget, QDialogButtonBox, QHeaderView, \ |
24 QTreeWidgetItem, QApplication, QLineEdit, QMenu, QInputDialog |
25 QTreeWidgetItem, QApplication, QLineEdit, QMenu, QInputDialog |
25 |
26 |
26 from E5Gui.E5Application import e5App |
27 from E5Gui.E5Application import e5App |
27 from E5Gui import E5MessageBox |
28 from E5Gui import E5MessageBox, E5FileDialog |
28 |
29 |
29 from .Ui_HgLogBrowserDialog import Ui_HgLogBrowserDialog |
30 from .Ui_HgLogBrowserDialog import Ui_HgLogBrowserDialog |
30 |
31 |
|
32 from .HgDiffHighlighter import HgDiffHighlighter |
|
33 from .HgDiffGenerator import HgDiffGenerator |
|
34 |
31 import UI.PixmapCache |
35 import UI.PixmapCache |
|
36 import Preferences |
|
37 import Utilities |
32 |
38 |
33 COLORNAMES = ["blue", "darkgreen", "red", "green", "darkblue", "purple", |
39 COLORNAMES = ["blue", "darkgreen", "red", "green", "darkblue", "purple", |
34 "cyan", "olive", "magenta", "darkred", "darkmagenta", |
40 "cyan", "olive", "magenta", "darkred", "darkmagenta", |
35 "darkcyan", "gray", "yellow"] |
41 "darkcyan", "gray", "yellow"] |
36 COLORS = [str(QColor(x).name()) for x in COLORNAMES] |
42 COLORS = [str(QColor(x).name()) for x in COLORNAMES] |
1305 else: |
1305 else: |
1306 self.__finish() |
1306 self.__finish() |
1307 elif button == self.refreshButton: |
1307 elif button == self.refreshButton: |
1308 self.on_refreshButton_clicked() |
1308 self.on_refreshButton_clicked() |
1309 |
1309 |
1310 def __updateDiffButtons(self): |
1310 def __updateSbsSelectLabel(self): |
1311 """ |
1311 """ |
1312 Private slot to update the enabled status of the diff buttons. |
1312 Private slot to update the enabled status of the diff buttons. |
1313 """ |
1313 """ |
1314 selectionLength = len(self.logTree.selectedItems()) |
1314 self.sbsSelectLabel.clear() |
1315 if selectionLength <= 1: |
1315 if self.__isFile: |
1316 current = self.logTree.currentItem() |
1316 selectedItems = self.logTree.selectedItems() |
1317 if current is None: |
1317 if len(selectedItems) == 1: |
1318 self.diffP1Button.setEnabled(False) |
1318 currentItem = selectedItems[0] |
1319 self.diffP2Button.setEnabled(False) |
1319 rev2 = currentItem.text(self.RevisionColumn).split(":", 1)[0]\ |
1320 else: |
1320 .strip() |
1321 parents = current.data(0, self.__parentsRole) |
1321 parents = currentItem.data(0, self.__parentsRole) |
1322 self.diffP1Button.setEnabled(len(parents) > 0) |
1322 if parents: |
1323 self.diffP2Button.setEnabled(len(parents) > 1) |
1323 parentLinks = [] |
1324 |
1324 for index in range(len(parents)): |
1325 self.diffRevisionsButton.setEnabled(False) |
1325 parentLinks.append( |
1326 elif selectionLength == 2: |
1326 '<a href="sbsdiff:{0}_{1}"> {2} </a>' |
1327 self.diffP1Button.setEnabled(False) |
1327 .format(parents[index], rev2, index + 1)) |
1328 self.diffP2Button.setEnabled(False) |
1328 self.sbsSelectLabel.setText( |
1329 |
1329 self.tr('Side-by-Side Diff to Parent {0}').format( |
1330 self.diffRevisionsButton.setEnabled(True) |
1330 " ".join(parentLinks))) |
1331 else: |
1331 elif len(selectedItems) == 2: |
1332 self.diffP1Button.setEnabled(False) |
1332 rev1 = int(selectedItems[0].text(self.RevisionColumn) |
1333 self.diffP2Button.setEnabled(False) |
1333 .split(":", 1)[0]) |
1334 |
1334 rev2 = int(selectedItems[1].text(self.RevisionColumn) |
1335 self.diffRevisionsButton.setEnabled(False) |
1335 .split(":", 1)[0]) |
|
1336 if rev1 > rev2: |
|
1337 # Swap the entries, so that rev1 < rev2 |
|
1338 rev1, rev2 = rev2, rev1 |
|
1339 self.sbsSelectLabel.setText(self.tr( |
|
1340 '<a href="sbsdiff:{0}_{1}">Side-by-Side Compare</a>') |
|
1341 .format(rev1, rev2)) |
1336 |
1342 |
1337 def __updateToolMenuActions(self): |
1343 def __updateToolMenuActions(self): |
1338 """ |
1344 """ |
1339 Private slot to update the status of the tool menu actions and |
1345 Private slot to update the status of the tool menu actions and |
1340 the tool menu button. |
1346 the tool menu button. |
1409 |
1415 |
1410 self.actionsButton.setEnabled(True) |
1416 self.actionsButton.setEnabled(True) |
1411 else: |
1417 else: |
1412 self.actionsButton.setEnabled(False) |
1418 self.actionsButton.setEnabled(False) |
1413 |
1419 |
1414 def __updateGui(self, itm): |
1420 def __updateDetailsAndFiles(self): |
1415 """ |
1421 """ |
1416 Private slot to update GUI elements except tool menu actions. |
1422 Private slot to update the details and file changes panes. |
1417 |
|
1418 @param itm reference to the item the update should be based on |
|
1419 (QTreeWidgetItem) |
|
1420 """ |
1423 """ |
1421 self.detailsEdit.clear() |
1424 self.detailsEdit.clear() |
1422 self.messageEdit.clear() |
|
1423 self.filesTree.clear() |
1425 self.filesTree.clear() |
1424 |
1426 self.__diffUpdatesFiles = False |
|
1427 |
|
1428 selectedItems = self.logTree.selectedItems() |
|
1429 if len(selectedItems) == 1: |
|
1430 self.detailsEdit.setHtml( |
|
1431 self.__generateDetailsTableText(selectedItems[0])) |
|
1432 self.__updateFilesTree(self.filesTree, selectedItems[0]) |
|
1433 self.__resizeColumnsFiles() |
|
1434 self.__resortFiles() |
|
1435 elif len(selectedItems) == 2: |
|
1436 self.__diffUpdatesFiles = True |
|
1437 index1 = self.logTree.indexOfTopLevelItem(selectedItems[0]) |
|
1438 index2 = self.logTree.indexOfTopLevelItem(selectedItems[1]) |
|
1439 if index1 > index2: |
|
1440 # Swap the entries |
|
1441 selectedItems[0], selectedItems[1] = \ |
|
1442 selectedItems[1], selectedItems[0] |
|
1443 html = "{0}<hr/>{1}".format( |
|
1444 self.__generateDetailsTableText(selectedItems[0]), |
|
1445 self.__generateDetailsTableText(selectedItems[1]), |
|
1446 ) |
|
1447 self.detailsEdit.setHtml(html) |
|
1448 # self.filesTree is updated by the diff |
|
1449 |
|
1450 def __generateDetailsTableText(self, itm): |
|
1451 """ |
|
1452 Private method to generate an HTML table with the details of the given |
|
1453 changeset. |
|
1454 |
|
1455 @param itm reference to the item the table should be based on |
|
1456 @type QTreeWidgetItem |
|
1457 @return HTML table containing details |
|
1458 @rtype str |
|
1459 """ |
1425 if itm is not None: |
1460 if itm is not None: |
1426 if itm.text(self.TagsColumn): |
1461 if itm.text(self.TagsColumn): |
1427 tagsStr = self.__tagsTemplate.format(itm.text(self.TagsColumn)) |
1462 tagsStr = self.__tagsTemplate.format(itm.text(self.TagsColumn)) |
1428 else: |
1463 else: |
1429 tagsStr = "" |
1464 tagsStr = "" |
|
1465 |
1430 if itm.text(self.BookmarksColumn): |
1466 if itm.text(self.BookmarksColumn): |
1431 bookmarksStr = self.__bookmarksTemplate.format( |
1467 bookmarksStr = self.__bookmarksTemplate.format( |
1432 itm.text(self.BookmarksColumn)) |
1468 itm.text(self.BookmarksColumn)) |
1433 else: |
1469 else: |
1434 bookmarksStr = "" |
1470 bookmarksStr = "" |
1435 |
1471 |
1436 if itm.data(0, self.__latestTagRole): |
1472 if self.projectMode and itm.data(0, self.__latestTagRole): |
1437 latestTagLinks = [] |
1473 latestTagLinks = [] |
1438 for tag in itm.data(0, self.__latestTagRole): |
1474 for tag in itm.data(0, self.__latestTagRole): |
1439 url = QUrl() |
1475 latestTagLinks.append('<a href="rev:{0}">{1}</a>'.format( |
1440 url.setScheme("rev") |
1476 self.__getRevisionOfTag(tag)[0], tag)) |
1441 url.setPath(self.__getRevisionOfTag(tag)[0]) |
|
1442 latestTagLinks.append('<a href="{0}">{1}</a>'.format( |
|
1443 url.toString(), tag)) |
|
1444 latestTagStr = self.__latestTagTemplate.format( |
1477 latestTagStr = self.__latestTagTemplate.format( |
1445 ", ".join(latestTagLinks)) |
1478 ", ".join(latestTagLinks)) |
1446 else: |
1479 else: |
1447 latestTagStr = "" |
1480 latestTagStr = "" |
1448 |
1481 |
1449 rev = int(itm.text(self.RevisionColumn).split(":", 1)[0]) |
1482 rev = int(itm.text(self.RevisionColumn).split(":", 1)[0]) |
1450 |
1483 |
1451 parentLinks = [] |
1484 if itm.data(0, self.__parentsRole): |
1452 for parent in [str(x) for x in itm.data(0, self.__parentsRole)]: |
1485 parentLinks = [] |
1453 url = QUrl() |
1486 for parent in [str(x) for x in |
1454 url.setScheme("rev") |
1487 itm.data(0, self.__parentsRole)]: |
1455 url.setPath(parent) |
1488 parentLinks.append( |
1456 parentLinks.append('<a href="{0}">{1}</a>'.format( |
1489 '<a href="rev:{0}">{0}</a>'.format(parent)) |
1457 url.toString(), parent)) |
1490 parentsStr = self.__parentsTemplate.format( |
|
1491 ", ".join(parentLinks)) |
|
1492 else: |
|
1493 parentsStr = "" |
1458 |
1494 |
1459 childLinks = [] |
1495 if self.__childrenInfo[rev]: |
1460 for child in [str(x) for x in self.__childrenInfo[rev]]: |
1496 childLinks = [] |
1461 url = QUrl() |
1497 for child in [str(x) for x in self.__childrenInfo[rev]]: |
1462 url.setScheme("rev") |
1498 childLinks.append( |
1463 url.setPath(child) |
1499 '<a href="rev:{0}">{0}</a>'.format(child)) |
1464 childLinks.append('<a href="{0}">{1}</a>'.format( |
1500 childrenStr = self.__childrenTemplate.format( |
1465 url.toString(), child)) |
1501 ", ".join(childLinks)) |
|
1502 else: |
|
1503 childrenStr = "" |
1466 |
1504 |
1467 self.detailsEdit.setHtml(self.__detailsTemplate.format( |
1505 messageStr = "<br />\n".join([ |
|
1506 line.strip() for line in itm.data(0, self.__messageRole) |
|
1507 ]) |
|
1508 |
|
1509 html = self.__detailsTemplate.format( |
1468 itm.text(self.RevisionColumn), |
1510 itm.text(self.RevisionColumn), |
1469 itm.text(self.DateColumn), |
1511 itm.text(self.DateColumn), |
1470 itm.text(self.AuthorColumn), |
1512 itm.text(self.AuthorColumn), |
1471 itm.text(self.BranchColumn).replace( |
1513 itm.text(self.BranchColumn).replace( |
1472 self.ClosedIndicator, ""), |
1514 self.ClosedIndicator, ""), |
1473 ", ".join(parentLinks), |
1515 parentsStr + childrenStr + tagsStr + latestTagStr + |
1474 ", ".join(childLinks), |
1516 bookmarksStr, |
1475 tagsStr + latestTagStr + bookmarksStr, |
1517 messageStr, |
1476 )) |
1518 ) |
1477 |
1519 else: |
1478 for line in itm.data(0, self.__messageRole): |
1520 html = "" |
1479 self.messageEdit.append(line.strip()) |
1521 |
1480 |
1522 return html |
|
1523 |
|
1524 def __updateFilesTree(self, parent, itm): |
|
1525 """ |
|
1526 Private method to update the files tree with changes of the given item. |
|
1527 |
|
1528 @param parent parent for the items to be added |
|
1529 @type QTreeWidget or QTreeWidgetItem |
|
1530 @param itm reference to the item the update should be based on |
|
1531 @type QTreeWidgetItem |
|
1532 """ |
|
1533 if itm is not None: |
1481 changes = itm.data(0, self.__changesRole) |
1534 changes = itm.data(0, self.__changesRole) |
1482 if len(changes) > 0: |
1535 if len(changes) > 0: |
1483 for change in changes: |
1536 for change in changes: |
1484 self.__generateFileItem( |
1537 QTreeWidgetItem(parent, [ |
1485 change["action"], change["path"], change["copyfrom"]) |
1538 self.flags[change["action"]], |
1486 self.__resizeColumnsFiles() |
1539 change["path"].strip(), |
1487 self.__resortFiles() |
1540 change["copyfrom"].strip(), |
|
1541 ]) |
1488 |
1542 |
1489 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
1543 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
1490 def on_logTree_currentItemChanged(self, current, previous): |
1544 def on_logTree_currentItemChanged(self, current, previous): |
1491 """ |
1545 """ |
1492 Private slot called, when the current item of the log tree changes. |
1546 Private slot called, when the current item of the log tree changes. |
1493 |
1547 |
1494 @param current reference to the new current item (QTreeWidgetItem) |
1548 @param current reference to the new current item (QTreeWidgetItem) |
1495 @param previous reference to the old current item (QTreeWidgetItem) |
1549 @param previous reference to the old current item (QTreeWidgetItem) |
1496 """ |
1550 """ |
1497 self.__updateGui(current) |
|
1498 self.__updateDiffButtons() |
|
1499 self.__updateToolMenuActions() |
1551 self.__updateToolMenuActions() |
1500 |
1552 |
1501 # Highlight the current entry using a bold font |
1553 # Highlight the current entry using a bold font |
1502 for col in range(self.logTree.columnCount()): |
1554 for col in range(self.logTree.columnCount()): |
1503 current and current.setFont(col, self.__logTreeBoldFont) |
1555 current and current.setFont(col, self.__logTreeBoldFont) |
1504 previous and previous.setFont(col, self.__logTreeNormalFont) |
1556 previous and previous.setFont(col, self.__logTreeNormalFont) |
1505 |
1557 |
1506 # set the state of the up and down buttons |
1558 # set the state of the up and down buttons |
1507 self.upButton.setEnabled( |
1559 self.upButton.setEnabled( |
1508 current is not None and |
1560 current is not None and |
1509 self.logTree.indexOfTopLevelItem(current) > 0) |
1561 self.logTree.indexOfTopLevelItem(current) > 0) |
1510 self.downButton.setEnabled( |
1562 self.downButton.setEnabled( |
1511 current is not None and |
1563 current is not None and |
1512 int(current.text(self.RevisionColumn).split(":")[0]) > 0) |
1564 int(current.text(self.RevisionColumn).split(":")[0]) > 0) |
1513 |
1565 |
1514 @pyqtSlot() |
1566 @pyqtSlot() |
1515 def on_logTree_itemSelectionChanged(self): |
1567 def on_logTree_itemSelectionChanged(self): |
1516 """ |
1568 """ |
1517 Private slot called, when the selection has changed. |
1569 Private slot called, when the selection has changed. |
1518 """ |
1570 """ |
1519 self.__updateDiffButtons() |
1571 self.__updateDetailsAndFiles() |
|
1572 self.__updateSbsSelectLabel() |
1520 self.__updateToolMenuActions() |
1573 self.__updateToolMenuActions() |
|
1574 self.__generateDiffs() |
1521 |
1575 |
1522 @pyqtSlot() |
1576 @pyqtSlot() |
1523 def on_upButton_clicked(self): |
1577 def on_upButton_clicked(self): |
1524 """ |
1578 """ |
1525 Private slot to move the current item up one entry. |
1579 Private slot to move the current item up one entry. |
1546 """ |
1600 """ |
1547 Private slot to handle the Next button. |
1601 Private slot to handle the Next button. |
1548 """ |
1602 """ |
1549 if self.__lastRev > 0: |
1603 if self.__lastRev > 0: |
1550 self.__getLogEntries(startRev=self.__lastRev - 1) |
1604 self.__getLogEntries(startRev=self.__lastRev - 1) |
1551 |
|
1552 @pyqtSlot() |
|
1553 def on_diffP1Button_clicked(self): |
|
1554 """ |
|
1555 Private slot to handle the Diff to Parent 1 button. |
|
1556 """ |
|
1557 if len(self.logTree.selectedItems()): |
|
1558 itm = self.logTree.selectedItems()[0] |
|
1559 else: |
|
1560 itm = self.logTree.currentItem() |
|
1561 if itm is None: |
|
1562 self.diffP1Button.setEnabled(False) |
|
1563 return |
|
1564 rev2 = int(itm.text(self.RevisionColumn).split(":")[0]) |
|
1565 |
|
1566 rev1 = itm.data(0, self.__parentsRole)[0] |
|
1567 if rev1 < 0: |
|
1568 self.diffP1Button.setEnabled(False) |
|
1569 return |
|
1570 |
|
1571 self.__diffRevisions(rev1, rev2) |
|
1572 |
|
1573 @pyqtSlot() |
|
1574 def on_diffP2Button_clicked(self): |
|
1575 """ |
|
1576 Private slot to handle the Diff to Parent 2 button. |
|
1577 """ |
|
1578 if len(self.logTree.selectedItems()): |
|
1579 itm = self.logTree.selectedItems()[0] |
|
1580 else: |
|
1581 itm = self.logTree.currentItem() |
|
1582 if itm is None: |
|
1583 self.diffP2Button.setEnabled(False) |
|
1584 return |
|
1585 rev2 = int(itm.text(self.RevisionColumn).split(":")[0]) |
|
1586 |
|
1587 rev1 = itm.data(0, self.__parentsRole)[1] |
|
1588 if rev1 < 0: |
|
1589 self.diffP2Button.setEnabled(False) |
|
1590 return |
|
1591 |
|
1592 self.__diffRevisions(rev1, rev2) |
|
1593 |
|
1594 @pyqtSlot() |
|
1595 def on_diffRevisionsButton_clicked(self): |
|
1596 """ |
|
1597 Private slot to handle the Compare Revisions button. |
|
1598 """ |
|
1599 items = self.logTree.selectedItems() |
|
1600 |
|
1601 rev2 = int(items[0].text(self.RevisionColumn).split(":")[0]) |
|
1602 rev1 = int(items[1].text(self.RevisionColumn).split(":")[0]) |
|
1603 |
|
1604 self.__diffRevisions(min(rev1, rev2), max(rev1, rev2)) |
|
1605 |
1605 |
1606 @pyqtSlot(QDate) |
1606 @pyqtSlot(QDate) |
1607 def on_fromDate_dateChanged(self, date): |
1607 def on_fromDate_dateChanged(self, date): |
1608 """ |
1608 """ |
1609 Private slot called, when the from date changes. |
1609 Private slot called, when the from date changes. |
2194 self.logTree.setCurrentItem(itm) |
2191 self.logTree.setCurrentItem(itm) |
2195 else: |
2192 else: |
2196 # load the next batch and try again |
2193 # load the next batch and try again |
2197 self.on_nextButton_clicked() |
2194 self.on_nextButton_clicked() |
2198 self.__revisionClicked(url) |
2195 self.__revisionClicked(url) |
|
2196 |
|
2197 ########################################################################### |
|
2198 ## Diff handling methods below |
|
2199 ########################################################################### |
|
2200 |
|
2201 def __generateDiffs(self, parent=1): |
|
2202 """ |
|
2203 Private slot to generate diff outputs for the selected item. |
|
2204 |
|
2205 @param parent number of parent to diff against |
|
2206 @type int |
|
2207 """ |
|
2208 self.diffEdit.clear() |
|
2209 self.diffLabel.setText(self.tr("Differences")) |
|
2210 self.diffSelectLabel.clear() |
|
2211 |
|
2212 selectedItems = self.logTree.selectedItems() |
|
2213 if len(selectedItems) == 1: |
|
2214 currentItem = selectedItems[0] |
|
2215 rev2 = currentItem.text(self.RevisionColumn).split(":", 1)[0] |
|
2216 parents = currentItem.data(0, self.__parentsRole) |
|
2217 if len(parents) >= parent: |
|
2218 self.diffLabel.setText( |
|
2219 self.tr("Differences to Parent {0}").format(parent)) |
|
2220 rev1 = parents[parent - 1] |
|
2221 |
|
2222 self.__diffGenerator.start(self.__filename, [rev1, rev2], |
|
2223 self.__bundle) |
|
2224 |
|
2225 if len(parents) > 1: |
|
2226 if parent == 1: |
|
2227 par1 = " 1 " |
|
2228 else: |
|
2229 par1 = '<a href="diff:1"> 1 </a>' |
|
2230 if parent == 2: |
|
2231 par2 = " 2 " |
|
2232 else: |
|
2233 par2 = '<a href="diff:2"> 2 </a>' |
|
2234 self.diffSelectLabel.setText( |
|
2235 self.tr('Diff to Parent {0}{1}').format(par1, par2)) |
|
2236 elif len(selectedItems) == 2: |
|
2237 rev2 = int(selectedItems[0].text( |
|
2238 self.RevisionColumn).split(":")[0]) |
|
2239 rev1 = int(selectedItems[1].text( |
|
2240 self.RevisionColumn).split(":")[0]) |
|
2241 |
|
2242 self.__diffGenerator.start(self.__filename, |
|
2243 [min(rev1, rev2), max(rev1, rev2)], |
|
2244 self.__bundle) |
|
2245 |
|
2246 def __generatorFinished(self): |
|
2247 """ |
|
2248 Private slot connected to the finished signal of the diff generator. |
|
2249 """ |
|
2250 diff, errors, fileSeparators = self.__diffGenerator.getResult() |
|
2251 |
|
2252 if diff: |
|
2253 self.diffEdit.setPlainText("".join(diff)) |
|
2254 elif errors: |
|
2255 self.diffEdit.setPlainText("".join(errors)) |
|
2256 else: |
|
2257 self.diffEdit.setPlainText(self.tr('There is no difference.')) |
|
2258 |
|
2259 self.saveLabel.setVisible(bool(diff)) |
|
2260 |
|
2261 if self.__diffUpdatesFiles: |
|
2262 for oldFileName, newFileName, lineNumber in fileSeparators: |
|
2263 if oldFileName == newFileName: |
|
2264 fileName = oldFileName |
|
2265 elif oldFileName == "__NULL__": |
|
2266 fileName = newFileName |
|
2267 else: |
|
2268 fileName = oldFileName |
|
2269 item = QTreeWidgetItem(self.filesTree, ["", fileName, ""]) |
|
2270 item.setData(0, self.__diffFileLineRole, lineNumber) |
|
2271 self.__resizeColumnsFiles() |
|
2272 self.__resortFiles() |
|
2273 else: |
|
2274 for oldFileName, newFileName, lineNumber in fileSeparators: |
|
2275 for fileName in (oldFileName, newFileName): |
|
2276 if fileName != "__NULL__": |
|
2277 items = self.filesTree.findItems( |
|
2278 fileName, Qt.MatchExactly, 1) |
|
2279 for item in items: |
|
2280 item.setData(0, self.__diffFileLineRole, |
|
2281 lineNumber) |
|
2282 |
|
2283 tc = self.diffEdit.textCursor() |
|
2284 tc.movePosition(QTextCursor.Start) |
|
2285 self.diffEdit.setTextCursor(tc) |
|
2286 self.diffEdit.ensureCursorVisible() |
|
2287 |
|
2288 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) |
|
2289 def on_filesTree_currentItemChanged(self, current, previous): |
|
2290 """ |
|
2291 Private slot called, when the current item of the files tree changes. |
|
2292 |
|
2293 @param current reference to the new current item (QTreeWidgetItem) |
|
2294 @param previous reference to the old current item (QTreeWidgetItem) |
|
2295 """ |
|
2296 if current: |
|
2297 para = current.data(0, self.__diffFileLineRole) |
|
2298 if para is not None: |
|
2299 if para == 0: |
|
2300 tc = self.diffEdit.textCursor() |
|
2301 tc.movePosition(QTextCursor.Start) |
|
2302 self.diffEdit.setTextCursor(tc) |
|
2303 self.diffEdit.ensureCursorVisible() |
|
2304 elif para == -1: |
|
2305 tc = self.diffEdit.textCursor() |
|
2306 tc.movePosition(QTextCursor.End) |
|
2307 self.diffEdit.setTextCursor(tc) |
|
2308 self.diffEdit.ensureCursorVisible() |
|
2309 else: |
|
2310 # step 1: move cursor to end |
|
2311 tc = self.diffEdit.textCursor() |
|
2312 tc.movePosition(QTextCursor.End) |
|
2313 self.diffEdit.setTextCursor(tc) |
|
2314 self.diffEdit.ensureCursorVisible() |
|
2315 |
|
2316 # step 2: move cursor to desired line |
|
2317 tc = self.diffEdit.textCursor() |
|
2318 delta = tc.blockNumber() - para |
|
2319 tc.movePosition(QTextCursor.PreviousBlock, |
|
2320 QTextCursor.MoveAnchor, delta) |
|
2321 self.diffEdit.setTextCursor(tc) |
|
2322 self.diffEdit.ensureCursorVisible() |
|
2323 |
|
2324 @pyqtSlot(str) |
|
2325 def on_diffSelectLabel_linkActivated(self, link): |
|
2326 """ |
|
2327 Private slot to handle the selection of a diff target. |
|
2328 |
|
2329 @param link activated link |
|
2330 @type str |
|
2331 """ |
|
2332 if ":" in link: |
|
2333 scheme, parent = link.split(":", 1) |
|
2334 if scheme == "diff": |
|
2335 try: |
|
2336 parent = int(parent) |
|
2337 self.__generateDiffs(parent) |
|
2338 except ValueError: |
|
2339 # ignore silently |
|
2340 pass |
|
2341 |
|
2342 @pyqtSlot(str) |
|
2343 def on_saveLabel_linkActivated(self, link): |
|
2344 """ |
|
2345 Private slot to handle the selection of the save link. |
|
2346 |
|
2347 @param link activated link |
|
2348 @type str |
|
2349 """ |
|
2350 if ":" not in link: |
|
2351 return |
|
2352 |
|
2353 scheme, rest = link.split(":", 1) |
|
2354 if scheme != "save" or rest != "me": |
|
2355 return |
|
2356 |
|
2357 if self.projectMode: |
|
2358 fname = self.vcs.splitPath(self.__filename)[0] |
|
2359 fname += "/{0}.diff".format(os.path.split(fname)[-1]) |
|
2360 else: |
|
2361 dname, fname = self.vcs.splitPath(self.__filename) |
|
2362 if fname != '.': |
|
2363 fname = "{0}.diff".format(self.__filename) |
|
2364 else: |
|
2365 fname = dname |
|
2366 |
|
2367 fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( |
|
2368 self, |
|
2369 self.tr("Save Diff"), |
|
2370 fname, |
|
2371 self.tr("Patch Files (*.diff)"), |
|
2372 None, |
|
2373 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) |
|
2374 |
|
2375 if not fname: |
|
2376 return # user aborted |
|
2377 |
|
2378 ext = QFileInfo(fname).suffix() |
|
2379 if not ext: |
|
2380 ex = selectedFilter.split("(*")[1].split(")")[0] |
|
2381 if ex: |
|
2382 fname += ex |
|
2383 if QFileInfo(fname).exists(): |
|
2384 res = E5MessageBox.yesNo( |
|
2385 self, |
|
2386 self.tr("Save Diff"), |
|
2387 self.tr("<p>The patch file <b>{0}</b> already exists." |
|
2388 " Overwrite it?</p>").format(fname), |
|
2389 icon=E5MessageBox.Warning) |
|
2390 if not res: |
|
2391 return |
|
2392 fname = Utilities.toNativeSeparators(fname) |
|
2393 |
|
2394 eol = e5App().getObject("Project").getEolString() |
|
2395 try: |
|
2396 f = open(fname, "w", encoding="utf-8", newline="") |
|
2397 f.write(eol.join(self.diffEdit.toPlainText().splitlines())) |
|
2398 f.close() |
|
2399 except IOError as why: |
|
2400 E5MessageBox.critical( |
|
2401 self, self.tr('Save Diff'), |
|
2402 self.tr( |
|
2403 '<p>The patch file <b>{0}</b> could not be saved.' |
|
2404 '<br>Reason: {1}</p>') |
|
2405 .format(fname, str(why))) |
|
2406 |
|
2407 @pyqtSlot(str) |
|
2408 def on_sbsSelectLabel_linkActivated(self, link): |
|
2409 """ |
|
2410 Private slot to handle selection of a side-by-side link. |
|
2411 |
|
2412 @param link text of the selected link |
|
2413 @type str |
|
2414 """ |
|
2415 if ":" in link: |
|
2416 scheme, path = link.split(":", 1) |
|
2417 if scheme == "sbsdiff" and "_" in path: |
|
2418 rev1, rev2 = path.split("_", 1) |
|
2419 self.vcs.hgSbsDiff(self.__filename, revisions=(rev1, rev2)) |