# # # add_file "guitone/res/dialogs/revision_diff.ui" # content [c2d6932f4262a673b95c1588db61492d082e8b17] # # add_file "guitone/src/view/dialogs/RevisionDiff.cpp" # content [8e5f7cfdc82f4ea811c72387596b40711d45ac57] # # add_file "guitone/src/view/dialogs/RevisionDiff.h" # content [30a57d9b293fa8ea65371907ef3d5ff0a5996e44] # # patch "guitone/guitone.pro" # from [94af3d787686a8f7727a0491e459a01ba1c451a6] # to [dfe02e6f1114d8de5b82ffa4bb285480489b58bb] # # patch "guitone/src/model/ContentDiff.cpp" # from [bdaf1d319eaa667501b6321534535a990e61d011] # to [01b29d9366d9187f3f4b06f920eef473a109f44d] # # patch "guitone/src/model/ContentDiff.h" # from [7b893384b27150e421956e5834087d7fe454c041] # to [db6aed4aa7fa672a4dcfb29f160d45cea1f5228f] # # patch "guitone/src/view/InventoryView.cpp" # from [64145819868bf2bc34916de61f252985eb0838e0] # to [a9729726a253d1b035732e56347949c2c0434b2e] # # patch "guitone/src/view/InventoryView.h" # from [e27d8be7c798a13d350783fdaf5b38bc85b6e6bc] # to [4be916c896d8a6131f74b44eef8b4fb1a78cfdde] # ============================================================ --- guitone/res/dialogs/revision_diff.ui c2d6932f4262a673b95c1588db61492d082e8b17 +++ guitone/res/dialogs/revision_diff.ui c2d6932f4262a673b95c1588db61492d082e8b17 @@ -0,0 +1,93 @@ + + RevisionDiffDialog + + + + 0 + 0 + 444 + 432 + + + + Differences in %1 between %2 and %3 + + + + 9 + + + 6 + + + + + 0 + + + 6 + + + + + + + + 0 + + + 6 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + + + DiffView + QTreeView +
../DiffView.h
+
+
+ + + + closeButton + clicked() + RevisionDiffDialog + accept() + + + 214 + 225 + + + 199 + 149 + + + + +
============================================================ --- guitone/src/view/dialogs/RevisionDiff.cpp 8e5f7cfdc82f4ea811c72387596b40711d45ac57 +++ guitone/src/view/dialogs/RevisionDiff.cpp 8e5f7cfdc82f4ea811c72387596b40711d45ac57 @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2006 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "RevisionDiff.h" +#include "../../monotone/Monotone.h" + +#include + +RevisionDiff::RevisionDiff(QWidget* parent) + : QDialog(parent) +{ + setupUi(this); + diffView->setItemsExpandable(true); + diffView->setRootIsDecorated(true); + // arrow width is approx. 10px + diffView->setIndentation(10); +} + +void RevisionDiff::init(QString path, QString leftRevision, QString rightRevision) +{ + QString title = windowTitle(); + QString left = tr("base revision"); + QString right = tr("workspace revision"); + + if (leftRevision.size() > 0) + { + left = leftRevision.left(8).append("..."); + } + + if (rightRevision.size() > 0) + { + right = rightRevision.left(8).append("..."); + } + + setWindowTitle(title.arg(path).arg(left).arg(right)); + + diffModel = new ContentDiff(this); + diffView->setModel(diffModel); + + // FIXME: proper error handling here + if (!diffModel->readDiff(path, leftRevision, rightRevision)) + { + qDebug("RevisionDiff::init: couldn't execute readDiff"); + done(1); + } +} + +RevisionDiff::~RevisionDiff() {} + ============================================================ --- guitone/src/view/dialogs/RevisionDiff.h 30a57d9b293fa8ea65371907ef3d5ff0a5996e44 +++ guitone/src/view/dialogs/RevisionDiff.h 30a57d9b293fa8ea65371907ef3d5ff0a5996e44 @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2006 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef REVISION_DIFF_H +#define REVISION_DIFF_H + +#include "ui_revision_diff.h" +#include "../../model/ContentDiff.h" + +class RevisionDiff : public QDialog, private Ui::RevisionDiffDialog +{ + Q_OBJECT + +public: + RevisionDiff(QWidget*); + void init(QString, QString, QString); + + ~RevisionDiff(); + +private: + ContentDiff * diffModel; +}; + +#endif ============================================================ --- guitone/guitone.pro 94af3d787686a8f7727a0491e459a01ba1c451a6 +++ guitone/guitone.pro dfe02e6f1114d8de5b82ffa4bb285480489b58bb @@ -16,6 +16,7 @@ HEADERS += src/view/Guitone.h \ src/view/dialogs/Preferences.h \ src/view/dialogs/AncestryGraph.h \ src/view/dialogs/FileDiff.h \ + src/view/dialogs/RevisionDiff.h \ src/view/dialogs/KeyManagement.h \ src/view/dialogs/GenerateKeypair.h \ src/view/dialogs/About.h \ @@ -51,6 +52,7 @@ SOURCES += src/view/Guitone.cpp \ src/view/dialogs/Preferences.cpp \ src/view/dialogs/AncestryGraph.cpp \ src/view/dialogs/FileDiff.cpp \ + src/view/dialogs/RevisionDiff.cpp \ src/view/dialogs/KeyManagement.cpp \ src/view/dialogs/GenerateKeypair.cpp \ src/view/dialogs/About.cpp \ @@ -80,6 +82,7 @@ FORMS += res/dialogs/switch_workspace. res/dialogs/preferences.ui \ res/dialogs/ancestry_graph.ui \ res/dialogs/file_diff.ui \ + res/dialogs/revision_diff.ui \ res/dialogs/key_management.ui \ res/dialogs/generate_keypair.ui \ res/dialogs/about.ui ============================================================ --- guitone/src/model/ContentDiff.cpp bdaf1d319eaa667501b6321534535a990e61d011 +++ guitone/src/model/ContentDiff.cpp 01b29d9366d9187f3f4b06f920eef473a109f44d @@ -21,7 +21,8 @@ #include "ContentDiff.h" #include "../monotone/Monotone.h" -#include +#include +#include ContentDiff::ContentDiff(QObject *parent) : AutomateCommand(parent) @@ -35,21 +36,7 @@ bool ContentDiff::readDiff(QString fileN bool ContentDiff::readDiff(QString fileName) { - Monotone * mtn = Monotone::singleton(); - - QStringList cmd; - cmd << "get_base_revision_id"; - int retCode; - - if (!mtn->executeCommand(cmd, retCode) || retCode != 0) - { - qWarning("ContentDiff::readDiff: could not execute get_base_revision_id"); - return false; - } - - QString rev = mtn->getOutput(); - rev.chop(1); - return readDiff(fileName, rev); + return readDiff(fileName, QString(), QString()); } bool ContentDiff::readDiff(QString fileName, QString leftRevision) @@ -64,10 +51,28 @@ bool ContentDiff::readDiff( // reset the view reset(); + Monotone *mtn = Monotone::singleton(); + QStringList cmd; cmd << "content_diff" << fileName; QStringList opts; + + if (leftRevision.length() == 0) + { + int retCode; + + if (!mtn->executeCommand(QStringList() << "get_base_revision_id", retCode) + || retCode != 0) + { + qWarning("ContentDiff::readDiff: could not execute get_base_revision_id"); + return false; + } + + leftRevision = mtn->getOutput(); + leftRevision.chop(1); + } + opts << "r" << leftRevision; if (rightRevision.length() > 0) @@ -75,8 +80,6 @@ bool ContentDiff::readDiff( opts << "r" << rightRevision; } - Monotone *mtn = Monotone::singleton(); - return mtn->triggerCommand(this, cmd, opts); } @@ -87,43 +90,171 @@ void ContentDiff::parseOutput(AutomateCo diffParser = new DiffParser(AutomateCommand::data); + // flatten the data for the current view + FileDiffs fileDiffs = diffParser->getAllDiffs(); + + QMapIterator i(fileDiffs); + while (i.hasNext()) + { + i.next(); + ListLine * fileLine = new ListLine(ListLine::FileLine); + lines.append(fileLine); + + Diff * diff = (Diff*)i.value(); + + if (diff->is_binary) + { + fileLine->secondColumn = tr("%1 (binary)").arg(i.key()); + continue; + } + + fileLine->secondColumn = + tr("%1 (%2 hunks)").arg(i.key()).arg(diff->hunks.size()); + + for (int j=0, k=diff->hunks.size(); jhunks.at(j); + for (int m=0, n=hunk->lines.size(); mlines.at(m); + ListLine * hunkLine = new ListLine(ListLine::HunkLine); + fileLine->addChild(hunkLine); + + if (line->state == DiffLine::Unchanged) + { + hunkLine->firstColumn = QString::number(hunk->leftStart + m); + } + else if (line->state == DiffLine::Added) + { + hunkLine->firstColumn = QString("+"); + } + else + { + hunkLine->firstColumn = QString("-"); + } + hunkLine->secondColumn = line->content; + } + + if (j < k-1) + { + ListLine * sep = new ListLine(ListLine::HunkSep); + fileLine->addChild(sep); + } + } + } + + reset(); + emit diffRead(); } -// -// dummy implementations, eventually these will get its own view sometime -// int ContentDiff::columnCount(const QModelIndex &parent) const { - return 0; + return 2; } QVariant ContentDiff::data(const QModelIndex &index, int role) const { - return QVariant(); + if (!index.isValid()) return QVariant(); + + int col = index.column(); + ListLine * line = static_cast(index.internalPointer()); + + if (role == Qt::DisplayRole) + { + switch (col) + { + case 0: return QVariant(line->firstColumn); + case 1: return QVariant(line->secondColumn); + } + + Q_ASSERT(false); + } + + if (role == Qt::FontRole && col == 1) + { + QFont font; + + if (line->type == ListLine::FileLine) + { + font.setBold(true); + } + + if (line->type == ListLine::HunkLine) + { + font.setStyleHint(QFont::Courier); + font.setFamily("Courier"); + } + + return QVariant(font); + } + + if (role == Qt::TextAlignmentRole && col == 0) + { + return QVariant(Qt::AlignRight); + } + + if (role == Qt::BackgroundRole) + { + if (line->type != ListLine::HunkLine) return QVariant(); + + if (line->firstColumn.compare("-") == 0) + { + return QVariant(QBrush(Qt::red)); + } + + if (line->firstColumn.compare("+") == 0) + { + return QVariant(QBrush(Qt::green)); + } + + // unchanged lines + return QVariant(); + } + + return QVariant(); } Qt::ItemFlags ContentDiff::flags(const QModelIndex &index) const { - return 0; + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant ContentDiff::headerData(int section, Qt::Orientation orientation, int role) const { - return QVariant(); + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + if (section == 0) return QVariant(tr("Line")); + return QVariant(tr("File/Content")); } int ContentDiff::rowCount(const QModelIndex& parent) const { - return 0; + if (!parent.isValid()) return lines.size(); + ListLine * line = static_cast(parent.internalPointer()); + return line->lines.size(); } QModelIndex ContentDiff::index(int row, int column, const QModelIndex& parent) const { - return QModelIndex(); + if (!parent.isValid()) + { + if (row >= lines.size()) return QModelIndex(); + ListLine * line = lines.at(row); + return createIndex(row, column, line); + } + + ListLine * parentLine = static_cast(parent.internalPointer()); + if (row >= parentLine->lines.size()) return QModelIndex(); + ListLine * line = parentLine->lines.at(row); + return createIndex(row, column, line); } QModelIndex ContentDiff::parent(const QModelIndex& index) const { - return QModelIndex(); + if (!index.isValid()) return QModelIndex(); + ListLine * line = static_cast(index.internalPointer()); + if (!line->parent) return QModelIndex(); + return createIndex(lines.indexOf(line->parent) , 0, line->parent); } + ============================================================ --- guitone/src/model/ContentDiff.h 7b893384b27150e421956e5834087d7fe454c041 +++ guitone/src/model/ContentDiff.h db6aed4aa7fa672a4dcfb29f160d45cea1f5228f @@ -26,6 +26,28 @@ #include "AutomateCommand.h" #include "../util/DiffParser.h" +#include + +struct ListLine +{ + enum Type { FileLine, HunkSep, HunkLine }; + + Type type; + QString firstColumn; + QString secondColumn; + ListLine * parent; + QList lines; + + ListLine() : type(HunkLine), parent(0) {} + ListLine(Type t) : type(t), parent(0) + { + if (type == HunkSep) firstColumn = "..."; + } + void addChild(ListLine * l) { lines.append(l); l->parent = this; } +}; + +typedef QList ListLines; + class ContentDiff : public AutomateCommand { Q_OBJECT @@ -56,6 +78,7 @@ private: private: void parseOutput(AutomateCommand*); DiffParser * diffParser; + ListLines lines; }; #endif ============================================================ --- guitone/src/view/InventoryView.cpp 64145819868bf2bc34916de61f252985eb0838e0 +++ guitone/src/view/InventoryView.cpp a9729726a253d1b035732e56347949c2c0434b2e @@ -24,6 +24,7 @@ #include "../model/InventoryItem.h" #include "../monotone/Monotone.h" #include "dialogs/FileDiff.h" +#include "dialogs/RevisionDiff.h" #include #include @@ -134,6 +135,7 @@ void InventoryView::contextMenuEvent(QCo if (item->hasChangedRecursive()) { menu.addAction(actCommit); + menu.addAction(actRevisionDiff); } if (item->hasNotStatus(InventoryItem::Ignored) && @@ -147,12 +149,11 @@ void InventoryView::contextMenuEvent(QCo { menu.addAction(actRevert); - // added files and directories can't be diffed if (item->hasNotStatus(InventoryItem::Added) && !item->isDirectory()) { - menu.addAction(actDiff); - } + menu.addAction(actFileDiff); + } } } @@ -174,12 +175,12 @@ void InventoryView::contextMenuEvent(QCo if (item->hasStatus(InventoryItem::Patched) && item->hasNotStatus(InventoryItem::Added)) { - actDiff->setFont(activeFont); + actFileDiff->setFont(activeFont); actOpen->setFont(normalFont); } else { - actDiff->setFont(normalFont); + actFileDiff->setFont(normalFont); actOpen->setFont(activeFont); } } @@ -249,9 +250,13 @@ void InventoryView::createAndConnectCont actRevert->setStatusTip(tr("Revert uncommitted changes")); connect(actRevert, SIGNAL(triggered()), this, SLOT(slotRevert())); - actDiff = new QAction(tr("D&iff"), this); - actDiff->setStatusTip(tr("Diff against base revision")); - connect(actDiff, SIGNAL(triggered()), this, SLOT(slotDiff())); + actFileDiff = new QAction(tr("D&iff"), this); + actFileDiff->setStatusTip(tr("Diff against base revision")); + connect(actFileDiff, SIGNAL(triggered()), this, SLOT(slotFileDiff())); + + actRevisionDiff = new QAction(tr("D&iff all"), this); + actRevisionDiff->setStatusTip(tr("Show all differences")); + connect(actRevisionDiff, SIGNAL(triggered()), this, SLOT(slotRevisionDiff())); actRename = new QAction(tr("Rena&me"), this); actRename->setStatusTip(tr("Rename file")); @@ -374,7 +379,7 @@ void InventoryView::slotUnignore(void) qDebug("InventoryView::slotUnignore!!!"); } -void InventoryView::slotDiff(void) +void InventoryView::slotFileDiff(void) { QModelIndex index(getSingleSelection()); if (!index.isValid()) return; @@ -383,7 +388,7 @@ void InventoryView::slotDiff(void) if (item->isDirectory() || !item->hasStatus(InventoryItem::Patched)) { - qDebug("InventoryView::slotDiff: File is a directory or not patched/unknown."); + qDebug("InventoryView::slotFileDiff: File is a directory or not patched/unknown."); return; } @@ -393,6 +398,20 @@ void InventoryView::slotDiff(void) clearSelection(); } +void InventoryView::slotRevisionDiff(void) +{ + QModelIndex index(getSingleSelection()); + if (!index.isValid()) return; + + InventoryItem * item = static_cast(index.internalPointer()); + + RevisionDiff dlg(this); + dlg.init(item->getPath(), QString(), QString()); + dlg.exec(); + + clearSelection(); +} + QModelIndex InventoryView::getSingleSelection() { QItemSelectionModel *selectionModel = this->selectionModel(); @@ -432,7 +451,7 @@ void InventoryView::itemClicked(const QM if (item->hasStatus(InventoryItem::Patched) && item->hasNotStatus(InventoryItem::Added)) { - slotDiff(); + slotFileDiff(); return; } ============================================================ --- guitone/src/view/InventoryView.h e27d8be7c798a13d350783fdaf5b38bc85b6e6bc +++ guitone/src/view/InventoryView.h 4be916c896d8a6131f74b44eef8b4fb1a78cfdde @@ -58,7 +58,8 @@ private: QAction *actUnignore; QAction *actRevert; QAction *actRename; - QAction *actDiff; + QAction *actFileDiff; + QAction *actRevisionDiff; Type type; @@ -75,7 +76,8 @@ private slots: void slotUnignore(void); void slotRevert(void); void slotRename(void); - void slotDiff(void); + void slotFileDiff(void); + void slotRevisionDiff(void); }; #endif