# # # add_file "res/forms/dialogs/netsync.ui" # content [2c01c845bdfe4490cff1659390efe749d72fe544] # # add_file "src/view/dialogs/Netsync.cpp" # content [8782a4afbfaf1af1871e297529c6c47cac028a55] # # add_file "src/view/dialogs/Netsync.h" # content [33cb539497a787d27d32cb594a901ffd8584f4f7] # # patch "NEWS" # from [ea03d1c9b0c26b701b56a1e217213eaadaa7106c] # to [1c169dfc4599d4ea266a86ffffcec3bb77db7ecd] # # patch "src/model/AutomateCommand.cpp" # from [1c819d32c321a539d0d59e100a632d17fdba16ca] # to [80b69f1ceaee3c47a08a18914f5cda659bed27d0] # # patch "src/model/AutomateCommand.h" # from [93a7d2bbd7d51d8b0f75ff9d63092ffc69aaa247] # to [b2e0fd928d2e273b314398bc250066de03ea5348] # # patch "src/monotone/MonotoneThread.cpp" # from [c15b3807460064117d7d5904624decb5f0f52759] # to [56a8906a9d0f808ea3a08d88920bfffd1afecf69] # # patch "src/monotone/MonotoneThread.h" # from [80bb125d5e5b9d5b51d3bc2eb9701f1080689c7d] # to [8a6365f540ed8706b8eae206865dfac75fa452a3] # # patch "src/util/StdioParser.cpp" # from [48c78328277cff98a5ba59e95e32c3440637c964] # to [d4cba87dc4415fc989015ccadcc95ba28b3e50ae] # # patch "src/util/StdioParser.h" # from [5f6b6793087a0d3d884c32f1cfa786632c67c647] # to [280e28221050eecf94aac14355dc0f80c09f7dd3] # # patch "src/view/dialogs/DatabaseDialogManager.cpp" # from [903fe5513823171829c2d7feaefc5996bcd05a7d] # to [bf76575815a0ab6bf17bb5c75fe52a1f4c5db4a5] # # patch "src/view/dialogs/DatabaseDialogManager.h" # from [532a5c5e04720356b89b3584d40833c422ed2a26] # to [1a090d7cc52c821066410946fbcdd592158eddda] # # patch "src/view/mainwindows/DatabaseWindow.cpp" # from [37323d919fecd0fda6c5266a8af16203b88edf04] # to [32afece8ed33326fca00fe4180fcc440dcf07256] # # patch "src/view/widgets/DatabaseMenuBar.cpp" # from [24128d740e1d3b1b08839bf94d1f348b96bf1fd4] # to [ce3a7aa362d08e1f816cd9456b8d3fd555a4fadc] # # patch "src/view/widgets/DatabaseMenuBar.h" # from [b100fad7678bb201825278f5d496dc29bd43fe26] # to [e891dcedbca13fdf865e320f4a6026e1c5eadbb3] # # patch "src/vocab.h" # from [1b2725cb1d597d7dd4035b6763f47bf02eb062cb] # to [47f7cac5d9cfea1f8308980d3de9268b1e97a674] # ============================================================ --- res/forms/dialogs/netsync.ui 2c01c845bdfe4490cff1659390efe749d72fe544 +++ res/forms/dialogs/netsync.ui 2c01c845bdfe4490cff1659390efe749d72fe544 @@ -0,0 +1,223 @@ + + + NetsyncDialog + + + + 0 + 0 + 583 + 308 + + + + + 0 + 0 + + + + Synchronize with other nodes + + + + 10 + + + QLayout::SetFixedSize + + + + + + + Settings + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Host + + + + + + + true + + + + + + + Authentication + + + + + + + Include-Pattern + + + + + + + Example: net.venge.monotone* + + + * + + + + + + + Exclude-Pattern + + + + + + + Example: net.venge.monotone{,.guitone} + + + + + + + + + + + + + Action + + + + + + Pull + + + true + + + + + + + Push + + + + + + + Sync + + + + + + + + + + + + + + Qt::Horizontal + + + + 300 + 20 + + + + + + + + Start + + + + + + + Close + + + + + + + + + + 0 + + + + + 0 + + + -1 + + + + + + + + 11 + + + + Progress: + + + Qt::NoTextInteraction + + + + + + + + + + includePattern + excludePattern + + + + + closeButton + clicked() + NetsyncDialog + accept() + + + 484 + 235 + + + 386 + 238 + + + + + ============================================================ --- src/view/dialogs/Netsync.cpp 8782a4afbfaf1af1871e297529c6c47cac028a55 +++ src/view/dialogs/Netsync.cpp 8782a4afbfaf1af1871e297529c6c47cac028a55 @@ -0,0 +1,233 @@ +/*************************************************************************** + * Copyright (C) 2009 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 3 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, see . * + ***************************************************************************/ + +#include "Netsync.h" +#include "Settings.h" +#include "MonotoneUtil.h" + +#include + +Netsync::Netsync(QWidget * parent, const DatabaseFile & database) + : Dialog(parent), db(database), running(false) +{ + setupUi(this); + Dialog::init(); + + connect( + startStopButton, SIGNAL(clicked()), + this, SLOT(startStopClicked()) + ); +} + +Netsync::~Netsync() {} + +void Netsync::init() +{ + progressWidget->hide(); + + QMap defaults = + MonotoneUtil::getDatabaseVariables(db, "database"); + + QList knownServers = + (MonotoneUtil::getDatabaseVariables(db, "known-servers")).keys(); + + includePattern->setText(defaults.contains("default-include-pattern") ? + defaults["default-include-pattern"] : "*"); + + excludePattern->setText(defaults.contains("default-exclude-pattern") ? + defaults["default-exclude-pattern"] : ""); + + host->clear(); + host->addItems(knownServers); + + if (defaults.contains("default-server")) + { + if (!knownServers.contains(defaults["default-server"])) + { + host->addItem(defaults["default-server"]); + } + host->setCurrentIndex(host->findText(defaults["default-server"])); + } + + QMap privateKeys = + MonotoneUtil::getPrivateKeyList(db); + + keys->clear(); + keys->addItem(tr(""), QString()); + + for (QMap::const_iterator it = privateKeys.constBegin(); + it != privateKeys.constEnd(); it++) + { + keys->addItem(it.value(), it.key()); + } +} + +void Netsync::start(Action act, + const QString & host, + const QString & key, + const QString & include, + const QString & exclude) +{ + QMap actions; + actions[Pull] = "pull"; + actions[Push] = "push"; + actions[Sync] = "sync"; + + QStringList cmd; + cmd << actions[act] << host << include; + + QStringList opts; + if (!exclude.isEmpty()) + { + opts << "exclude" << exclude; + } + + if (!key.isEmpty()) + { + opts << "key" << key; + } + + MonotoneTask task(cmd, opts); + AutomateCommand::enqueueDatabaseTask(db, task); + + progressText->setText(tr("Connecting to %1...").arg(host)); + startStopButton->setText(tr("Stop")); + settingsGroup->setEnabled(false); + actionsGroup->setEnabled(false); + overallProgress->setMaximum(0); + progressWidget->show(); + running = true; +} + +void Netsync::stop() +{ + stopAllTasks(); + progressWidget->hide(); + startStopButton->setText(tr("Start")); + settingsGroup->setEnabled(true); + actionsGroup->setEnabled(true); + running = false; +} + +void Netsync::startStopClicked() +{ + if (running) + { + stop(); + return; + } + + Action act; + QStringList cmd; + if (actionPull->isChecked()) + act = Pull; + else if (actionPush->isChecked()) + act = Push; + else if (actionSync->isChecked()) + act = Sync; + else + I(false); + + QString include("*"); + if (!includePattern->text().isEmpty()) + { + include = includePattern->text(); + } + + QString exclude; + if (!excludePattern->text().isEmpty()) + { + exclude = excludePattern->text(); + } + + QString key = keys->itemData(keys->currentIndex()).toString(); + + start(act, host->currentText(), key, include, exclude); +} + +void Netsync::processTaskResult(const MonotoneTask & task) +{ + running = false; + startStopButton->setText(tr("Start")); + settingsGroup->setEnabled(true); + actionsGroup->setEnabled(true); + + overallProgress->setMaximum(1); + overallProgress->setValue(1); + + if (task.getReturnCode() != 0) + { + progressText->setText(tr("Connection failed: %1") + .arg(task.getDecodedOutput())); + return; + } + + progressText->setText(tr("Successful exchange with %1") + .arg(QString(task.getArguments().at(1)))); +} + +void Netsync::tickerUpdate(const TickerMap & tickers) +{ + // we "compress" the values of all found tickers into one progress bar + // this also means that we cannot display an actual progress unless + // we know the total values of all tickers + bool knowTotalValue = true; + int progress = 0, total = 0; + QStringList progressTexts; + foreach (char ident, tickers.keys()) + { + if (tickers[ident].complete) + continue; + + // we never get total values for the transferred bytes, so ignore + // these completly for the progress bar calculation + if (ident == '<' || ident == '>') + { + progressTexts.push_back(tickers[ident].toString(true)); + } + else + { + if (tickers[ident].total == 0) + knowTotalValue = false; + + progress += tickers[ident].progress; + total += tickers[ident].total; + progressTexts.push_back(tickers[ident].toString(false)); + } + } + + if (knowTotalValue) + overallProgress->setMaximum(total); + else + overallProgress->setMaximum(0); + + overallProgress->setValue(progress); + progressText->setText(progressTexts.join(", ")); + + progressWidget->show(); +} + +void Netsync::accept() +{ + if (running) + stop(); + + progressWidget->hide(); + done(QDialog::Accepted); +} + ============================================================ --- src/view/dialogs/Netsync.h 33cb539497a787d27d32cb594a901ffd8584f4f7 +++ src/view/dialogs/Netsync.h 33cb539497a787d27d32cb594a901ffd8584f4f7 @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2008 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 3 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, see . * + ***************************************************************************/ + +#ifndef NETSYNC_H +#define NETSYNC_H + +#include "vocab.h" +#include "Dialog.h" +#include "AutomateCommand.h" +#include "ui_netsync.h" + +class Netsync : public Dialog, public AutomateCommand, private Ui::NetsyncDialog +{ + Q_OBJECT +public: + enum Action { Push, Pull, Sync }; + + Netsync(QWidget *, const DatabaseFile &); + ~Netsync(); + void init(); + +public slots: + void start(Action, + const QString &, + const QString &, + const QString &, + const QString &); + void stop(); + +protected: + void accept(); + void processTaskResult(const MonotoneTask &); + virtual void tickerUpdate(const TickerMap &); + +protected slots: + void startStopClicked(); + +private: + DatabaseFile db; + bool running; +}; + +#endif ============================================================ --- NEWS ea03d1c9b0c26b701b56a1e217213eaadaa7106c +++ NEWS 1c169dfc4599d4ea266a86ffffcec3bb77db7ecd @@ -1,6 +1,10 @@ ????-??-?? (0.10) - new: create / initialize new monotone databases + - new: synchronize with other monotone databases via netsync - new: directly select the key to sign a revision with in the commit dialog + - bugfix: no longer load the user's default hooks under ~/.monotone/monotonerc + or _MTN/monotonerc whose out-of-band output would interfer with our stdio + interface. - bugfix: no longer crash the changeset browser when a database contains no revisions and the user clicks on "more/all changes" ============================================================ --- src/model/AutomateCommand.cpp 1c819d32c321a539d0d59e100a632d17fdba16ca +++ src/model/AutomateCommand.cpp 80b69f1ceaee3c47a08a18914f5cda659bed27d0 @@ -36,7 +36,10 @@ AutomateCommand::~AutomateCommand() AutomateCommand::~AutomateCommand() { - if (cmdHelperCreated) delete cmdHelper; + if (cmdHelperCreated) + { + delete cmdHelper; + } } void AutomateCommand::enqueueTask(MonotoneThread * thread, const MonotoneTask & task) @@ -57,6 +60,11 @@ void AutomateCommand::enqueueTask(Monoto cmdHelper, SLOT(taskFinished(const MonotoneTask &)) ); + QObject::connect( + thread, SIGNAL(tickersChanged(const TickerMap &)), + cmdHelper, SLOT(tickersChanged(const TickerMap &)) + ); + connectedThreads.append(threadNumber); } @@ -97,6 +105,11 @@ void AutomateCommand::stopAllTasks() } } +void AutomateCommand::tickerUpdate(const TickerMap & tickers) +{ + Q_UNUSED(tickers); +} + AutomateCommandHelper::AutomateCommandHelper(AutomateCommand * cmd) : command(cmd) {} @@ -132,3 +145,8 @@ void AutomateCommandHelper::taskFinished command->processTaskResult(task); } +void AutomateCommandHelper::tickersChanged(const TickerMap & tickers) +{ + command->tickerUpdate(tickers); +} + ============================================================ --- src/model/AutomateCommand.h 93a7d2bbd7d51d8b0f75ff9d63092ffc69aaa247 +++ src/model/AutomateCommand.h b2e0fd928d2e273b314398bc250066de03ea5348 @@ -36,6 +36,7 @@ protected: protected: virtual void processTaskResult(const MonotoneTask &) = 0; + virtual void tickerUpdate(const TickerMap &); void enqueueWorkspaceTask(const WorkspacePath &, const MonotoneTask &); void enqueueDatabaseTask(const DatabaseFile &, const MonotoneTask &); void stopAllTasks(); @@ -60,8 +61,8 @@ protected slots: protected slots: virtual void taskAborted(const MonotoneTask &); - void taskFinished(const MonotoneTask &); + void tickersChanged(const TickerMap &); private: AutomateCommand * command; ============================================================ --- src/monotone/MonotoneThread.cpp c15b3807460064117d7d5904624decb5f0f52759 +++ src/monotone/MonotoneThread.cpp 56a8906a9d0f808ea3a08d88920bfffd1afecf69 @@ -70,13 +70,6 @@ void MonotoneTask::init(const ByteArrayL finished = false; abortTask = false; outputEncoding = "UTF-8"; - - static bool initialized = false; - if (!initialized) - { - qRegisterMetaType("MonotoneTask"); - initialized = true; - } } ByteArrayList MonotoneTask::stringToByteArrayList(const QStringList & list) @@ -148,12 +141,7 @@ MonotoneThread::MonotoneThread( int thread, const QString & m, const QString & d, const QString & w ) : QThread(), doAbort(false), commandNumber(0), threadNumber(thread), mtnBinary(m), databasePath(d), workspacePath(w) -{ - connect( - this, SIGNAL(aborted(int, QProcess::ProcessError, const QString &)), - this, SLOT(cleanup()) - ); -} +{} MonotoneThread::~MonotoneThread() {} @@ -199,6 +187,7 @@ void MonotoneThread::run() args << "stdio"; args << QString("--automate-stdio-size=%1").arg(StdioBufferSize); args << "--db" << databasePath; + args << "--norc"; // check whether we need to work on a specific workspace directory or not if (!workspacePath.isEmpty()) @@ -218,6 +207,7 @@ void MonotoneThread::run() "workspace, which may lead to serious path resolution " "errors on execution. Its recommended you remove " "this workspace before you retry this again!").arg(tmpPath)); + cleanup(process); return; } process->setWorkingDirectory(tmpPath); @@ -239,6 +229,7 @@ void MonotoneThread::run() emit aborted(threadNumber, process->error(), QString::fromUtf8(process->readAllStandardError()) ); + cleanup(process); return; } @@ -255,6 +246,7 @@ void MonotoneThread::run() QString err = QString::fromUtf8(process->readAllStandardError()); emit aborted(threadNumber, process->error(), err); + cleanup(process); return; } process->setReadChannel(QProcess::StandardOutput); @@ -285,6 +277,7 @@ void MonotoneThread::run() emit aborted(threadNumber, process->error(), QString::fromUtf8(process->readAllStandardError()) ); + cleanup(process); return; } @@ -323,6 +316,7 @@ void MonotoneThread::run() buffer.clear(); output.clear(); + tickers.clear(); processingTask = true; D(QString("thread %1: task %2 (%3) started").arg(threadNumber) @@ -335,55 +329,74 @@ void MonotoneThread::run() emit aborted(threadNumber, process->error(), QString::fromUtf8(process->readAllStandardError()) ); + cleanup(process); return; } - // we can't assume that all stderr output from stdio is evil - // just because there are some automate commands which are - // programmed badly (f.e. genkey, which outputs status messages - // on stderr) QByteArray err = process->readAllStandardError(); if (err.size() != 0) { - // FIXME: expand this list further or wait for nvm.automate_out_of_band to land - QStringList cmdsWithSpuriousOutput = - QStringList() << "genkey" << "select" << "inventory"; - QString cmd = task.getArguments().at(0); + task.setOutput(err); + task.setReturnCode(-1); - if (!cmdsWithSpuriousOutput.contains(cmd)) + processingTask = false; + buffer.clear(); + output.clear(); + queue.dequeue(); + + emit taskAborted(task); + continue; + } + + buffer.append(process->readAllStandardOutput()); + + int returnCode = -1; + while (!buffer.isEmpty()) + { + StdioParser parser(buffer); + + // if the chunk is not yet complete, try again later + if (!parser.parse()) { - task.setOutput(err); - task.setReturnCode(-1); + D("cannot parse - continueing"); + break; + } - processingTask = false; - buffer.clear(); - output.clear(); - queue.dequeue(); + TickerMap tickerUpdates = parser.getTickers(); + if (tickerUpdates.size() > 0) + { + if (tickers.size() == 0) + { + tickers = tickerUpdates; + } + else + { + // update previous ticker values + foreach (char ident, tickerUpdates.keys()) + { + if (tickers.contains(ident)) + tickers[ident].update(tickerUpdates[ident]); + else + tickers[ident] = tickerUpdates[ident]; + } + } - emit taskAborted(task); - continue; + emit tickersChanged(tickers); } - } - buffer.append(process->readAllStandardOutput()); - StdioParser parser(buffer); + buffer = parser.getLeftBytes(); + output.append(parser.getPayload()); - // if the chunk is not yet complete, try again later - if (!parser.parse()) - { - D("cannot parse - continueing"); - continue; + if (parser.getChunkType() == 'l') + { + returnCode = parser.getErrorCode(); + I(buffer.isEmpty()); + break; + } } - buffer = parser.getLeftBytes(); - output.append(parser.getPayload()); - int returnCode = parser.getErrorCode(); - - // TODO: support for other chunk types here? - if (parser.getChunkType() == 'm') - { + if (returnCode < 0) continue; - } task.setOutput(output); task.setReturnCode(returnCode); ============================================================ --- src/monotone/MonotoneThread.h 80bb125d5e5b9d5b51d3bc2eb9701f1080689c7d +++ src/monotone/MonotoneThread.h 8a6365f540ed8706b8eae206865dfac75fa452a3 @@ -157,6 +157,9 @@ signals: //! signaled if the interal mtn process was aborted (i.e. crashed) void aborted(int, QProcess::ProcessError, const QString &); + //! signals newly setup / changed tickers + void tickersChanged(const TickerMap &); + private slots: void cleanup(QProcess *); @@ -172,6 +175,7 @@ private: QQueue queue; QMutex lock; QWaitCondition waitForTasks; + TickerMap tickers; }; #endif ============================================================ --- src/util/StdioParser.cpp 48c78328277cff98a5ba59e95e32c3440637c964 +++ src/util/StdioParser.cpp d4cba87dc4415fc989015ccadcc95ba28b3e50ae @@ -31,7 +31,6 @@ bool StdioParser::parse() errorCode = getNumber(); I(getNext() == ':'); chunkType = getNext(); - I(chunkType == 'm' || chunkType == 'l'); I(getNext() == ':'); chunkSize = getNumber(); I(getNext() == ':'); @@ -42,7 +41,20 @@ bool StdioParser::parse() return false; } - payload = getNext(chunkSize); + if (chunkType == 'm' || chunkType == 'l') + { + payload = getNext(chunkSize); + } + else + if (chunkType == 't') + { + parseTicks(getNext(chunkSize)); + } + else + { + oobMessages.push_back(qMakePair(chunkType, getNext(chunkSize))); + } + return true; } @@ -69,3 +81,56 @@ int StdioParser::getNumber() return number; } +void StdioParser::parseTicks(const QByteArray & ticks) +{ + I(ticks.endsWith(';')); + + QList tickerList = ticks.left(ticks.size() - 1).split(';'); + I(tickerList.size() > 0); + + foreach (QByteArray tick, tickerList) + { + I(tick.size() > 0); + char ident = tick.at(0); + tick.remove(0, 1); + + if (!tickers.contains(ident)) + { + tickers[ident] = Ticker(); + } + + if (tick.size() == 0) + { + tickers[ident].complete = true; + continue; + } + + if (tick.at(0) == ':') + { + tick.remove(0, 1); + tickers[ident] = Ticker(tick); + continue; + } + + if (tick.at(0) == '=') + { + tick.remove(0, 1); + bool ok; + tickers[ident].total = tick.toUInt(&ok); + I(ok); + continue; + } + + if (tick.at(0) == '#') + { + tick.remove(0, 1); + bool ok; + tickers[ident].progress = tick.toUInt(&ok); + I(ok); + continue; + } + + I(false); + } +} + ============================================================ --- src/util/StdioParser.h 5f6b6793087a0d3d884c32f1cfa786632c67c647 +++ src/util/StdioParser.h 280e28221050eecf94aac14355dc0f80c09f7dd3 @@ -19,6 +19,7 @@ #ifndef STDIO_PARSER_H #define STDIO_PARSER_H +#include "vocab.h" #include "AbstractParser.h" class StdioParser : public AbstractParser @@ -32,15 +33,19 @@ public: inline char getChunkType() const { return chunkType; } inline int getChunkSize() const { return chunkSize; } inline QByteArray getPayload() const { return payload; } + inline TickerMap getTickers() const { return tickers; } private: int getNumber(); + void parseTicks(const QByteArray &); int commandNumber; int errorCode; char chunkType; int chunkSize; QByteArray payload; + TickerMap tickers; + QList > oobMessages; }; #endif ============================================================ --- src/view/dialogs/DatabaseDialogManager.cpp 903fe5513823171829c2d7feaefc5996bcd05a7d +++ src/view/dialogs/DatabaseDialogManager.cpp bf76575815a0ab6bf17bb5c75fe52a1f4c5db4a5 @@ -22,7 +22,8 @@ DatabaseDialogManager::DatabaseDialogMan DatabaseDialogManager::DatabaseDialogManager(QWidget * parent) : DialogManager(parent), changesetBrowser(0), checkoutRevision(0), fileDiff(0), fileHistory(0), generateKeypair(0), - keyManagement(0), revisionDiff(0), revisionManifest(0), selectRevision(0) + netsync(0), keyManagement(0), revisionDiff(0), + revisionManifest(0), selectRevision(0) {} DatabaseDialogManager::~DatabaseDialogManager() @@ -37,6 +38,7 @@ void DatabaseDialogManager::cleanup() if (fileDiff) { delete fileDiff; fileDiff = 0; } if (fileHistory) { delete fileHistory; fileHistory = 0; } if (generateKeypair) { delete generateKeypair; generateKeypair = 0; } + if (netsync) { delete netsync; netsync = 0; } if (keyManagement) { delete keyManagement; keyManagement = 0; } if (revisionDiff) { delete revisionDiff; revisionDiff = 0; } if (revisionManifest) { delete revisionManifest; revisionManifest = 0; } @@ -50,6 +52,7 @@ void DatabaseDialogManager::closeAllDial if (fileDiff) fileDiff->close(); if (fileHistory) fileHistory->close(); if (generateKeypair) generateKeypair->close(); + if (netsync) netsync->close(); if (keyManagement) keyManagement->close(); if (revisionDiff) revisionDiff->close(); if (revisionManifest) revisionManifest->close(); @@ -150,6 +153,17 @@ void DatabaseDialogManager::showGenerate showDialog(generateKeypair); } +void DatabaseDialogManager::showNetsync() +{ + if (!netsync) + { + netsync = new Netsync(parentWidget(), databaseFile); + } + + netsync->init(); + showDialog(netsync); +} + void DatabaseDialogManager::showKeyManagement() { if (!keyManagement) ============================================================ --- src/view/dialogs/DatabaseDialogManager.h 532a5c5e04720356b89b3584d40833c422ed2a26 +++ src/view/dialogs/DatabaseDialogManager.h 1a090d7cc52c821066410946fbcdd592158eddda @@ -25,6 +25,7 @@ #include "FileDiff.h" #include "FileHistory.h" #include "GenerateKeypair.h" +#include "Netsync.h" #include "KeyManagement.h" #include "RevisionDiff.h" #include "RevisionManifest.h" @@ -50,6 +51,7 @@ public slots: virtual void showFileDiff(const QString & file, const QString & base, const QString & target); void showFileHistory(const QString & file, const QString & startRevision); void showGenerateKeypair(); + void showNetsync(); void showKeyManagement(); virtual void showRevisionDiff(const QString & path, const QString & base, const QString & target); void showRevisionManifest(const QString & revision); @@ -61,6 +63,7 @@ protected: FileDiff * fileDiff; FileHistory * fileHistory; GenerateKeypair * generateKeypair; + Netsync * netsync; KeyManagement * keyManagement; RevisionDiff * revisionDiff; RevisionManifest * revisionManifest; ============================================================ --- src/view/mainwindows/DatabaseWindow.cpp 37323d919fecd0fda6c5266a8af16203b88edf04 +++ src/view/mainwindows/DatabaseWindow.cpp 32afece8ed33326fca00fe4180fcc440dcf07256 @@ -63,6 +63,11 @@ void DatabaseWindow::init() ); connect( + menuBar, SIGNAL(showNetsync()), + dialogManager, SLOT(showNetsync()) + ); + + connect( menuBar, SIGNAL(showChangesetBrowser()), dialogManager, SLOT(showChangesetBrowser()) ); @@ -148,7 +153,7 @@ void DatabaseWindow::load(const QString Settings::addItemToList("RecentDatabaseList", path, 5); - reinterpret_cast(dialogManager)->init(databaseFile); - databaseVariables->setDatabaseFile(databaseFile); + reinterpret_cast(dialogManager)->init(databaseFile); + databaseVariables->setDatabaseFile(databaseFile); } ============================================================ --- src/view/widgets/DatabaseMenuBar.cpp 24128d740e1d3b1b08839bf94d1f348b96bf1fd4 +++ src/view/widgets/DatabaseMenuBar.cpp ce3a7aa362d08e1f816cd9456b8d3fd555a4fadc @@ -23,6 +23,8 @@ DatabaseMenuBar::DatabaseMenuBar(QWidget // // The Database menu // + actionNetsync = new QAction(tr("Network synchronisation"), this); + actionNetsync->setShortcut(tr("F5")); actionChangeset_browser = new QAction(tr("Changeset browser"), this); actionChangeset_browser->setShortcut(tr("Ctrl+B")); actionKey_management = new QAction(tr("Key management"), this); @@ -32,6 +34,8 @@ DatabaseMenuBar::DatabaseMenuBar(QWidget menuDatabase = new QMenu(tr("Database"), this); + menuDatabase->addAction(actionNetsync); + menuDatabase->addSeparator(); menuDatabase->addAction(actionChangeset_browser); menuDatabase->addAction(actionKey_management); menuDatabase->addSeparator(); @@ -41,6 +45,11 @@ DatabaseMenuBar::DatabaseMenuBar(QWidget insertMenu(menuWindow->menuAction(), menuDatabase); connect( + actionNetsync, SIGNAL(triggered()), + this, SIGNAL(showNetsync()) + ); + + connect( actionChangeset_browser, SIGNAL(triggered()), this, SIGNAL(showChangesetBrowser()) ); ============================================================ --- src/view/widgets/DatabaseMenuBar.h b100fad7678bb201825278f5d496dc29bd43fe26 +++ src/view/widgets/DatabaseMenuBar.h e891dcedbca13fdf865e320f4a6026e1c5eadbb3 @@ -31,11 +31,13 @@ signals: void addDockWidgetAction(QAction * action); signals: + void showNetsync(); void showCheckoutRevision(); void showChangesetBrowser(); void showKeyManagement(); protected: + QAction * actionNetsync; QAction * actionKey_management; QAction * actionChangeset_browser; QAction * actionCheckout_revision; ============================================================ --- src/vocab.h 1b2725cb1d597d7dd4035b6763f47bf02eb062cb +++ src/vocab.h 47f7cac5d9cfea1f8308980d3de9268b1e97a674 @@ -96,6 +96,61 @@ typedef QList StanzaList; typedef QList Stanza; typedef QList StanzaList; +struct Ticker +{ + QByteArray name; + int total; + int progress; + bool complete; + + Ticker(const QByteArray & n = QByteArray()) + : name(n), total(0), progress(0), complete(false) {} + + /** + * Updates this ticker's values with the values from another one + * (of the same type) + */ + void update(const Ticker & other) + { + if (name.isEmpty()) name = other.name; + if (total < other.total) total = other.total; + if (progress < other.progress) progress = other.progress; + if (!complete && other.complete) complete = true; + } + + QString toString(bool shortened = true) const + { + if (complete) + return QObject::tr("%1: completed") + .arg(QString(name)); + + QString prog; + if (shortened && progress > (1024 * 1024)) + prog = QString("%1M").arg((double)progress / (1024 * 1024), 0, 'f', 1); + else if (shortened && progress > 1024) + prog = QString("%1K").arg((double)progress / 1024, 0, 'f', 1); + else + prog = QString::number(progress); + + if (total == 0) + return QObject::tr("%1: %2") + .arg(QString(name)).arg(prog); + + QString tot; + if (shortened && total > (1024 * 1024)) + tot = QString("%1M").arg((double)total / (1024 * 1024), 0, 'f', 1); + else if (shortened && total > 1024) + tot = QString("%1K").arg((double)total / 1024, 0, 'f', 1); + else + tot = QString::number(total); + + return QObject::tr("%1: %2 / %3") + .arg(QString(name)).arg(prog).arg(tot); + } +}; + +typedef QMap TickerMap; + // FIXME: just a few stupid typedefs which should be expanded later on // to more "intelligent" objects typedef QString GuitoneException;