# # # patch "i18n/guitone_de.ts" # from [79bc887d4ffadd3faba7d75ed3cc3ccec81e6c9e] # to [6be3e2fa47d6c27f9251ae59f70d35cde5bd8afe] # # patch "src/model/Monotone.cpp" # from [e0c6415cf076a4202bb07b1450ba8dc4c849f895] # to [4cc05b131cecb36322a3d7d429284b85a8bfd712] # # patch "src/model/Monotone.h" # from [56a26664cc846b890ac19ab47e9ce2e0a3686a94] # to [2d1520727dceebdf384cf8b6f93b44164917b119] # # patch "src/model/Workspace.cpp" # from [d9af7b5957edc7427aba2ce770b58693b668a155] # to [2da2321fcd0d8925d6a5438a86029e1411624c76] # # patch "src/view/Guitone.cpp" # from [b7382268b4b19157b7f3490d031f72bc601c2e1b] # to [613f05389f207af2e7a267b624a7d754e1d86e71] # # patch "src/view/Guitone.h" # from [8420bd69cc67c4cb78a9c4cc528bdf3bc0c4f15c] # to [8f614347750418fabecb935530e5037eda3b703d] # ============================================================ --- i18n/guitone_de.ts 79bc887d4ffadd3faba7d75ed3cc3ccec81e6c9e +++ i18n/guitone_de.ts 6be3e2fa47d6c27f9251ae59f70d35cde5bd8afe @@ -67,6 +67,10 @@ &Import Working Directory... &Importiere Arbeitsbereich + + Critical Monotone Error + + Monotone @@ -80,20 +84,28 @@ Process exited - Prozess beendet + Prozess beendet The internal connection to the monotone process was terminated (code %1, status %2) - Die interne Verbindung zum Monotone-Prozess wurde beendet (Code %1, Status %2) + Die interne Verbindung zum Monotone-Prozess wurde beendet (Code %1, Status %2) Monotone startup error - Fehler beim Starten von Monotone + Fehler beim Starten von Monotone Couldn't startup the monotone process (ProcessError %1). Have you installed it properly? - Konnte den monotone-Prozess nicht starten (ProcessError %1). Ist das Programm korrekt installiert? + Konnte den monotone-Prozess nicht starten (ProcessError %1). Ist das Programm korrekt installiert? + + Monotone failed to start. Have you installed it properly? + + + + The connection to the monotone process was terminated. + + SandboxItem ============================================================ --- src/model/Monotone.cpp e0c6415cf076a4202bb07b1450ba8dc4c849f895 +++ src/model/Monotone.cpp 4cc05b131cecb36322a3d7d429284b85a8bfd712 @@ -22,87 +22,75 @@ Monotone* Monotone::instance = 0; -Monotone::Monotone(QDir *workingDirectory) : QProcess() +Monotone::Monotone(QObject * parent) : QObject(parent), process(0) { isProcessingData = false; output = new QStringList(); - +} + +void Monotone::setup(QDir *workingDirectory) +{ + if (process) { delete process; process = 0; } + + process = new QProcess(this); + // read & parse mtn's output as soon as it gets available connect( - this, SIGNAL(readyReadStandardOutput()), + process, SIGNAL(readyReadStandardOutput()), this, SLOT(parseLineFromStdout()) ); - + // monitor if the process is exited unexpectedly connect( - this, SIGNAL(finished(int, QProcess::ExitStatus)), + process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processTerminated(int, QProcess::ExitStatus)) ); - + // check if there occurs an startup error connect( - this, SIGNAL(error(QProcess::ProcessError)), + process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(startupError(QProcess::ProcessError)) ); - - setWorkingDirectory(workingDirectory->absolutePath()); + + process->setWorkingDirectory(workingDirectory->absolutePath()); + QStringList args; args << "automate"; args << "stdio"; - + // Start up monotone's executable 'mtn' - start("mtn", args); + qDebug("Starting mtn..."); + process->start("mtn", args); + qDebug("Start returned."); } Monotone::~Monotone() { // terminate any running process - terminate(); + process->terminate(); + delete process; + process = 0; } -Monotone* Monotone::singleton(QDir *workingDirectory = NULL) +Monotone* Monotone::singleton(QObject * parent) { - // if we have no instance created yet we need a workspace directory - Q_ASSERT( !(workingDirectory == NULL && instance == 0) ); - - // create a new instance if there is either no instance - // or a new working directory has been explicitely given - if (instance == 0 || workingDirectory != NULL) - { - instance = new Monotone(workingDirectory); - } + // create a new instance if there is no instance + if (instance == 0) { instance = new Monotone(parent); } return instance; } -void Monotone::startupError(QProcess::ProcessError error) +void Monotone::startupError(QProcess::ProcessError /* error */) { - QMessageBox::critical( - NULL, - tr("Monotone startup error"), - tr("Couldn't startup the monotone process (ProcessError %1). Have you installed it properly?") - .arg(error), - QMessageBox::Ok, 0, 0 - ); - qApp->exit(1); + emit criticalError(tr("Monotone failed to start. Have you installed it properly?")); } -void Monotone::processTerminated(int code, QProcess::ExitStatus status) +void Monotone::processTerminated(int code, QProcess::ExitStatus /* status */) { // this was a normal exit if (code == 0) return; - - // TODO: log the error output somewhere or at least output it on guitone's STDERR - // QProcess' buffers are still intact at this point - QMessageBox::critical( - NULL, - tr("Process exited"), - tr("The internal connection to the monotone process was terminated (code %1, status %2)") - .arg(code) - .arg(status), - QMessageBox::Ok, 0, 0 - ); - qApp->exit(1); + + emit criticalError(tr("The connection to the monotone process was terminated.")); } bool Monotone::triggerCommand(QString cmd) @@ -110,26 +98,28 @@ // wait until the process has been started up if it is not already // running. Notice that if waitForStartup times out after 15 seconds // the error handler is called and the program is terminated! - if (state() != QProcess::Running && !waitForStarted(15000)) + if (process->state() != QProcess::Running && !process->waitForStarted(15000)) { + qDebug("QProcess is not running."); return false; } - + // check if we're already processing a command if (isProcessingData) { + qDebug("Process is already busy."); return false; } - + isProcessingData = true; output->clear(); - + QString finalCmd; QStringList parts = QStringList::split(' ', cmd); - + // a command starts with a lowercase L finalCmd += "l"; - + // and each part of the command is prefixed with its size // separated by a colon for (QStringList::Iterator it = parts.begin(); it != parts.end(); ++it) @@ -138,47 +128,47 @@ finalCmd += ":"; finalCmd += *it; } - + // finally, the cmd ends with "e\n" finalCmd += "e\n"; qDebug("Final command: %s", finalCmd.latin1()); - + // QProcess in QT4 dosen't have a writeToStdin(). // So we need the following QTextStream - QTextStream streamStdIn(this); - + QTextStream streamStdIn(process); + // Write our finalCmd to mtn's StdIn streamStdIn << finalCmd; - + return true; } void Monotone::parseLineFromStdout() { QByteArray byteArray; - + // read all data from stdin //byteArray = readStdout(); - byteArray = readAllStandardOutput(); + byteArray = process->readAllStandardOutput(); QString temp(byteArray); - + // splits the input into lines - QStringList inputList = QStringList::split("\n", temp); - + QStringList inputList = QStringList::split("\n", temp); + QString lineFromStdIn(""); - + // parse out the contents of each line - QRegExp regex("^(\\d+):(\\d+):([ml]):(\\d+):([^\\n]*)"); + QRegExp regex("^(\\d+):(\\d+):([ml]):(\\d+):([^\\n]*)"); for (QStringList::Iterator it = inputList.begin(); it != inputList.end(); ++it ) { - lineFromStdIn = *it; + lineFromStdIn = *it; if (regex.search(lineFromStdIn) == -1) { qWarning("Monotone::parseLineFromStdout: Can't parse data %s", lineFromStdIn.latin1()); continue; } - QStringList list = regex.capturedTexts(); - + QStringList list = regex.capturedTexts(); + // last output? then this contains just the status, // and no additional information if (list[3].compare("l") == 0) @@ -186,9 +176,9 @@ isProcessingData = false; emit commandFinished(list[2].toInt()); break; - } + } // valid output, append it - output->append(list[5]); + output->append(list[5]); } } ============================================================ --- src/model/Monotone.h 56a26664cc846b890ac19ab47e9ce2e0a3686a94 +++ src/model/Monotone.h 2d1520727dceebdf384cf8b6f93b44164917b119 @@ -21,45 +21,45 @@ #ifndef MONOTONE_H #define MONOTONE_H +#include // // Forward class declarations // class QDir; -//#include -// -// Lib Includes -// -#include - - -class Monotone : public QProcess +class Monotone : public QObject { Q_OBJECT - + public: - static Monotone* singleton(QDir*); - void init(QDir*); - ~Monotone(); + static Monotone* singleton(QObject * parent = 0); + + void setup(QDir*); + virtual ~Monotone(); + bool triggerCommand(QString cmd); QStringList* getOutput(); - + protected: - Monotone(QDir*); - - private: + Monotone(QObject *); + + private: QStringList *output; bool isProcessingData; static Monotone* instance; + QProcess * process; + private slots: void parseLineFromStdout(); void processTerminated(int, QProcess::ExitStatus); void startupError(QProcess::ProcessError); - + signals: void commandFinished(int returnCode); + + void criticalError(const QString &); }; #endif ============================================================ --- src/model/Workspace.cpp d9af7b5957edc7427aba2ce770b58693b668a155 +++ src/model/Workspace.cpp 2da2321fcd0d8925d6a5438a86029e1411624c76 @@ -66,24 +66,26 @@ { // enable the wait cursor qApp->setOverrideCursor(Qt::WaitCursor); - - monotone = Monotone::singleton(workspaceDir); - + + monotone = Monotone::singleton(); + connect( monotone, SIGNAL(commandFinished(int)), this, SLOT(parseInventory(int)) ); + monotone->setup(workspaceDir); + return monotone->triggerCommand("inventory"); } void Workspace::parseInventory(int returnCode) { QStringList *output = monotone->getOutput(); - + // TODO: Better error reporting! if ((returnCode > 0) && (!output->isEmpty())) - { + { QString error = output->front(); qWarning("A error occured: %s", error.latin1()); // restore the normal cursor @@ -97,16 +99,16 @@ qApp->restoreOverrideCursor(); return; } - + QRegExp regex("^(R|D|[ ])(R|A|[ ])(M|P|U|I|[ ])\\s(\\d+)\\s(\\d+)\\s(.+)$"); regex.setMinimal(true); - + QMap renameMap; QMap::iterator renameIter; WorkspaceItem *item; QList* tempItems = new QList(); - + for (QStringList::Iterator it = output->begin(); it != output->end(); ++it) { if (regex.search(*it) == -1) @@ -199,7 +201,7 @@ } // Display the item properties - //qDebug("Item: Name %s, PATH %s, Status %s, IsDir %d", + //qDebug("Item: Name %s, PATH %s, Status %s, IsDir %d", // item->getFilename().trimmed().latin1(), // item->getPath().trimmed().latin1(), // item->statusString().trimmed().latin1(), @@ -219,7 +221,7 @@ renameMap[id]->setRenamedFrom(renameMap[-id]); renameMap[-id]->setRenamedTo(renameMap[id]); } - + // clear the map to avoid memory leaks by having pointers to objects // in more than one container // TODO: we should still look out for some tool which allows us to @@ -229,11 +231,11 @@ rootItem->setChildren(*(buildTreeRecursive(tempItems, NULL))); delete tempItems; - + // reset the model to repaint the view completly // (all QModelIndexes are discarded through that, e.g. selections!) this->reset(); - + // restore the normal cursor qApp->restoreOverrideCursor(); @@ -281,10 +283,10 @@ { currentItem->setChildren(*(buildTreeRecursive(items, currentItem))); } - + // append item to final list finalItems->push_back(currentItem); - + } return finalItems; } @@ -304,13 +306,13 @@ } WorkspaceItem *childItem = parentItem->child(row); - + if (childItem) { return createIndex(row, column, childItem); } - - return QModelIndex(); + + return QModelIndex(); } int Workspace::columnCount(const QModelIndex &parent) const @@ -331,7 +333,7 @@ } WorkspaceItem *item = static_cast(index.internalPointer()); - + if((role == Qt::DecorationRole)) { return iconProvider->getIcon(item); @@ -349,7 +351,7 @@ return Qt::ItemIsEnabled; } - // TODO: add Qt::ItemIsDragEnabled and Qt::ItemIsDropEnabled as soon as this works... + // TODO: add Qt::ItemIsDragEnabled and Qt::ItemIsDropEnabled as soon as this works... return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } @@ -370,7 +372,7 @@ { return QModelIndex(); } - + WorkspaceItem *childItem = static_cast(index.internalPointer()); WorkspaceItem *parentItem = childItem->parent(); @@ -381,8 +383,8 @@ return createIndex(parentItem->row(), 0, parentItem); } - -int Workspace::rowCount(const QModelIndex& parent) const + +int Workspace::rowCount(const QModelIndex& parent) const { WorkspaceItem *parentItem = rootItem; @@ -391,6 +393,6 @@ parentItem = static_cast(parent.internalPointer()); } - return parentItem->childCount(); + return parentItem->childCount(); } + - ============================================================ --- src/view/Guitone.cpp b7382268b4b19157b7f3490d031f72bc601c2e1b +++ src/view/Guitone.cpp 613f05389f207af2e7a267b624a7d754e1d86e71 @@ -19,22 +19,28 @@ ***************************************************************************/ #include "Guitone.h" +#include "../model/Monotone.h" Guitone::Guitone() : QMainWindow() { + gotError = false; setCaption(tr("guitone - a frontend for monotone")); - + // create Workspace model myWorkspace = new Workspace(this); + // connect Monotone: + connect(Monotone::singleton(this), SIGNAL(criticalError(const QString &)), + this, SLOT(criticalMtnError(const QString &))); + // // Menubar // menu = menuBar()->addMenu(tr("&File")); QAction* act = menu->addAction(tr("&Import Working Directory..."), this, SLOT(chooseWorkspace())); act->setShortcut(tr("Ctrl+I", "Import")); - + act = menu->addAction(tr("&Quit"), this, SLOT(close())); act->setShortcut(tr("Ctrl+Q", "Quit")); @@ -43,8 +49,8 @@ // toolBar = new QToolBar(this); toolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea); - addToolBar(toolBar); - + addToolBar(toolBar); + // // Main view // @@ -57,7 +63,7 @@ listView = new WorkspaceView(mainSplitter, WorkspaceView::FileList); listView->setModel(myWorkspace); - listView->setSelectionModel(selection); + listView->setSelectionModel(selection); /* treeView->setRootIndex(mySandbox->index(0,0)); listView->setRootIndex(mySandbox->index(0,0)); @@ -68,13 +74,13 @@ connect(treeView, SIGNAL(updateDisplay(std::list*)), listView, SLOT(display(std::list*))); - + connect(treeView, SIGNAL(findAndSelectItem(SandboxItem*)), this, SLOT(doFindAndSelectItem(SandboxItem*))); connect(listView, SIGNAL(findAndSelectItem(SandboxItem*)), this, SLOT(doFindAndSelectItem(SandboxItem*))); - - */ + + */ setCentralWidget(mainSplitter); resize(450, 600); @@ -85,6 +91,16 @@ Guitone::~Guitone() {} +void Guitone::criticalMtnError(const QString & msg) +{ + if (gotError == false) + { + gotError = true; + QMessageBox::critical(NULL, tr("Critical Monotone Error"), + msg, QMessageBox::Ok, 0, 0); + qApp->exit(1); + } +} void Guitone::chooseWorkspace() { @@ -138,14 +154,14 @@ treeView->selectItem(sbItem); return; } - - // check if the element can be selected in the current list + + // check if the element can be selected in the current list if (listView->selectItem(sbItem)) { return; } - - // the item doesn't seem to be in the current list, + + // the item doesn't seem to be in the current list, // lets try to select its parent first, this updates // the list and we may have then luck and see the correct // selected item ============================================================ --- src/view/Guitone.h 8420bd69cc67c4cb78a9c4cc528bdf3bc0c4f15c +++ src/view/Guitone.h 8f614347750418fabecb935530e5037eda3b703d @@ -48,6 +48,7 @@ ~Guitone(); private slots: void chooseWorkspace(); + void criticalMtnError(const QString &); //void doFindAndSelectItem( WorkspaceItem* ); private: @@ -56,6 +57,8 @@ Workspace *myWorkspace; WorkspaceView *treeView; WorkspaceView *listView; + + bool gotError; };