reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit f94f80c7bcc5e7db9dba8ae590f7dd87a12365a9
parent 0e6a47df9bbb962e0bcf65ad49fe477bce1c51c7
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Mon,  5 Jun 2017 23:43:23 -0400

Merge branch 'multi-import'

Diffstat:
Msrc/api.cpp | 16++++------------
Msrc/archive.cpp | 4----
Msrc/download.cpp | 2--
Msrc/encoding.hpp | 4++--
Msrc/import.cpp | 238++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/import.hpp | 24++++++++++++++++++------
Msrc/main.cpp | 4++--
Msrc/manager.cpp | 4++--
Msrc/reapack.cpp | 16++++++++++++++--
Msrc/reapack.hpp | 2+-
Msrc/resource.rc | 21+++++++++++----------
Msrc/thread.cpp | 4+++-
12 files changed, 196 insertions(+), 143 deletions(-)

diff --git a/src/api.cpp b/src/api.cpp @@ -327,8 +327,8 @@ autoInstall: usually set to 2 (obey user setting).)", reapack->setRemoteEnabled(enable, existing); } - - reapack->config()->remotes.add(remote); + else + reapack->config()->remotes.add(remote); } catch(const reapack_error &e) { if(errorOut) @@ -341,16 +341,8 @@ autoInstall: usually set to 2 (obey user setting).)", return false; } - if(commit) { - if(reapack->hasTransaction()) - reapack->setupTransaction()->runTasks(); - else { - reapack->refreshManager(); - reapack->refreshBrowser(); - } - - reapack->config()->write(); - } + if(commit) + reapack->commitConfig(); return true; }); diff --git a/src/archive.cpp b/src/archive.cpp @@ -222,8 +222,6 @@ FileExtractor::FileExtractor(const Path &target, const ArchiveReaderPtr &reader) bool FileExtractor::run() { - ThreadNotifier::get()->notify({this, Running}); - ofstream stream; if(!FS::open(stream, m_path.temp())) { setError({FS::lastError(), m_path.temp().join()}); @@ -360,8 +358,6 @@ FileCompressor::FileCompressor(const Path &target, const ArchiveWriterPtr &write bool FileCompressor::run() { - ThreadNotifier::get()->notify({this, Running}); - ifstream stream; if(!FS::open(stream, m_path)) { setError({FS::lastError(), m_path.join()}); diff --git a/src/download.cpp b/src/download.cpp @@ -115,8 +115,6 @@ void Download::setName(const string &name) bool Download::run() { - ThreadNotifier::get()->notify({this, Running}); - ostream *stream = openStream(); if(!stream) return false; diff --git a/src/encoding.hpp b/src/encoding.hpp @@ -47,9 +47,9 @@ typedef std::string auto_string; #define AUTO_STR(text) text #define to_autostring std::to_string #define auto_snprintf snprintf -#define make_autostring(string) string +#define make_autostring(string) (string) #define make_autocstring(cstr) cstr -#define from_autostring(string) string +#define from_autostring(string) (string) #endif diff --git a/src/import.cpp b/src/import.cpp @@ -31,11 +31,11 @@ using namespace std; -static const auto_char *TITLE = AUTO_STR("ReaPack: Import a repository"); +static const auto_char *TITLE = AUTO_STR("ReaPack: Import repositories"); static const string DISCOVER_URL = "https://reapack.com/repos"; Import::Import(ReaPack *reapack) - : Dialog(IDD_IMPORT_DIALOG), m_reapack(reapack), m_download(nullptr) + : Dialog(IDD_IMPORT_DIALOG), m_reapack(reapack), m_pool(nullptr), m_state(OK) { } @@ -48,7 +48,6 @@ void Import::onInit() m_url = getControl(IDC_URL); m_progress = getControl(IDC_PROGRESS); m_discover = getControl(IDC_DISCOVER); - m_ok = getControl(IDOK); #ifdef PBM_SETMARQUEE SendMessage(m_progress, PBM_SETMARQUEE, 1, 0); @@ -65,10 +64,14 @@ void Import::onCommand(const int id, int) openURL(DISCOVER_URL); break; case IDCANCEL: - if(m_download) - m_download->abort(); - - close(); + if(m_pool) { + disable(getControl(IDOK)); + disable(getControl(IDCANCEL)); + m_pool->abort(); + m_state = Close; + } + else + close(); break; } } @@ -81,133 +84,170 @@ void Import::onTimer(int) #endif } -void Import::fetch() +ThreadPool *Import::setupPool() { - if(m_download) - return; + if(!m_pool) { + m_state = OK; + m_pool = new ThreadPool; - string url = getText(m_url); - boost::algorithm::trim(url); + m_pool->onAbort([=] { if(!m_state) m_state = Aborted; }); + m_pool->onDone([=] { + setWaiting(false); - if(url.empty()) { - close(); - return; - } + if(m_state == OK) + processQueue(); - setWaiting(true); + queue<ImportData> clear; + m_queue.swap(clear); - const auto &opts = m_reapack->config()->network; - MemoryDownload *dl = m_download = new MemoryDownload(url, opts); - - dl->onFinish([=] { - const ThreadTask::State state = dl->state(); - if(state == ThreadTask::Aborted) { - // at this point `this` is deleted, so there is nothing else - // we can do without crashing - return; - } + delete m_pool; + m_pool = nullptr; - setWaiting(false); + if(m_state == Close) + close(); + else + SetFocus(m_url); + }); + } - if(state != ThreadTask::Success) { - const string msg = "Download failed: " + dl->error().message; - MessageBox(handle(), make_autostring(msg).c_str(), TITLE, MB_OK); - SetFocus(m_url); - return; - } + return m_pool; +} - read(); - }); +void Import::fetch() +{ + if(m_pool) // ignore repeated presses on OK + return; - dl->setCleanupHandler([=] { - // if we are still alive - if(dl->state() != ThreadTask::Aborted) - m_download = nullptr; + const auto &opts = m_reapack->config()->network; - delete dl; - }); + stringstream stream(getText(m_url)); + string url; + while(getline(stream, url)) { + boost::algorithm::trim(url); + + if(url.empty()) + continue; + + MemoryDownload *dl = new MemoryDownload(url, opts); + + dl->onFinish([=] { + switch(dl->state()) { + case ThreadTask::Success: + // copy for later use, as `dl` won't be around after this callback + if(!read(dl)) + m_pool->abort(); + break; + case ThreadTask::Failure: { + auto_char msg[1024]; + auto_snprintf(msg, auto_size(msg), + AUTO_STR("Download failed: %s\r\n\r\nURL: %s"), + make_autostring(dl->error().message).c_str(), make_autostring(url).c_str()); + MessageBox(handle(), msg, TITLE, MB_OK); + m_pool->abort(); + break; + } + default: + break; + } + }); + + setupPool()->push(dl); + } - dl->start(); + if(m_pool) + setWaiting(true); + else + close(); } -void Import::read() +bool Import::read(MemoryDownload *dl) { - assert(m_download); + auto_char msg[1024]; try { - IndexPtr index = Index::load({}, m_download->contents().c_str()); - close(import({index->name(), m_download->url()})); + IndexPtr index = Index::load({}, dl->contents().c_str()); + Remote remote = m_reapack->remote(index->name()); + const bool exists = !remote.isNull(); + + if(exists && remote.url() != dl->url()) { + if(remote.isProtected()) { + auto_snprintf(msg, auto_size(msg), + AUTO_STR("The repository %s is protected and cannot be overwritten."), + make_autostring(index->name()).c_str()); + MessageBox(handle(), msg, TITLE, MB_OK); + return false; + } + else { + auto_snprintf(msg, auto_size(msg), + AUTO_STR("%s is already configured with a different URL.\r\n") + AUTO_STR("Do you want to overwrite it?"), + make_autostring(index->name()).c_str()); + + const auto answer = MessageBox(handle(), msg, TITLE, MB_YESNO); + + if(answer != IDYES) + return true; + } + } + else if(exists && remote.isEnabled()) // url is also the same + return true; // nothing to do + + remote.setName(index->name()); + remote.setUrl(dl->url()); + m_queue.push({remote, dl->contents()}); + + return true; } catch(const reapack_error &e) { - const string msg = "The received file is invalid: " + string(e.what()); - MessageBox(handle(), make_autostring(msg).c_str(), TITLE, MB_OK); - SetFocus(m_url); + auto_snprintf(msg, auto_size(msg), + AUTO_STR("The received file is invalid: %s\r\n%s"), + make_autostring(string(e.what())).c_str(), + make_autostring(dl->url()).c_str()); + MessageBox(handle(), msg, TITLE, MB_OK); + return false; } } -bool Import::import(const Remote &remote) +void Import::processQueue() { - auto_char msg[1024]; + bool ok = true; - if(const Remote &existing = m_reapack->remote(remote.name())) { - if(existing.isProtected()) { - MessageBox(handle(), - AUTO_STR("This repository is protected and cannot be overwritten."), - TITLE, MB_OK); + while(!m_queue.empty()) { + if(!import(m_queue.front())) + ok = false; - return true; - } - else if(existing.url() != remote.url()) { - auto_snprintf(msg, auto_size(msg), - AUTO_STR("%s is already configured with a different URL.\r\n") - AUTO_STR("Do you want to overwrite it?"), - make_autostring(remote.name()).c_str()); + m_queue.pop(); + } - const auto answer = MessageBox(handle(), msg, TITLE, MB_YESNO); + if(ok) + m_state = Close; - if(answer != IDYES) - return false; - } - else if(existing.isEnabled()) { - auto_snprintf(msg, auto_size(msg), - AUTO_STR("%s is already configured.\r\nNothing to do!"), - make_autostring(remote.name()).c_str()); - MessageBox(handle(), msg, TITLE, MB_OK); + m_reapack->commitConfig(); +} +bool Import::import(const ImportData &data) +{ + if(!data.remote.isEnabled()) { + if(Transaction *tx = m_reapack->setupTransaction()) { + m_reapack->enable(data.remote); + tx->synchronize(data.remote); return true; } - else { - Transaction *tx = m_reapack->setupTransaction(); - - if(!tx) - return true; - - m_reapack->enable(existing); - tx->runTasks(); - - m_reapack->config()->write(); + else + return false; + } - auto_snprintf(msg, auto_size(msg), AUTO_STR("%s has been enabled."), - make_autostring(remote.name()).c_str()); - MessageBox(handle(), msg, TITLE, MB_OK); + Config *config = m_reapack->config(); + config->remotes.add(data.remote); + if(config->install.autoInstall) { + if(Transaction *tx = m_reapack->setupTransaction()) { + tx->synchronize(data.remote); return true; } } - Config *config = m_reapack->config(); - config->remotes.add(remote); - config->write(); - - FS::write(Index::pathFor(remote.name()), m_download->contents()); - - auto_snprintf(msg, auto_size(msg), - AUTO_STR("%s has been successfully imported into your repository list."), - make_autostring(remote.name()).c_str()); - MessageBox(handle(), msg, TITLE, MB_OK); - - m_reapack->refreshManager(); - m_reapack->refreshBrowser(); + FS::write(Index::pathFor(data.remote.name()), data.contents); return true; } diff --git a/src/import.hpp b/src/import.hpp @@ -20,13 +20,14 @@ #include "dialog.hpp" -#include "encoding.hpp" +#include "remote.hpp" +#include <queue> #include <string> class MemoryDownload; class ReaPack; -class Remote; +class ThreadPool; class Import : public Dialog { @@ -39,19 +40,30 @@ protected: void onTimer(int) override; private: + enum State { + OK, + Aborted, + Close, + }; + + struct ImportData { Remote remote; std::string contents; }; + + ThreadPool *setupPool(); void fetch(); - void read(); - bool import(const Remote &); + bool read(MemoryDownload *); + void processQueue(); + bool import(const ImportData &); void setWaiting(bool); ReaPack *m_reapack; - MemoryDownload *m_download; + ThreadPool *m_pool; + State m_state; + std::queue<ImportData> m_queue; short m_fakePos; HWND m_url; HWND m_progress; HWND m_discover; - HWND m_ok; }; #endif diff --git a/src/main.cpp b/src/main.cpp @@ -92,7 +92,7 @@ static void menuHook(const char *name, HMENU handle, int f) menu.addAction(AUTO_STR("&Browse packages..."), NamedCommandLookup("_REAPACK_BROWSE")); - menu.addAction(AUTO_STR("&Import a repository..."), + menu.addAction(AUTO_STR("&Import repositories..."), NamedCommandLookup("_REAPACK_IMPORT")); menu.addAction(AUTO_STR("&Manage repositories..."), @@ -149,7 +149,7 @@ static void setupActions() reapack->setupAction("REAPACK_BROWSE", "ReaPack: Browse packages...", &reapack->browseAction, bind(&ReaPack::browsePackages, reapack)); - reapack->setupAction("REAPACK_IMPORT", "ReaPack: Import a repository...", + reapack->setupAction("REAPACK_IMPORT", "ReaPack: Import repositories...", &reapack->importAction, bind(&ReaPack::importRemote, reapack)); reapack->setupAction("REAPACK_MANAGE", "ReaPack: Manage repositories...", diff --git a/src/manager.cpp b/src/manager.cpp @@ -466,7 +466,7 @@ void Manager::copyUrl() void Manager::importExport() { Menu menu; - menu.addAction(AUTO_STR("Import a &repository..."), ACTION_IMPORT_REPO); + menu.addAction(AUTO_STR("Import &repositories..."), ACTION_IMPORT_REPO); menu.addSeparator(); menu.addAction(AUTO_STR("Import offline archive..."), ACTION_IMPORT_ARCHIVE); menu.addAction(AUTO_STR("&Export offline archive..."), ACTION_EXPORT_ARCHIVE); @@ -692,8 +692,8 @@ bool Manager::apply() if(syncAll) m_reapack->synchronizeAll(); + tx->onFinish(bind(&Config::write, m_config)); tx->runTasks(); - m_config->write(); reset(); return true; diff --git a/src/reapack.cpp b/src/reapack.cpp @@ -182,7 +182,6 @@ void ReaPack::setRemoteEnabled(const bool enable, const Remote &remote) return; m_config->remotes.add(copy); - refreshManager(); }); } @@ -331,7 +330,6 @@ Transaction *ReaPack::setupTransaction() Dialog::Show<Report>(m_instance, m_mainWindow, *m_tx->receipt()); } - }); m_tx->setObsoleteHandler([=] (vector<Registry::Entry> &entries) { @@ -358,6 +356,20 @@ void ReaPack::teardownTransaction() refreshBrowser(); } +void ReaPack::commitConfig() +{ + if(m_tx) { + m_tx->onFinish(bind(&ReaPack::refreshManager, this)); + m_tx->onFinish(bind(&Config::write, m_config)); + m_tx->runTasks(); + } + else { + refreshManager(); + refreshBrowser(); + m_config->write(); + } +} + void ReaPack::refreshManager() { if(m_manager) diff --git a/src/reapack.hpp b/src/reapack.hpp @@ -77,8 +77,8 @@ public: Remote remote(const std::string &name) const; - bool hasTransaction() const { return m_tx != nullptr; } Transaction *setupTransaction(); + void commitConfig(); Config *config() const { return m_config; } private: diff --git a/src/resource.rc b/src/resource.rc @@ -69,19 +69,20 @@ BEGIN DEFPUSHBUTTON "&Close", IDOK, 409, 248, 45, 14 END -IDD_IMPORT_DIALOG DIALOGEX 0, 0, 290, 69 +IDD_IMPORT_DIALOG DIALOGEX 0, 0, 290, 83 STYLE DIALOG_STYLE FONT DIALOG_FONT BEGIN - GROUPBOX "Repository settings", IDC_GROUPBOX, 2, 4, 287, 41 - RTEXT "Index URL:", IDC_LABEL, 5, 19, 50, 10 - EDITTEXT IDC_URL, 60, 16, 220, 14, ES_AUTOHSCROLL - LTEXT "Type or paste the URL to a repository index in the box above.", - IDC_LABEL2, 60, 33, 220, 10 - CONTROL "", IDC_PROGRESS, PROGRESS_CLASS, PBS_MARQUEE | NOT WS_VISIBLE, 10, 54, 150, 5 - PUSHBUTTON "&Discover repositories...", IDC_DISCOVER, 5, 50, 90, 14 - DEFPUSHBUTTON "&OK", IDOK, 201, 50, 40, 14 - PUSHBUTTON "&Cancel", IDCANCEL, 244, 50, 40, 14 + LTEXT "Type or paste one or more repository index URLs (one per line):", + IDC_LABEL, 5, 5, 280, 10 +#ifdef __APPLE__ + GROUPBOX "", IDC_GROUPBOX, 3, 9, 284, 51 +#endif + EDITTEXT IDC_URL, 5, 18, 278, 40, WS_VSCROLL | ES_MULTILINE | ES_WANTRETURN + CONTROL "", IDC_PROGRESS, PROGRESS_CLASS, PBS_MARQUEE | NOT WS_VISIBLE, 10, 68, 150, 5 + PUSHBUTTON "&Discover repositories...", IDC_DISCOVER, 5, 64, 90, 14 + DEFPUSHBUTTON "&OK", IDOK, 201, 64, 40, 14 + PUSHBUTTON "&Cancel", IDCANCEL, 244, 64, 40, 14 END IDD_BROWSER_DIALOG DIALOGEX 0, 0, 500, 250 diff --git a/src/thread.cpp b/src/thread.cpp @@ -68,8 +68,10 @@ void ThreadTask::exec() { State state = Aborted; - if(!aborted()) + if(!aborted()) { + ThreadNotifier::get()->notify({this, Running}); state = run() ? Success : Failure; + } ThreadNotifier::get()->notify({this, state}); };