# # # patch "i18n/guitone_de.qm" # from [92e44318cdd9c80c79fc023376d8f3ea3c0ca9e9] # to [2fd1174515f976c44c21eeaede95500ef2387237] # # patch "i18n/guitone_de.ts" # from [fb312dd3b4574049e13837d110b310e569271557] # to [1aaaadfcb3efe96c55074583710159c1727c8832] # # patch "src/Properties.cpp" # from [0c0831ffb9b173f3241286a72d34e4aa1cab46e5] # to [f860c5e50b3b293d2694378ceada921be3a38bff] # # patch "src/Properties.h" # from [cee36b1fe0f3991a17365a3e2545b4098d95c723] # to [4d87d01a0081b68d48ffe76ae53616587a765ba0] # # patch "src/model/Monotone.cpp" # from [83753eba69edcd9a471999590ca9685b6cb0f06f] # to [bf0826043e54b20fe041910b5448ae9f4850fc14] # # patch "src/model/Monotone.h" # from [3306459c13c76bf2bb5ddfb6c30ba16607f4b5fe] # to [45e871cfc575a52e9f4f1a75a26057837bbeac8d] # # patch "src/model/Workspace.cpp" # from [11e182493b340a11ebe3729fe5f1bb1a93d7e210] # to [687f1015e073c3cbb3114bebe220f6fda4dc7855] # # patch "src/model/Workspace.h" # from [5bd5433c0ba3e9b54d352e8685a636f9a40eaef8] # to [6a7552214cb613775845cb358ea2e93ef9dd0b53] # # patch "src/view/Guitone.cpp" # from [22d38d0766c14d9a0927dc23c941aea3de1b1c1d] # to [c1f1a06ade3dbdabd5131f815b85f4e0946e198f] # # patch "src/view/Guitone.h" # from [df4f6efd4141467224a52cd0d97bb92a2e4652c9] # to [240ec4685a00a13789e9667ff6d5c83ed282ae26] # # patch "src/view/WorkspaceView.cpp" # from [1d9d8dd6173a212bc4f584458ca031e3d8dc4bc9] # to [fa1baaf882b961e59dac7bb709b5074e4793bee3] # ============================================================ # i18n/guitone_de.qm is binary ============================================================ --- i18n/guitone_de.ts fb312dd3b4574049e13837d110b310e569271557 +++ i18n/guitone_de.ts 1aaaadfcb3efe96c55074583710159c1727c8832 @@ -19,7 +19,7 @@ Ready - Bereit + Bereit Select your workspace... @@ -65,7 +65,7 @@ &Import Working Directory... - &Importiere Arbeitsbereich + &Importiere Arbeitsbereich Critical Monotone Error @@ -88,6 +88,22 @@ &Show ignored files Ignorierte Dateien a&nzeigen + + &Recent Workspaces + &Vorherige Arbeitsbereiche + + + &Open Workspace + Arbeitsbereich &öffnen + + + &%1 %2 + &%1 %2 + + + No previous workspaces available. + Keine vorherigen Arbeitsbereiche verfügbar. + Monotone ============================================================ --- src/Properties.cpp 0c0831ffb9b173f3241286a72d34e4aa1cab46e5 +++ src/Properties.cpp f860c5e50b3b293d2694378ceada921be3a38bff @@ -20,46 +20,90 @@ #include "Properties.h" -Properties::Properties() +Properties* Properties::props = 0; + +Properties* Properties::singleton() { - settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, "GUITONE", "GUITONE"); + if (!props) + { + props = new Properties(); + } + return props; } +Properties::Properties() + : QSettings(QSettings::IniFormat, QSettings::UserScope, "GUITONE", "GUITONE") +{} -Properties::~Properties() -{ - delete settings; -} +Properties::~Properties() {} -QSize Properties::getStartupSize(void) const +QSize Properties::getStartupSize(void) { - return settings->value("StartupSize", QSize(300, 200)).toSize(); + return singleton()->value("StartupSize", QSize(640, 480)).toSize(); } void Properties::setStartupSize(QSize size) { - settings->setValue("StartupSize", size); - settings->sync(); + props = singleton(); + props->setValue("StartupSize", size); + props->sync(); } -QSize Properties::getFileListSize(void) const +QSize Properties::getFileListSize(void) { - return settings->value("FileListSize", QSize(100, 100)).toSize(); + return singleton()->value("FileListSize", QSize(100, 100)).toSize(); } void Properties::setFileListSize(QSize size) { - settings->setValue("FileListSize", size); - settings->sync(); + props = singleton(); + props->setValue("FileListSize", size); + props->sync(); } -QSize Properties::getTreeViewSize(void) const +QSize Properties::getTreeViewSize(void) { - return settings->value("TreeViewSize", QSize(100, 100)).toSize(); + return singleton()->value("TreeViewSize", QSize(100, 100)).toSize(); } void Properties::setTreeViewSize(QSize size) { - settings->setValue("TreeViewSize", size); - settings->sync(); + props = singleton(); + props->setValue("TreeViewSize", size); + props->sync(); } + +QStringList Properties::getPreviousWorkspaces() +{ + return singleton()->value("RecentWorkspaceList").toStringList(); +} + +void Properties::setPreviousWorkspaces(QStringList list) +{ + props = singleton(); + props->setValue("RecentWorkspaceList", list); + props->sync(); +} + +void Properties::addPreviousWorkspace(QString workspace) +{ + QStringList list = getPreviousWorkspaces(); + // do not do anything if we've already recorded this workspace + if (list.contains(workspace)) return; + + if (list.size() > MaxPreviousWorkspaces) + { + list.removeLast(); + } + list.prepend(workspace); + setPreviousWorkspaces(list); +} + +void Properties::removePreviousWorkspace(QString workspace) +{ + QStringList list = getPreviousWorkspaces(); + int pos = list.indexOf(workspace); + if (pos == -1) return; + list.removeAt(pos); + setPreviousWorkspaces(list); +} ============================================================ --- src/Properties.h cee36b1fe0f3991a17365a3e2545b4098d95c723 +++ src/Properties.h 4d87d01a0081b68d48ffe76ae53616587a765ba0 @@ -27,22 +27,29 @@ class QSettings; class QSize; -class Properties +class Properties : public QSettings { public: - Properties(void); - ~Properties(void); - QSize getStartupSize(void) const; - void setStartupSize(QSize size); - QSize getFileListSize(void) const; - void setFileListSize(QSize size); - QSize getTreeViewSize(void) const; - void setTreeViewSize(QSize size); + static QSize getStartupSize(void); + static void setStartupSize(QSize size); + static QSize getFileListSize(void); + static void setFileListSize(QSize size); + static QSize getTreeViewSize(void); + static void setTreeViewSize(QSize size); + static QStringList getPreviousWorkspaces(); + static void setPreviousWorkspaces(QStringList); + static void addPreviousWorkspace(QString); + static void removePreviousWorkspace(QString); - + // FIXME: we may want to make this configurable later on + enum { MaxPreviousWorkspaces = 8 }; private: QSettings *settings; + Properties(); + ~Properties(void); + static Properties* singleton(); + static Properties* props; }; ============================================================ --- src/model/Monotone.cpp 83753eba69edcd9a471999590ca9685b6cb0f06f +++ src/model/Monotone.cpp bf0826043e54b20fe041910b5448ae9f4850fc14 @@ -52,7 +52,7 @@ process, SIGNAL(readyReadStandardOutput()), this, SLOT(parseLineFromStdout()) ); - + // monitor if the process is exited unexpectedly connect( process, SIGNAL(finished(int, QProcess::ExitStatus)), @@ -175,6 +175,10 @@ for (QStringList::Iterator it = inputList.begin(); it != inputList.end(); ++it ) { lineFromStdIn = *it; + // FIXME: skip empty lines, they pop up otherwise, but + // I have no idea where these should come from + if (lineFromStdIn.size() == 0) continue; + if (regex.indexIn(lineFromStdIn) == -1) { qWarning("Monotone::parseLineFromStdout: Can't parse data %s", qPrintable(lineFromStdIn)); @@ -182,6 +186,12 @@ } QStringList list = regex.capturedTexts(); + // valid output, append it + if (list[5].size() > 0) + { + output->append(list[5]); + } + // last output? then this contains just the status, // and no additional information if (list[3].compare("l") == 0) @@ -189,9 +199,7 @@ isProcessingData = false; emit commandFinished(list[2].toInt()); break; - } - // valid output, append it - output->append(list[5]); + } } } ============================================================ --- src/model/Monotone.h 3306459c13c76bf2bb5ddfb6c30ba16607f4b5fe +++ src/model/Monotone.h 45e871cfc575a52e9f4f1a75a26057837bbeac8d @@ -49,12 +49,11 @@ bool isProcessingData; bool isCleanExit; static Monotone* instance; - QProcess * process; private slots: void parseLineFromStdout(); - void processTerminated(int, QProcess::ExitStatus); + void processTerminated(int, QProcess::ExitStatus); void startupError(QProcess::ProcessError); signals: ============================================================ --- src/model/Workspace.cpp 11e182493b340a11ebe3729fe5f1bb1a93d7e210 +++ src/model/Workspace.cpp 687f1015e073c3cbb3114bebe220f6fda4dc7855 @@ -71,6 +71,11 @@ return false; } +QString Workspace::getNormalizedWorkspaceDir() +{ + return workspaceDir->path(); +} + bool Workspace::readInventory() { if(modelPresent) @@ -97,25 +102,27 @@ void Workspace::createModel(int returnCode) { - if(modelPresent) + if (modelPresent) { qWarning("Workspace::createModel: A model is already created!"); return; } QStringList *output = monotone->getOutput(); - // TODO: Better error reporting! + // FIXME: we should throw the monotone interface errors further + // to the user or at least log them somewhere... if ((returnCode > 0) && (!output->isEmpty())) { QString error = output->front(); - qWarning("A error occured: %s", qPrintable(error)); + + qWarning("Workspace::parseInventory: A monotone error occured: %s", qPrintable(error)); // restore the normal cursor qApp->restoreOverrideCursor(); return; } else if (returnCode > 0) { - qWarning("Workspace::parseInventory: A error occured with empty output list"); + qWarning("Workspace::parseInventory: A monotone error occured (no further information available)"); // restore the normal cursor qApp->restoreOverrideCursor(); return; @@ -131,28 +138,27 @@ int to_id(0); QString path(""); bool isDirectory(false); - bool isOk(false); for (QStringList::Iterator it = output->begin(); it != output->end(); ++it) { - parseOutputLine(*it, status, from_id, to_id, path, isDirectory, isOk); - - if (isOk) + if (!parseInventoryLine(*it, status, from_id, to_id, path, isDirectory)) { - // this item is given a parent explicitely later on - item = new WorkspaceItem(NULL, path, status, isDirectory); + continue; + } + + // this item is given a parent explicitely later on + item = new WorkspaceItem(NULL, path, status, isDirectory); - if (from_id > 0) - { - renameMap[-from_id] = item; - } - if (to_id > 0) - { - renameMap[to_id] = item; - } + if (from_id > 0) + { + renameMap[-from_id] = item; + } + if (to_id > 0) + { + renameMap[to_id] = item; + } - tempItems.push_back(item); - } + tempItems.push_back(item); } int id = 0; @@ -339,21 +345,19 @@ return parentItem->childCount(); } -void Workspace::parseOutputLine(const QString &inputString, - int &status, - int &from_id, - int &to_id, - QString &path, - bool &isDirectory, - bool &ok) +bool Workspace::parseInventoryLine( + const QString &inputString, + int &status, + int &from_id, + int &to_id, + QString &path, + bool &isDirectory) { if (regex->indexIn(inputString) == -1) { qWarning("Couldn't parse inventory line %s", qPrintable(inputString)); - ok = false; - return; + return false; } - ok = true; QStringList list = regex->capturedTexts(); status = 0; @@ -363,69 +367,71 @@ { status |= WorkspaceItem::RenamedFrom; } else - if (list[1].compare("D") == 0) - { - status |= WorkspaceItem::Dropped; - } - else - { - if (list[1].compare(" ") != 0) - { - qWarning("Unknown status first tripel %s", qPrintable(list[1])); - } - } + if (list[1].compare("D") == 0) + { + status |= WorkspaceItem::Dropped; + } + else + if (list[1].compare(" ") != 0) + { + qWarning("Unknown status first tripel %s", qPrintable(list[1])); + return false; + } - // the second match - if (list[2].compare("R") == 0) - { - status |= WorkspaceItem::RenamedTo; - } else - if (list[2].compare("A") == 0) - { - status |= WorkspaceItem::Added; - } - else - { - if (list[2].compare(" ") != 0) - { - qWarning("Unknown status second tripel %s", qPrintable(list[2])); - } - } - // the third match - if (list[3].compare("M") == 0) - { - status |= WorkspaceItem::Missing; - } else - if (list[3].compare("P") == 0) - { - status |= WorkspaceItem::Patched; - } else - if (list[3].compare("U") == 0) - { - status |= WorkspaceItem::Unknown; - } else - if (list[3].compare("I") == 0) - { - status |= WorkspaceItem::Ignored; - } else - { - // if nothing is outputted, the file is unchanged - status |= WorkspaceItem::Unchanged; - } + // the second match + if (list[2].compare("R") == 0) + { + status |= WorkspaceItem::RenamedTo; + } else + if (list[2].compare("A") == 0) + { + status |= WorkspaceItem::Added; + } + else + if (list[2].compare(" ") != 0) + { + qWarning("Unknown status second tripel %s", qPrintable(list[2])); + return false; + } - // now determine if the file has been renamed - from_id = list[4].toInt(); - to_id = list[5].toInt(); + // the third match + if (list[3].compare("M") == 0) + { + status |= WorkspaceItem::Missing; + } else + if (list[3].compare("P") == 0) + { + status |= WorkspaceItem::Patched; + } else + if (list[3].compare("U") == 0) + { + status |= WorkspaceItem::Unknown; + } else + if (list[3].compare("I") == 0) + { + status |= WorkspaceItem::Ignored; + } else + { + // if nothing is outputted, the file is unchanged + status |= WorkspaceItem::Unchanged; + } - // remove trailing slash - path = list[6].trimmed(); - isDirectory = false; - if (path.endsWith('/')) - { - isDirectory = true; - path = path.left(path.length() - 1); - } + // now determine if the file has been renamed + from_id = list[4].toInt(); + to_id = list[5].toInt(); + + // remove trailing slash + path = list[6].trimmed(); + isDirectory = false; + if (path.endsWith('/')) + { + isDirectory = true; + path = path.left(path.length() - 1); + } + + // parsing was successful + return true; } void Workspace::deleteModel(void) ============================================================ --- src/model/Workspace.h 5bd5433c0ba3e9b54d352e8685a636f9a40eaef8 +++ src/model/Workspace.h 6a7552214cb613775845cb358ea2e93ef9dd0b53 @@ -36,6 +36,7 @@ Workspace(QObject *parent); ~Workspace(); bool setWorkspaceDir(QString workspace); + QString getNormalizedWorkspaceDir(); bool readInventory(); // needed Qt Model methods @@ -48,7 +49,7 @@ int columnCount(const QModelIndex&) const; private: - void parseOutputLine(const QString &, int &, int &, int &, QString &, bool &, bool &); + bool parseInventoryLine(const QString &, int &, int &, int &, QString &, bool &); QList buildTreeRecursive(QList &, WorkspaceItem*); void deleteModel(void); ============================================================ --- src/view/Guitone.cpp 22d38d0766c14d9a0927dc23c941aea3de1b1c1d +++ src/view/Guitone.cpp c1f1a06ade3dbdabd5131f815b85f4e0946e198f @@ -34,21 +34,30 @@ // create Workspace model myWorkspace = new Workspace(this); - // connect Monotone: - connect(Monotone::singleton(this), SIGNAL(criticalError(const QString &)), - this, SLOT(criticalMtnError(const QString &))); + // connect to Monotone to catch critical errors + connect( + Monotone::singleton(this), SIGNAL(criticalError(const QString &)), + this, SLOT(criticalMtnError(const QString &)) + ); // // Menubar // menu = menuBar()->addMenu(tr("&File")); menu->addAction( - tr("&Import Working Directory..."), + tr("&Open Workspace"), this, SLOT(chooseWorkspace()), - Qt::CTRL + Qt::Key_I + Qt::CTRL + Qt::Key_O ); + + // + // load recent workspace list + // + wsSubMenu = menu->addMenu(tr("&Recent Workspaces")); + updatePreviousWorkspacesMenu(); + QAction *act = menu->addAction(""); act->setSeparator(true); @@ -107,10 +116,14 @@ setCentralWidget(mainSplitter); - Properties properties; - resize(properties.getStartupSize()); + resize(Properties::getStartupSize()); - statusBar()->showMessage(tr("Ready"), 2000); + // load the most recent previous workspace, if there is any + QStringList list = Properties::getPreviousWorkspaces(); + if (list.size() > 0) + { + loadWorkspace(list[0]); + } } @@ -162,7 +175,14 @@ statusBar()->showMessage(tr("Loading aborted"), 2000); return; } + + loadWorkspace(fn); +} +void Guitone::loadWorkspace(QString fn) +{ + qDebug("Last workspace %s", qPrintable(fn)); + if (!myWorkspace->setWorkspaceDir(fn)) { QMessageBox::information( @@ -170,7 +190,10 @@ tr("Invalid workspace"), tr("The chosen directory is no monotone workspace!"), QMessageBox::Ok - ); + ); + // remove the workspace if it was recorded as recent workspace + Properties::removePreviousWorkspace(fn); + updatePreviousWorkspacesMenu(); return; } @@ -185,15 +208,17 @@ return; } + // add the workspace to the recent workspace list + Properties::addPreviousWorkspace(myWorkspace->getNormalizedWorkspaceDir()); + updatePreviousWorkspacesMenu(); + statusBar()->showMessage(tr("Loading workspace..."), 2000 ); } void Guitone::closeEvent(QCloseEvent *event) { - // Do some cleanup before colseing down the application - Properties properties; - - properties.setStartupSize(size()); + // Do some cleanup before closing down the application + Properties::setStartupSize(size()); event->accept(); } @@ -207,3 +232,38 @@ hide ? tr("&Hide ignored files") : tr("&Show ignored files") ); } + +void Guitone::openRecentWorkspace() +{ + QAction *action = qobject_cast(sender()); + if (action) + { + loadWorkspace(action->data().toString()); + } +} + +void Guitone::updatePreviousWorkspacesMenu() +{ + // clear previous actions + wsSubMenu->clear(); + + QStringList previousWs = Properties::getPreviousWorkspaces(); + int elemCount = previousWs.size(); + if (elemCount == 0) + { + wsSubMenu->addAction(tr("No previous workspaces available.")); + return; + } + + QAction *act; + // add the new actions + for (int i = 0; i < elemCount; ++i) + { + act = wsSubMenu->addAction( + tr("&%1 %2").arg(i + 1).arg(previousWs[i]), + this, + SLOT(openRecentWorkspace()) + ); + act->setData(previousWs[i]); + } +} ============================================================ --- src/view/Guitone.h df4f6efd4141467224a52cd0d97bb92a2e4652c9 +++ src/view/Guitone.h 240ec4685a00a13789e9667ff6d5c83ed282ae26 @@ -42,7 +42,8 @@ ~Guitone(); private slots: void chooseWorkspace(); - void criticalMtnError(const QString &); + void openRecentWorkspace(); + void criticalMtnError(const QString &); //void doFindAndSelectItem( WorkspaceItem* ); void slotMapFolderTreeToFileList( const QModelIndex &proxyIndex ); void slotMapFileListToFolderTree( const QModelIndex &proxyIndex ); @@ -50,8 +51,11 @@ private: void closeEvent(QCloseEvent *event); - + void loadWorkspace(QString); + void updatePreviousWorkspacesMenu(); + QMenu *menu; + QMenu *wsSubMenu; QAction *actShowHideIgnored; QToolBar *toolBar; Workspace *myWorkspace; ============================================================ --- src/view/WorkspaceView.cpp 1d9d8dd6173a212bc4f584458ca031e3d8dc4bc9 +++ src/view/WorkspaceView.cpp fa1baaf882b961e59dac7bb709b5074e4793bee3 @@ -24,18 +24,16 @@ WorkspaceView::WorkspaceView(QWidget* parent, Type type_) : QTreeView(parent), type(type_) { - Properties properties; - if(type == FileList) { setRootIsDecorated(false); setItemsExpandable(false); - resize(properties.getFileListSize()); + resize(Properties::getFileListSize()); } else { setRootIsDecorated(true); - resize(properties.getTreeViewSize()); + resize(Properties::getTreeViewSize()); } setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -241,14 +239,13 @@ void WorkspaceView::slotSaveState(void) { - Properties properties; - if(type == FileList) + if (type == FileList) { - properties.setFileListSize(size()); + Properties::setFileListSize(size()); } else { - properties.setTreeViewSize(size()); + Properties::setTreeViewSize(size()); } }