# # # patch "guitone/res/forms/commit_revision.ui" # from [c91ce1af1162eac46c2c489763cd6738da0f9a1e] # to [c81cdb40189d873524fd833f832a700006a5394b] # # patch "guitone/src/Guitone.cpp" # from [df2c6d9a3e2178dc9d17c9b15f608d0d15188c13] # to [429342eb3c190c0350629dfa1d822848a100e3b3] # # patch "guitone/src/model/GetRevision.cpp" # from [f6421a3436ec2f167ed088d707584c9339115902] # to [3fe7773819ed4ef94ca0bb470b5caf4b2d33caa5] # # patch "guitone/src/model/GetRevision.h" # from [6d277716bb8d73b698e0106d281a1cf626cf01bb] # to [e6d1d83047029447b8e9d5fb338cf72877458c46] # # patch "guitone/src/monotone/Monotone.cpp" # from [b3a10f1cb905f11321c0ef7abcdab6eefcb659f3] # to [c022744d0e1f616a544f8702e9bb4ff2b72a4dc6] # # patch "guitone/src/monotone/Monotone.h" # from [8e3e6a9750e2ce4b0c5c42fa20d13a505eb43015] # to [8ed8e4851e892adfd5c8d445ff798f39a6a0019b] # # patch "guitone/src/monotone/MonotoneDelegate.cpp" # from [0986e7b2551f4ccf87bcecc7da446cb417514d84] # to [aff2e9c2aa1b91433f4d50d29c58b48ed4a45844] # # patch "guitone/src/view/MainWindow.cpp" # from [fbf63888c531297f7941918ebeb4ee6de9f1ed5c] # to [3d713da4f0ef02fb7a34fd7839cfc67d7b4a1876] # # patch "guitone/src/view/MainWindow.h" # from [a6eec6cd24f15814a4f00dc46cb458432930a2f1] # to [1d9646474da2a667ec9b56ce3f30a8fc933b6969] # # patch "guitone/src/view/dialogs/CommitRevision.cpp" # from [b9af9c76a9a56da8ec92fa06fe3a10b2a495046c] # to [e796042390241526ff2848e24f0bebd8b432dc9c] # # patch "guitone/src/view/dialogs/CommitRevision.h" # from [9dfc94422ee757d0387ad5c82421dc3584bad2bc] # to [7d02ef0a24613fc88485823db77d5a9cfdbc6b35] # ============================================================ --- guitone/res/forms/commit_revision.ui c91ce1af1162eac46c2c489763cd6738da0f9a1e +++ guitone/res/forms/commit_revision.ui c81cdb40189d873524fd833f832a700006a5394b @@ -5,8 +5,8 @@ 0 0 - 423 - 522 + 468 + 565 @@ -48,6 +48,26 @@ 6 + + + 0 + + + 6 + + + + + Display changes against parent + + + + + + + + + QAbstractItemView::ExtendedSelection ============================================================ --- guitone/src/Guitone.cpp df2c6d9a3e2178dc9d17c9b15f608d0d15188c13 +++ guitone/src/Guitone.cpp 429342eb3c190c0350629dfa1d822848a100e3b3 @@ -250,12 +250,6 @@ bool Guitone::addMonotoneInstance(MainWi bool Guitone::addMonotoneInstance(MainWindow * wnd) { Monotone * mtn = new Monotone(wnd); - - // catch critical errors from the Monotone class - connect( - mtn, SIGNAL(criticalError(const QString &)), - wnd, SLOT(criticalMtnError(const QString &)) - ); // check the current monotone version and prompt the user // to enter the correct path if there is a problem @@ -295,11 +289,6 @@ bool Guitone::removeMonotoneInstance(Mai Monotone * mtn = monotoneInstances.value(wnd); - disconnect( - mtn, SIGNAL(criticalError(const QString &)), - wnd, SLOT(criticalMtnError(const QString &)) - ); - delete mtn; monotoneInstances.remove(wnd); ============================================================ --- guitone/src/model/GetRevision.cpp f6421a3436ec2f167ed088d707584c9339115902 +++ guitone/src/model/GetRevision.cpp 3fe7773819ed4ef94ca0bb470b5caf4b2d33caa5 @@ -39,8 +39,7 @@ bool GetRevision::readRevision(const QSt bool GetRevision::readRevision(const QString & rev) { - oldRevision = QString(); - changelist.clear(); + revision.clear(); QStringList cmd; cmd << "get_revision"; @@ -56,7 +55,9 @@ void GetRevision::parseOutput() { BasicIOParser parser(AutomateCommand::data); Q_ASSERT(parser.parse()); + StanzaList list = parser.getStanzas(); + QString currentRevision; for (int i=0, size = list.size(); i < size; ++i) { @@ -81,13 +82,19 @@ void GetRevision::parseOutput() break; } - // the calculated manifest is useless if we select parts of the rev - if (j == 0 && entry.sym == "new_manifest") break; + if (j == 0 && entry.sym == "new_manifest") + { + Q_ASSERT(entry.vals.size() == 1); + revision.new_manifest = entry.vals.at(0); + break; + } if (entry.sym == "old_revision") { Q_ASSERT(entry.vals.size() == 1); - oldRevision = entry.vals.at(0); + currentRevision = entry.vals.at(0); + QList changelist; + revision.changesAgainstParent.insert(currentRevision, changelist); break; } @@ -156,10 +163,13 @@ void GetRevision::parseOutput() // check if we really processed an item entry if (!found_change) continue; - - changelist.append(change); + Q_ASSERT(revision.changesAgainstParent.contains(currentRevision)); + revision.changesAgainstParent[currentRevision].append(change); } + // set the first as the default parent against which changes are displayed + changesAgainstParent = revision.changesAgainstParent.keys().at(0); + // reset the view reset(); @@ -167,24 +177,6 @@ void GetRevision::parseOutput() 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) - { - Change * change = static_cast(index.internalPointer()); - stream << change->getStanzaData() << "\n\n"; - } - - return dat; -} - int GetRevision::columnCount(const QModelIndex &parent) const { return 2; @@ -192,15 +184,14 @@ QVariant GetRevision::data(const QModelI QVariant GetRevision::data(const QModelIndex & index, int role) const { - if (!index.isValid()) - { + if (!index.isValid()) return QVariant(); - } + + if (!revision.changesAgainstParent.contains(changesAgainstParent)) + return QVariant(); - int row = index.row(); - if (row >= changelist.size()) return QVariant(); - - Change change(changelist.at(row)); + Change change = + revision.changesAgainstParent[changesAgainstParent].at(index.row()); if (role == Qt::DisplayRole) { @@ -256,18 +247,21 @@ int GetRevision::rowCount(const QModelIn int GetRevision::rowCount(const QModelIndex & parent) const { - return changelist.size(); + if (!revision.changesAgainstParent.contains(changesAgainstParent)) + return 0; + + return revision.changesAgainstParent[changesAgainstParent].size(); } QModelIndex GetRevision::index(int row, int column, const QModelIndex & parent) const { - if (!hasIndex(row, column, parent)) - { + if (!revision.changesAgainstParent.contains(changesAgainstParent)) return QModelIndex(); - } - Change * change = new Change(changelist.at(row)); - return createIndex(row, column, change); + if (row >= revision.changesAgainstParent[changesAgainstParent].size()) + return QModelIndex(); + + return createIndex(row, column, NULL); } QModelIndex GetRevision::parent(const QModelIndex& index) const @@ -275,3 +269,18 @@ QModelIndex GetRevision::parent(const QM return QModelIndex(); } +QStringList GetRevision::getParentRevisions() const +{ + return revision.changesAgainstParent.keys(); +} + +void GetRevision::showChangesAgainstParent(const QString & parent) +{ + QStringList parents = getParentRevisions(); + + if (!parents.contains(parent)) return; + + changesAgainstParent = parent; + reset(); +} + ============================================================ --- guitone/src/model/GetRevision.h 6d277716bb8d73b698e0106d281a1cf626cf01bb +++ guitone/src/model/GetRevision.h e6d1d83047029447b8e9d5fb338cf72877458c46 @@ -134,8 +134,17 @@ struct Change { } }; -typedef QList ChangeList; +struct Revision +{ + QString new_manifest; + QMap > changesAgainstParent; + inline void clear() + { + new_manifest = QString(); + changesAgainstParent.clear(); + } +}; class GetRevision : public QAbstractItemModel, public AutomateCommand { @@ -143,9 +152,8 @@ public: public: GetRevision(QObject *); virtual ~GetRevision(); + QStringList getParentRevisions() const; - QString getRevisionFromSelection(const QModelIndexList &); - // needed Qt Model methods QVariant data(const QModelIndex &, int) const; Qt::ItemFlags flags(const QModelIndex &) const; @@ -157,7 +165,8 @@ public slots: public slots: bool readRevision(const QString &); - + void showChangesAgainstParent(const QString &); + signals: void revisionRead(); @@ -165,8 +174,8 @@ private: void parseOutput(); MonotoneDelegate * mtnDelegate; - ChangeList changelist; - QString oldRevision; + Revision revision; + QString changesAgainstParent; }; #endif ============================================================ --- guitone/src/monotone/Monotone.cpp b3a10f1cb905f11321c0ef7abcdab6eefcb659f3 +++ guitone/src/monotone/Monotone.cpp c022744d0e1f616a544f8702e9bb4ff2b72a4dc6 @@ -101,6 +101,12 @@ Monotone::Monotone(QObject * parent) : Q commandCounter = -1; // true if the process is destroyed in the destructor isCleanExit = false; + // set to true to abort any running requests + // FIXME: we certainly need a better way to abort requested data than + // setting some flag which gets eventually evaluated or not + doAbortRequests = false; + // exec timeout waiter + timeout = false; // inialize the process var process = 0; // path to the monotone binary @@ -162,10 +168,11 @@ void Monotone::shutdownCurrentProcess() void Monotone::shutdownCurrentProcess() { - // if a previous process is running, exit it cleanly + // if a previous process is running, exit it cleanly if (process) { isCleanExit = true; + doAbortRequests = true; completedCommands.clear(); commandCounter = -1; @@ -202,13 +209,16 @@ void Monotone::shutdownCurrentProcess() } } -void Monotone::setupNewProcess() +bool Monotone::setupNewProcess() { Q_ASSERT(mtnBinaryPath.size() > 0); shutdownCurrentProcess(); isCleanExit = false; + doAbortRequests = false; + timeout = false; + process = new QProcess(this); // monitor if the process is exited unexpectedly @@ -252,16 +262,36 @@ void Monotone::setupNewProcess() D(QString("starting %1 %2").arg(mtnBinaryPath).arg(args.join(" "))); process->start(mtnBinaryPath, args); + + if (!process->waitForStarted(WaitForMonotoneStart)) + { + C("Timed out waiting for monotone start"); + return false; + } + + timer = startTimer(1000); + + while (!timeout && !doAbortRequests) + QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); + + if (!timeout) killTimer(timer); + + return !doAbortRequests; } +void Monotone::timerEvent(QTimerEvent * event) +{ + killTimer(timer); + timeout = true; +} + bool Monotone::loadWorkspace(QString workspace) { // try to find the _MTN directory in the given path and chdir to the root if (!normalizeWorkspacePath(workspace)) return false; workDir = workspace; mode = Workspace; - setupNewProcess(); - return true; + return setupNewProcess(); } bool Monotone::loadDatabase(QString db) @@ -270,8 +300,7 @@ bool Monotone::loadDatabase(QString db) // escalate later on if something goes wrong, e.g. file is not writable...? databaseFile = db; mode = Database; - setupNewProcess(); - return true; + return setupNewProcess(); } void Monotone::processError(QProcess::ProcessError error) @@ -316,18 +345,15 @@ void Monotone::processFinished(int code, { // this was a normal exit if (code == 0 || isCleanExit) return; - - QString msg(process->readAllStandardError()); - emit criticalError(tr( - "The monotone process exited unexpectedly (return code %1). Please " - "reconfigure the path to the monotone binary in the Preferences dialog " - "or check if the version of the database you try to load matches the " - "monotone version you are using.\n\n" - "monotone returned:\n%2" - ).arg(code).arg(stripMtnPrefix(msg)) - ); + doAbortRequests = true; } +QString Monotone::getStderr() +{ + QString output(process->readAllStandardError()); + return stripMtnPrefix(output); +} + bool Monotone::executeCommand(const QStringList & command, int & commandNumber) { QStringList opts; @@ -336,8 +362,7 @@ bool Monotone::executeCommand(const QStr bool Monotone::executeCommand(const QStringList & command, const QStringList & options, int & commandNumber) { - if (process->state() != QProcess::Running && - !process->waitForStarted(WaitForMonotoneStart)) + if (doAbortRequests || process->state() != QProcess::Running) { D("monotone is not running"); return false; @@ -348,11 +373,11 @@ bool Monotone::executeCommand(const QStr do { - waiter.wait(500); + waiter.wait(100); } - while (!isCommandFinished(commandNumber)); + while (!doAbortRequests && !isCommandFinished(commandNumber)); - return true; + return !doAbortRequests; } bool Monotone::triggerCommand(const QStringList & command, int & commandNumber) @@ -362,8 +387,7 @@ bool Monotone::triggerCommand(const QStr bool Monotone::triggerCommand(const QStringList & command, const QStringList & options, int & commandNumber) { - if (process->state() != QProcess::Running && - !process->waitForStarted(WaitForMonotoneStart)) + if (doAbortRequests || process->state() != QProcess::Running) { D("monotone is not running"); return false; @@ -626,7 +650,7 @@ QString Monotone::stripMtnPrefix(const Q // FIXME: we should actually use basename(mtnBinaryPath) in case // the binary is renamed, but I'm too lazy for this right now... // and this would also require to make this method non-static - QRegExp rx = QRegExp("^(?:mtn\\:)?[^:]+\\:[ ]*(.+)"); + QRegExp rx = QRegExp("^(?:mtn\\:[ ]+)?[^:]+\\:[ ]*(.+)"); for (int i=0, j=list.size(); i output; QMap completedCommands; int commandCounter; bool isCleanExit; + bool doAbortRequests; QProcess * process; QString workDir; QString databaseFile; QString mtnBinaryPath; QMutex lock; + + int timer; + bool timeout; private slots: void readAndParseStdout(); @@ -87,7 +93,6 @@ class Monotone : public QObject signals: void commandFinished(); - void criticalError(const QString &); }; #endif ============================================================ --- guitone/src/monotone/MonotoneDelegate.cpp 0986e7b2551f4ccf87bcecc7da446cb417514d84 +++ guitone/src/monotone/MonotoneDelegate.cpp aff2e9c2aa1b91433f4d50d29c58b48ed4a45844 @@ -50,6 +50,8 @@ bool MonotoneDelegate::triggerCommand(co return mtn->triggerCommand(cmd, opts, commandNumber); } +// FIXME: if a request is aborted, commandFinished is never signalled, +// do we need to take care of that here? void MonotoneDelegate::commandFinished() { QObject * obj = dynamic_cast(cmdModel); ============================================================ --- guitone/src/view/MainWindow.cpp fbf63888c531297f7941918ebeb4ee6de9f1ed5c +++ guitone/src/view/MainWindow.cpp 3d713da4f0ef02fb7a34fd7839cfc67d7b4a1876 @@ -158,17 +158,6 @@ MainWindow::~MainWindow() delete proxyModelFileList; } -void MainWindow::criticalMtnError(const QString & msg) -{ - // restore the normal cursor - APP->restoreOverrideCursor(); - - QMessageBox::critical(this, tr("Critical Monotone Error"), - msg, QMessageBox::Ok, 0, 0); - - close(); -} - // try to load the most recent workspace or database, if there are any // if everything fails, load nothing and hide the appropriate menus void MainWindow::loadRecent() @@ -210,8 +199,9 @@ bool MainWindow::doLoadWorkspace(QString { QMessageBox::critical( this, - tr("Invalid workspace"), - tr("The chosen directory is no monotone workspace!"), + tr("Failed to load workspace"), + tr("The workspace could not be loaded.\n" + "The last output was:\n\n%1").arg(mtn->getStderr()), QMessageBox::Ok ); @@ -273,12 +263,17 @@ bool MainWindow::doLoadDatabase(QString bool MainWindow::doLoadDatabase(QString fn) { - // FIXME: currently a database is always loaded, but not checked - // so this condition can actually never be false - if (!MTN(this)->loadDatabase(fn)) + Monotone * mtn = MTN(this); + if (!mtn->loadDatabase(fn)) { - qDebug("Could not load database."); - // remove the workspace if it was recorded as recent workspace + QMessageBox::critical( + this, + tr("Failed to load database"), + tr("The database could not be loaded.\n" + "The last output was:\n\n%1").arg(mtn->getStderr()), + QMessageBox::Ok + ); + Settings::removeItemFromList("RecentDatabaseList", fn); emit updatePreviousDatabasesMenu(); ============================================================ --- guitone/src/view/MainWindow.h a6eec6cd24f15814a4f00dc46cb458432930a2f1 +++ guitone/src/view/MainWindow.h 1d9646474da2a667ec9b56ce3f30a8fc933b6969 @@ -48,7 +48,6 @@ public slots: public slots: void doUpdatePreviousWorkspacesMenu(); void doUpdatePreviousDatabasesMenu(); - void criticalMtnError(const QString &); signals: void modeChanged(Mode); ============================================================ --- guitone/src/view/dialogs/CommitRevision.cpp b9af9c76a9a56da8ec92fa06fe3a10b2a495046c +++ guitone/src/view/dialogs/CommitRevision.cpp e796042390241526ff2848e24f0bebd8b432dc9c @@ -53,7 +53,7 @@ CommitRevision::CommitRevision(QWidget* connect( revModel, SIGNAL(revisionRead()), - this, SLOT(checkForChanges()) + this, SLOT(revisionRead()) ); connect( @@ -66,6 +66,11 @@ CommitRevision::CommitRevision(QWidget* this, SLOT(setChangelogEntryFromList(int)) ); + connect( + changesAgainstParent, SIGNAL(currentIndexChanged(const QString &)), + revModel, SLOT(showChangesAgainstParent(const QString &)) + ); + // initialize the text view with the most recent entry setChangelogEntryFromList(0); } @@ -84,12 +89,7 @@ void CommitRevision::accept() void CommitRevision::accept() { - QString newRev = revModel->getRevisionFromSelection( - changeView->selectionModel()->selectedRows(1) - ); - - D(QString("CommitRevision::accept: new revision to commit:\n%1").arg(newRev)); - + qDebug("CommitRevision::accept: do actual commit"); Settings::addItemToList("ChangelogEntries", changelogEntry->toPlainText(), 10); done(0); } @@ -110,7 +110,7 @@ void CommitRevision::invertChangesetSele selectionModel->select(selection, QItemSelectionModel::Toggle); } -void CommitRevision::checkForChanges() +void CommitRevision::revisionRead() { if (revModel->rowCount(QModelIndex()) == 0) { @@ -122,5 +122,7 @@ void CommitRevision::checkForChanges() ); reject(); } + + changesAgainstParent->addItems(revModel->getParentRevisions()); } ============================================================ --- guitone/src/view/dialogs/CommitRevision.h 9dfc94422ee757d0387ad5c82421dc3584bad2bc +++ guitone/src/view/dialogs/CommitRevision.h 7d02ef0a24613fc88485823db77d5a9cfdbc6b35 @@ -39,7 +39,7 @@ private slots: private slots: void invertChangesetSelection(); void setChangelogEntryFromList(int); - void checkForChanges(); + void revisionRead(); void accept(); };