# # # patch "guitone/res/forms/commit_revision.ui" # from [258d991be703a11e4ef344c8fc8f71d7a7bba0c9] # to [c91ce1af1162eac46c2c489763cd6738da0f9a1e] # # patch "guitone/src/model/GetRevision.cpp" # from [9ced226a036d9bf560f8b2bc7c2bd90d3d916b40] # to [f6421a3436ec2f167ed088d707584c9339115902] # # patch "guitone/src/model/GetRevision.h" # from [9cb280cfdc594b15059acce545e9c0a90a8de9d4] # to [6d277716bb8d73b698e0106d281a1cf626cf01bb] # # patch "guitone/src/view/dialogs/CommitRevision.cpp" # from [7e11a18bb2cfcfd540aeb08139768b216fa21874] # to [b9af9c76a9a56da8ec92fa06fe3a10b2a495046c] # # patch "guitone/src/view/dialogs/CommitRevision.h" # from [e8c3e1b8d4b3012c7c49c4128a4d9de6ffccd518] # to [9dfc94422ee757d0387ad5c82421dc3584bad2bc] # ============================================================ --- guitone/res/forms/commit_revision.ui 258d991be703a11e4ef344c8fc8f71d7a7bba0c9 +++ guitone/res/forms/commit_revision.ui c91ce1af1162eac46c2c489763cd6738da0f9a1e @@ -52,6 +52,12 @@ QAbstractItemView::ExtendedSelection + + false + + + false + @@ -376,21 +382,5 @@ - - previousChangelogEntryList - currentIndexChanged(QString) - changelogEntry - setPlainText(QString) - - - 308 - 376 - - - 293 - 322 - - - ============================================================ --- guitone/src/model/GetRevision.cpp 9ced226a036d9bf560f8b2bc7c2bd90d3d916b40 +++ guitone/src/model/GetRevision.cpp f6421a3436ec2f167ed088d707584c9339115902 @@ -21,12 +21,13 @@ #include "GetRevision.h" #include "InventoryItem.h" #include "Monotone.h" -#include "BasicIOParser.h" -#include +#include +#include +#include GetRevision::GetRevision(QObject *parent) - : QStandardItemModel(parent) + : QAbstractItemModel(parent) { mtnDelegate = new MonotoneDelegate(this); } @@ -38,7 +39,8 @@ bool GetRevision::readRevision(const QSt bool GetRevision::readRevision(const QString & rev) { - clear(); + oldRevision = QString(); + changelist.clear(); QStringList cmd; cmd << "get_revision"; @@ -56,17 +58,12 @@ void GetRevision::parseOutput() Q_ASSERT(parser.parse()); StanzaList list = parser.getStanzas(); - setHorizontalHeaderLabels(QStringList() << tr("Changes")); - QMap changemap; - for (int i=0, size = list.size(); i < size; ++i) { Stanza stanza = list.at(i); + Change change; + bool found_change = false; - QString type; - QString data; - QString data2; - for (int j=0, size2 = stanza.size(); j < size2; j++) { StanzaEntry entry = stanza.at(j); @@ -84,149 +81,197 @@ void GetRevision::parseOutput() break; } - // we're only interested in real changeset entries - if (j == 0 && (entry.sym == "new_manifest" || entry.sym == "old_revision")) - break; + // the calculated manifest is useless if we select parts of the rev + if (j == 0 && entry.sym == "new_manifest") break; - if (entry.sym == "delete") + if (entry.sym == "old_revision") { Q_ASSERT(entry.vals.size() == 1); - type = "delete"; - data = entry.vals.at(0); + oldRevision = entry.vals.at(0); break; } - if (entry.sym == "rename") + found_change = true; + + if (entry.sym == "delete") { Q_ASSERT(entry.vals.size() == 1); - type = "rename"; - data = entry.vals.at(0); - continue; + change.type = Delete; + change.stanza = stanza; + break; } - if (entry.sym == "to" && type == "rename") + if (entry.sym == "rename") { Q_ASSERT(entry.vals.size() == 1); - data = tr("%1 to %2").arg(data).arg(entry.vals.at(0)); + change.type = Rename; + change.stanza = stanza; break; } if (entry.sym == "add_dir") { Q_ASSERT(entry.vals.size() == 1); - type = "add_dir"; - data = entry.vals.at(0); + change.type = AddDir; + change.stanza = stanza; break; } if (entry.sym == "add_file") { Q_ASSERT(entry.vals.size() == 1); - type ="add_file"; - data = entry.vals.at(0); - // we're not interested in the FILEID here + change.type = AddFile; + change.stanza = stanza; break; } if (entry.sym == "patch") { Q_ASSERT(entry.vals.size() == 1); - type = "patch"; - data = entry.vals.at(0); - // we're not interested in the old/new FILEID here + change.type = Patch; + change.stanza = stanza; break; } if (entry.sym == "clear") { Q_ASSERT(entry.vals.size() == 1); - type = "clear"; - data = entry.vals.at(0); - continue; - } - - if (entry.sym == "attr" && type == "clear") - { - Q_ASSERT(entry.vals.size() == 1); - data = tr("'%1' from %2").arg(entry.vals.at(0)).arg(data); + change.type = AttrClear; + change.stanza = stanza; break; } if (entry.sym == "set") { Q_ASSERT(entry.vals.size() == 1); - type = "set"; - data = entry.vals.at(0); - continue; - } - - if (entry.sym == "attr" && type == "set") - { - Q_ASSERT(entry.vals.size() == 1); - data2 = entry.vals.at(0); - continue; - } - - if (entry.sym == "value" && type == "set") - { - Q_ASSERT(entry.vals.size() == 1); - data = tr("'%1' to '%2' for %3").arg(entry.vals.at(0)).arg(data2).arg(data); + change.type = AttrSet; + change.stanza = stanza; break; } + found_change = false; + qWarning("GetRevision::parseOutput(): Unknown symbol %s.", qPrintable(entry.sym)); } // check if we really processed an item entry - if (type.size() == 0) continue; + if (!found_change) continue; - if (!changemap.contains(type)) - { - changemap.insert(type, QStringList()); - } - - changemap[type].append(data); + changelist.append(change); } - QStandardItem * parent = invisibleRootItem(); + // reset the view + reset(); - // FIXME: we should create QStandardItems right from the start and - // store references to the original Stanza's as data part - // to make it possible to commit partial changes - foreach (QString key, changemap.keys()) + // signal that we've finished (whoever listens to that) + emit revisionRead(); +} + +QString GetRevision::getRevisionFromSelection(const QModelIndexList & indexes) +{ + QString dat; + QTextStream stream(&dat); + + stream << "format_version \"1\"\n\n"; + stream << "old_revision [" << oldRevision << "]\n\n"; + stream << "new_manifest [" << QString().fill('0', 40) << "]\n\n"; + + foreach (QModelIndex index, indexes) { - QString label; - if (key == "delete") - label = tr("deleted entries"); - else if (key == "renamed") - label = tr("renamed entries"); - else if (key == "add_dir") - label = tr("added directories"); - else if (key == "add_file") - label = tr("added files"); - else if (key == "patch") - label = tr("patched files"); - else if (key == "clear") - label = tr("removed attributes"); - else if (key == "set") - label = tr("added attributes"); - else - Q_ASSERT(false); - - QStandardItem * type = new QStandardItem(label); - parent->appendRow(type); - - foreach (QString data, changemap.value(key)) + Change * change = static_cast(index.internalPointer()); + stream << change->getStanzaData() << "\n\n"; + } + + return dat; +} + +int GetRevision::columnCount(const QModelIndex &parent) const +{ + return 2; +} + +QVariant GetRevision::data(const QModelIndex & index, int role) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + int row = index.row(); + if (row >= changelist.size()) return QVariant(); + + Change change(changelist.at(row)); + + if (role == Qt::DisplayRole) + { + switch (index.column()) { - QStandardItem * entry = new QStandardItem(data); - type->appendRow(entry); + case 0: return QVariant(change.getTypeString()); + case 1: return QVariant(change.getDisplayData()); + default: Q_ASSERT(false); } } + else + if (role == Qt::BackgroundRole) + { + if (index.column() == 0) + { + return QVariant(QBrush(change.getTypeColor())); + } + return QVariant(); + } + else + if (role == Qt::FontRole && index.column() == 0) + { + QFont font; + font.setPointSize(8); + return QVariant(font); + } + else + if (role == Qt::TextAlignmentRole && index.column() == 0) + { + return QVariant(Qt::AlignCenter); + } + return QVariant(); +} + +Qt::ItemFlags GetRevision::flags(const QModelIndex &index) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QVariant GetRevision::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole) + { + switch (section) + { + case 0: return QVariant(tr("Type")); + case 1: return QVariant(tr("Change")); + } + } + + return QVariant(); +} + +int GetRevision::rowCount(const QModelIndex & parent) const +{ + return changelist.size(); +} + +QModelIndex GetRevision::index(int row, int column, const QModelIndex & parent) const +{ + if (!hasIndex(row, column, parent)) + { + return QModelIndex(); + } - // reset the view - reset(); - - // signal that we've finished (whoever listens to that) - emit revisionRead(); + Change * change = new Change(changelist.at(row)); + return createIndex(row, column, change); } +QModelIndex GetRevision::parent(const QModelIndex& index) const +{ + return QModelIndex(); +} + ============================================================ --- guitone/src/model/GetRevision.h 9cb280cfdc594b15059acce545e9c0a90a8de9d4 +++ guitone/src/model/GetRevision.h 6d277716bb8d73b698e0106d281a1cf626cf01bb @@ -23,16 +23,137 @@ #include "AutomateCommand.h" #include "MonotoneDelegate.h" +#include "BasicIOParser.h" -#include +#include +enum ChangeType { Delete, Rename, AddDir, AddFile, Patch, AttrSet, AttrClear }; -class GetRevision : public QStandardItemModel, public AutomateCommand +struct Change { + ChangeType type; + Stanza stanza; + + inline QString getTypeString() + { + switch (type) + { + case Delete: return QObject::tr("Delete"); + case Rename: return QObject::tr("Rename"); + case AddDir: return QObject::tr("Dir add"); + case AddFile: return QObject::tr("File add"); + case Patch: return QObject::tr("Patch"); + case AttrSet: return QObject::tr("Attr set"); + case AttrClear: return QObject::tr("Attr clear"); + } + return QString(); + } + + inline Qt::GlobalColor getTypeColor() + { + switch (type) + { + case Delete: return Qt::red; + case Rename: return Qt::gray; + case AddDir: return Qt::darkGreen; + case AddFile: return Qt::green; + case Patch: return Qt::yellow; + case AttrSet: return Qt::cyan; + case AttrClear: return Qt::magenta; + } + return Qt::transparent; + } + + inline QString escape(const QString & in) + { + QString out(in); + out.replace("\\", "\\\\"); + out.replace("\"", "\\\""); + return out; + } + + inline QString getStanzaData() + { + switch (type) + { + case AddDir: + return QString("add_dir \"%1\"") + .arg(escape(stanza.at(0).vals.at(0))); + case AddFile: + return QString("add_file \"%1\"\ncontent [%2]") + .arg(escape(stanza.at(0).vals.at(0))) + .arg(stanza.at(1).vals.at(0)); + case Delete: + return QString("delete \"%1\"") + .arg(escape(stanza.at(0).vals.at(0))); + case Patch: + return QString("patch \"%1\"\nfrom [%2]\nto [%3]") + .arg(escape(stanza.at(0).vals.at(0))) + .arg(stanza.at(1).vals.at(0)) + .arg(stanza.at(2).vals.at(0)); + case Rename: + return QString("rename \"%1\"\nto \"%2\"") + .arg(escape(stanza.at(0).vals.at(0))) + .arg(escape(stanza.at(1).vals.at(0))); + case AttrClear: + return QObject::tr("clear \"%1\"\nattr \"%2\"") + .arg(escape(stanza.at(0).vals.at(0))) + .arg(escape(stanza.at(1).vals.at(0))); + case AttrSet: + return QObject::tr("set \"%1\"\nattr \"%2\"\nvalue \"%3\"") + .arg(stanza.at(0).vals.at(0)) + .arg(stanza.at(1).vals.at(0)) + .arg(stanza.at(2).vals.at(0)); + } + return QString(); + } + + inline QString getDisplayData() + { + switch (type) + { + case AddDir: + case AddFile: + case Patch: + case Delete: + return stanza.at(0).vals.at(0); + case Rename: + return QObject::tr("%1 to %2") + .arg(stanza.at(0).vals.at(0)) + .arg(stanza.at(1).vals.at(0)); + case AttrClear: + return QObject::tr("'%1' from %2") + .arg(stanza.at(1).vals.at(0)) + .arg(stanza.at(0).vals.at(0)); + case AttrSet: + return QObject::tr("'%1' to '%2' for %3") + .arg(stanza.at(1).vals.at(0)) + .arg(stanza.at(2).vals.at(0)) + .arg(stanza.at(0).vals.at(0)); + } + return QString(); + } +}; + +typedef QList ChangeList; + + +class GetRevision : public QAbstractItemModel, public AutomateCommand { Q_OBJECT public: - GetRevision(QObject*); + GetRevision(QObject *); virtual ~GetRevision(); + + QString getRevisionFromSelection(const QModelIndexList &); + + // needed Qt Model methods + QVariant data(const QModelIndex &, int) const; + Qt::ItemFlags flags(const QModelIndex &) const; + QVariant headerData(int, Qt::Orientation, int) const; + QModelIndex index(int, int, const QModelIndex &) const; + QModelIndex parent(const QModelIndex &) const; + int rowCount(const QModelIndex &) const; + int columnCount(const QModelIndex &) const; public slots: bool readRevision(const QString &); @@ -42,7 +163,10 @@ private: private: void parseOutput(); + MonotoneDelegate * mtnDelegate; + ChangeList changelist; + QString oldRevision; }; #endif ============================================================ --- guitone/src/view/dialogs/CommitRevision.cpp 7e11a18bb2cfcfd540aeb08139768b216fa21874 +++ guitone/src/view/dialogs/CommitRevision.cpp b9af9c76a9a56da8ec92fa06fe3a10b2a495046c @@ -21,9 +21,11 @@ #include "CommitRevision.h" #include "Monotone.h" #include "Settings.h" +#include "Guitone.h" #include #include +#include CommitRevision::CommitRevision(QWidget* parent) : Dialog(parent) { @@ -33,11 +35,20 @@ CommitRevision::CommitRevision(QWidget* // OSX sheet-alike dialog setWindowFlags(Qt::Sheet); - previousChangelogEntryList->addItems(Settings::getItemList("ChangelogEntries")); + QStringList entries = Settings::getItemList("ChangelogEntries"); + QRegExp re("\\s+"); + for (int i=0, j=entries.size(); iinsertItem(i, shortened, QVariant(entry)); + } + revModel = new GetRevision(this); changeView->setModel(revModel); - changeView->header()->hide(); revModel->readRevision(QString()); connect( @@ -49,6 +60,14 @@ CommitRevision::CommitRevision(QWidget* invertSelection, SIGNAL(clicked()), this, SLOT(invertChangesetSelection()) ); + + connect( + previousChangelogEntryList, SIGNAL(currentIndexChanged(int)), + this, SLOT(setChangelogEntryFromList(int)) + ); + + // initialize the text view with the most recent entry + setChangelogEntryFromList(0); } CommitRevision::~CommitRevision() @@ -56,10 +75,22 @@ CommitRevision::~CommitRevision() delete revModel; } +void CommitRevision::setChangelogEntryFromList(int index) +{ + changelogEntry->setPlainText( + previousChangelogEntryList->itemData(index).toString() + ); +} + void CommitRevision::accept() { + QString newRev = revModel->getRevisionFromSelection( + changeView->selectionModel()->selectedRows(1) + ); + + D(QString("CommitRevision::accept: new revision to commit:\n%1").arg(newRev)); + Settings::addItemToList("ChangelogEntries", changelogEntry->toPlainText(), 10); - qDebug("CommitRevision::accept: TODO: commit revision"); done(0); } @@ -68,34 +99,20 @@ void CommitRevision::invertChangesetSele QItemSelectionModel * selectionModel = changeView->selectionModel(); QModelIndex parent; - QModelIndex topLeft = revModel->index(0, 0, parent); QModelIndex bottomRight = revModel->index( revModel->rowCount(parent)-1, revModel->columnCount(parent)-1, parent ); - QItemSelection selection(topLeft, bottomRight); - // FIXME: this does not work in trees with more than two levels - foreach(QModelIndex parent, selection.indexes()) - { - topLeft = revModel->index(0, 0, parent); - bottomRight = revModel->index( - revModel->rowCount(parent)-1, - revModel->columnCount(parent)-1, - parent - ); - selection.merge(QItemSelection(topLeft, bottomRight), QItemSelectionModel::Select); - } - selectionModel->select(selection, QItemSelectionModel::Toggle); } void CommitRevision::checkForChanges() { - if (revModel->rowCount() == 0) + if (revModel->rowCount(QModelIndex()) == 0) { QMessageBox::information( this, ============================================================ --- guitone/src/view/dialogs/CommitRevision.h e8c3e1b8d4b3012c7c49c4128a4d9de6ffccd518 +++ guitone/src/view/dialogs/CommitRevision.h 9dfc94422ee757d0387ad5c82421dc3584bad2bc @@ -38,6 +38,7 @@ private slots: private slots: void invertChangesetSelection(); + void setChangelogEntryFromList(int); void checkForChanges(); void accept(); };