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:
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) {