reapack

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

commit 6138523350544f45273f99c9271cba69e60d4ccb
parent 27b73dc07ba24302ddc410355170b21f2011fdfe
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Fri, 12 Feb 2016 20:59:24 -0500

allow .ReaPackRemote import from an URL

Diffstat:
Msrc/dialog.cpp | 38+++++++++++++++++++++++++++++---------
Msrc/dialog.hpp | 12+++++++++---
Asrc/import.cpp | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/import.hpp | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/reapack.cpp | 38++++++++++++--------------------------
Msrc/reapack.hpp | 1+
Msrc/remote.cpp | 11+++++++----
Msrc/remote.hpp | 2++
Msrc/resource.hpp | 30+++++++++++++++++++-----------
Msrc/resource.rc | 16++++++++++++++++
Mtest/remote.cpp | 11+++++++++++
11 files changed, 369 insertions(+), 53 deletions(-)

diff --git a/src/dialog.cpp b/src/dialog.cpp @@ -62,7 +62,7 @@ WDL_DLGRET Dialog::Proc(HWND handle, UINT msg, WPARAM wParam, LPARAM lParam) break; #endif case WM_TIMER: - dlg->onTimer(); + dlg->onTimer((int)wParam); break; case WM_COMMAND: dlg->onCommand(LOWORD(wParam)); @@ -130,14 +130,9 @@ void Dialog::Destroy(Dialog *dlg) delete dlg; } -void Dialog::show(HWND handle) +void Dialog::setVisible(const bool visible, HWND handle) { - ShowWindow(handle, SW_SHOW); -} - -void Dialog::hide(HWND handle) -{ - ShowWindow(handle, SW_HIDE); + ShowWindow(handle, visible ? SW_SHOW : SW_HIDE); } void Dialog::close(const INT_PTR result) @@ -179,6 +174,27 @@ void Dialog::setEnabled(const bool enabled, HWND handle) EnableWindow(handle, enabled); } +int Dialog::startTimer(const int ms, int id) +{ + if(id == 0) { + if(m_timers.empty()) + id = 1; + else + id = *m_timers.rbegin(); + } + + m_timers.insert(id); + SetTimer(m_handle, id, ms, nullptr); + + return id; +} + +void Dialog::stopTimer(int id) +{ + KillTimer(m_handle, id); + m_timers.erase(id); +} + HWND Dialog::getControl(const int idc) { return GetDlgItem(m_handle, idc); @@ -197,7 +213,7 @@ void Dialog::onHide() { } -void Dialog::onTimer() +void Dialog::onTimer(int) { } @@ -224,4 +240,8 @@ void Dialog::onContextMenu(HWND, int, int) void Dialog::onDestroy() { + const set<int> timers = m_timers; // make a copy + + for(const int id : timers) + stopTimer(id); } diff --git a/src/dialog.hpp b/src/dialog.hpp @@ -19,6 +19,7 @@ #define REAPACK_DIALOG_HPP #include <map> +#include <set> #include <wdltypes.h> #include <reaper_plugin.h> @@ -72,13 +73,17 @@ public: void setEnabled(bool enable) { setEnabled(enable, m_handle); } void setEnabled(bool, HWND); - void show(HWND); void show() { show(m_handle); } - void hide(HWND); + void show(HWND handle) { setVisible(true, handle); } void hide() { hide(m_handle); } + void hide(HWND handle) { setVisible(false, handle); } + void setVisible(bool visible) { setVisible(visible, m_handle); } + void setVisible(bool, HWND); void close(INT_PTR = 0); void center(); void setFocus(); + int startTimer(int elapse, int id = 0); + void stopTimer(int id); protected: Dialog(int templateId); @@ -103,7 +108,7 @@ protected: virtual void onInit(); virtual void onShow(); virtual void onHide(); - virtual void onTimer(); + virtual void onTimer(int); virtual void onCommand(int); virtual void onNotify(LPNMHDR, LPARAM); virtual void onContextMenu(HWND, int x, int y); @@ -122,6 +127,7 @@ private: HWND m_handle; std::map<int, Control *> m_controls; + std::set<int> m_timers; }; class LockDialog { diff --git a/src/import.cpp b/src/import.cpp @@ -0,0 +1,204 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "import.hpp" + +#include "download.hpp" +#include "encoding.hpp" +#include "reapack.hpp" +#include "remote.hpp" +#include "resource.hpp" + +#include <cerrno> +#include <sstream> + +#include <reaper_plugin_functions.h> + +#ifndef _WIN32 // for SWELL +#define SetWindowTextA SetWindowText +#endif + +using namespace std; + +const char *Import::TITLE = "ReaPack: Import a remote repository"; +const char *SUBTITLE = "Select a .ReaPackRemote file"; + +Import::Import(ReaPack *reapack) + : Dialog(IDD_IMPORT_DIALOG), m_reapack(reapack), m_download(nullptr) +{ +} + +void Import::onInit() +{ + SetWindowTextA(handle(), TITLE); + SetWindowTextA(getControl(IDC_GROUPBOX), SUBTITLE); + + m_url = getControl(IDC_URL); + m_file = getControl(IDC_FILE); + m_progress = getControl(IDC_PROGRESS); + m_ok = getControl(IDOK); + + hide(m_progress); + +#ifdef PBM_SETMARQUEE + SendMessage(m_progress, PBM_SETMARQUEE, 1, 0); +#endif +} + +void Import::onCommand(const int id) +{ + switch(id) { + case IDC_BROWSE: + browseFile(); + break; + case IDOK: + import(); + break; + case IDCANCEL: + if(m_download) + m_download->abort(); + + close(); + break; + } +} + +void Import::onTimer(int) +{ +#ifndef PBM_SETMARQUEE + m_fakePos = (m_fakePos + 1) % 100; + SendMessage(m_progress, PBM_SETPOS, m_fakePos, 0); +#endif +} + +void Import::browseFile() +{ + string path(4096, 0); + if(!GetUserFileNameForRead(&path[0], SUBTITLE, "ReaPackRemote")) { + SetFocus(handle()); + return; + } + + SetWindowText(m_file, make_autostring(path).c_str()); + import(true); +} + +void Import::import(const bool fileOnly) +{ + auto_string input(4096, 0); + + if(!fileOnly) { + GetWindowText(m_url, &input[0], (int)input.size()); + + if(input[0] != 0) { + download(from_autostring(input)); + return; + } + } + + GetWindowText(m_file, &input[0], (int)input.size()); + + if(input[0] == 0) { + close(); + return; + } + + Remote::ReadCode code; + if(!import(Remote::fromFile(from_autostring(input), &code), code)) + SetFocus(m_file); +} + +bool Import::import(const Remote &remote, const Remote::ReadCode code) +{ + switch(code) { + case Remote::ReadFailure: + ShowMessageBox(strerror(errno), TITLE, 0); + return false; + case Remote::InvalidName: + ShowMessageBox("Invalid .ReaPackRemote file! (invalid name)", TITLE, 0); + return false; + case Remote::InvalidUrl: + ShowMessageBox("Invalid .ReaPackRemote file! (invalid url)", TITLE, 0); + return false; + default: + break; + }; + + m_reapack->import(remote); + close(); + + return true; +} + +void Import::download(const string &url) +{ + if(m_download) + return; + + setWaiting(true); + + Download *dl = new Download({}, url); + dl->onFinish([=] { + const Download::State state = dl->state(); + if(state == Download::Aborted) { + // we are deleted here, so there is nothing much we can do without crashing + return; + } + + setWaiting(false); + + if(state != Download::Success) { + ShowMessageBox(dl->contents().c_str(), "Download Failed", MB_OK); + SetFocus(m_url); + return; + } + + istringstream stream(dl->contents()); + + Remote::ReadCode code; + if(!import(Remote::fromFile(stream, &code), code)) + SetFocus(m_url); + }); + + dl->onDestroy([=] { + // if we are still alive + if(dl->state() != Download::Aborted) + m_download = nullptr; + + delete dl; + }); + + dl->start(); + + m_download = dl; +} + +void Import::setWaiting(const bool wait) +{ + setVisible(wait, m_progress); + setEnabled(!wait, m_url); + +#ifndef PBM_SETMARQUEE + if(wait) + startTimer(42, 1); + else + stopTimer(1); + + m_fakePos = 0; + SendMessage(m_progress, PBM_SETPOS, 0, 0); +#endif +} diff --git a/src/import.hpp b/src/import.hpp @@ -0,0 +1,59 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef REAPACK_IMPORT_HPP +#define REAPACK_IMPORT_HPP + +#include "dialog.hpp" + +#include "remote.hpp" + +#include <string> + +class Download; +class ReaPack; + +class Import : public Dialog +{ +public: + static const char *TITLE; + + Import(ReaPack *); + +protected: + void onInit() override; + void onCommand(int) override; + void onTimer(int) override; + +private: + void browseFile(); + void import(bool fileOnly = false); + bool import(const Remote &remote, Remote::ReadCode code); + void download(const std::string &url); + void setWaiting(bool); + + ReaPack *m_reapack; + Download *m_download; + short m_fakePos; + + HWND m_url; + HWND m_file; + HWND m_progress; + HWND m_ok; +}; + +#endif diff --git a/src/reapack.cpp b/src/reapack.cpp @@ -19,6 +19,7 @@ #include "config.hpp" #include "errors.hpp" +#include "import.hpp" #include "index.hpp" #include "manager.hpp" #include "progress.hpp" @@ -162,29 +163,11 @@ void ReaPack::uninstall(const Remote &remote) void ReaPack::importRemote() { - static const char *title = "ReaPack: Import remote repository"; - - string path(4096, 0); - if(!GetUserFileNameForRead(&path[0], title, "ReaPackRemote")) - return; - - Remote::ReadCode code; - const Remote remote = Remote::fromFile(path, &code); - - switch(code) { - case Remote::ReadFailure: - ShowMessageBox(strerror(errno), title, 0); - return; - case Remote::InvalidName: - ShowMessageBox("Invalid .ReaPackRemote file! (invalid name)", title, 0); - return; - case Remote::InvalidUrl: - ShowMessageBox("Invalid .ReaPackRemote file! (invalid url)", title, 0); - return; - default: - break; - }; + Dialog::Show<Import>(m_instance, m_mainWindow, this); +} +void ReaPack::import(const Remote &remote) +{ RemoteList *remotes = m_config->remotes(); const Remote &existing = remotes->get(remote.name()); @@ -192,7 +175,7 @@ void ReaPack::importRemote() if(!existing.isNull()) { if(existing.isProtected()) { ShowMessageBox( - "This remote is protected and cannot be overwritten.", title, MB_OK); + "This remote is protected and cannot be overwritten.", Import::TITLE, MB_OK); return; } @@ -200,7 +183,7 @@ void ReaPack::importRemote() const int button = ShowMessageBox( "This remote is already configured.\r\n" "Do you want to overwrite the current remote?" - , title, MB_YESNO); + , Import::TITLE, MB_YESNO); if(button != IDYES) return; @@ -209,7 +192,7 @@ void ReaPack::importRemote() ShowMessageBox( "This remote is already configured.\r\n" "Nothing to do!" - , title, MB_OK); + , Import::TITLE, MB_OK); return; } @@ -223,8 +206,11 @@ void ReaPack::importRemote() } } - if(!hitchhikeTransaction()) + if(!hitchhikeTransaction()) { + remotes->add(remote); + m_config->write(); return; + } m_transaction->synchronize(remote); diff --git a/src/reapack.hpp b/src/reapack.hpp @@ -53,6 +53,7 @@ public: void requireIndex(const Remote &, const std::function<void ()> &); void uninstall(const Remote &); void importRemote(); + void import(const Remote &); void manageRemotes(); void runTasks(); diff --git a/src/remote.cpp b/src/remote.cpp @@ -65,13 +65,16 @@ Remote Remote::fromFile(const string &path, ReadCode *code) return {}; } + return fromFile(file, code); +} + +Remote Remote::fromFile(istream &stream, ReadCode *code) +{ string name; - getline(file, name); + getline(stream, name); string url; - getline(file, url); - - file.close(); + getline(stream, url); if(!ValidateName(name)) { if(code) diff --git a/src/remote.hpp b/src/remote.hpp @@ -18,6 +18,7 @@ #ifndef REAPACK_REMOTE_HPP #define REAPACK_REMOTE_HPP +#include <istream> #include <map> #include <string> #include <vector> @@ -32,6 +33,7 @@ public: }; static Remote fromFile(const std::string &path, ReadCode *code = nullptr); + static Remote fromFile(std::istream &, ReadCode *code = nullptr); static Remote fromString(const std::string &data, ReadCode *code = nullptr); Remote(); diff --git a/src/resource.hpp b/src/resource.hpp @@ -25,6 +25,7 @@ #define PROGRESS_CLASS "msctls_progress32" #define WC_LISTVIEW "SysListView32" #define WC_TABCONTROL "SysTabControl32" +#define PBS_MARQUEE 0 #endif #define DIALOG_STYLE \ @@ -35,18 +36,25 @@ #define IDD_REPORT_DIALOG 101 #define IDD_CONFIG_DIALOG 102 #define IDD_ABOUT_DIALOG 103 +#define IDD_IMPORT_DIALOG 104 #define IDC_LABEL 200 -#define IDC_PROGRESS 201 -#define IDC_REPORT 202 -#define IDC_LIST 203 -#define IDC_IMPORT 204 -#define IDC_TABS 205 -#define IDC_ENABLE 206 -#define IDC_WEBSITE 207 -#define IDC_DONATE 208 -#define IDC_ABOUT 209 -#define IDC_CATEGORIES 210 -#define IDC_PACKAGES 211 +#define IDC_LABEL2 201 +#define IDC_LABEL3 202 +#define IDC_PROGRESS 210 +#define IDC_REPORT 211 +#define IDC_LIST 212 +#define IDC_IMPORT 213 +#define IDC_TABS 214 +#define IDC_ENABLE 215 +#define IDC_WEBSITE 216 +#define IDC_DONATE 217 +#define IDC_ABOUT 218 +#define IDC_CATEGORIES 219 +#define IDC_PACKAGES 220 +#define IDC_GROUPBOX 221 +#define IDC_URL 222 +#define IDC_FILE 223 +#define IDC_BROWSE 224 #endif diff --git a/src/resource.rc b/src/resource.rc @@ -61,3 +61,19 @@ BEGIN PUSHBUTTON "&Enable this repository!", IDC_ENABLE, 285, 250, 120, 14 DEFPUSHBUTTON "&Close", IDOK, 409, 250, 45, 14 END + +IDD_IMPORT_DIALOG DIALOGEX 0, 0, 290, 90 +STYLE DIALOG_STYLE +FONT DIALOG_FONT +BEGIN + GROUPBOX "", IDC_GROUPBOX, 2, 4, 287, 60 + RTEXT "From internet:", IDC_LABEL, 5, 19, 50, 10 + EDITTEXT IDC_URL, 60, 16, 220, 14, ES_AUTOHSCROLL + CTEXT "- or -", IDC_LABEL2, 0, 34, 290, 10 + RTEXT "From a file:", IDC_LABEL3, 5, 48, 50, 10 + EDITTEXT IDC_FILE, 60, 44, 177, 14, ES_AUTOHSCROLL + PUSHBUTTON "&Browse...", IDC_BROWSE, 240, 44, 40, 14 + CONTROL "", IDC_PROGRESS, PROGRESS_CLASS, PBS_MARQUEE, 10, 74, 150, 5 + DEFPUSHBUTTON "&OK", IDOK, 200, 70, 40, 14 + PUSHBUTTON "&Cancel", IDCANCEL, 244, 70, 40, 14 +END diff --git a/test/remote.cpp b/test/remote.cpp @@ -226,6 +226,17 @@ TEST_CASE("remote from file", M) { REQUIRE(code == Remote::Success); } + + SECTION("string stream") { + istringstream stream("name\nurl"); + + Remote::ReadCode code; + const Remote remote = Remote::fromFile(stream, &code); + REQUIRE(code == Remote::Success); + REQUIRE(remote.isValid()); + REQUIRE(remote.name() == "name"); + REQUIRE(remote.url() == "url"); + } }; TEST_CASE("unserialize remote", M) {