reapack

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

commit 62dfea006210ce32f3ee687f9c40d56eb26adcbd
parent 7b521042c393c183abc37408bff1159e4be2353c
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Sat,  9 Jul 2016 20:31:44 -0400

merge Package Contents and Packages Histry in a single dialog

Diffstat:
Msrc/about.cpp | 349+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/about.hpp | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/browser.cpp | 45++++++++++++++-------------------------------
Msrc/browser.hpp | 5++---
Asrc/ostream.cpp | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/ostream.hpp | 42++++++++++++++++++++++++++++++++++++++++++
Msrc/reapack.cpp | 12++++++++++--
Msrc/reapack.hpp | 2+-
Msrc/report.cpp | 142++++++++++++++-----------------------------------------------------------------
Msrc/report.hpp | 51++++++---------------------------------------------
Msrc/resource.hpp | 27+++++++++++++--------------
Msrc/resource.rc | 28++++++++++++++--------------
Msrc/tabbar.cpp | 13++++---------
13 files changed, 470 insertions(+), 384 deletions(-)

diff --git a/src/about.cpp b/src/about.cpp @@ -22,6 +22,7 @@ #include "index.hpp" #include "listview.hpp" #include "menu.hpp" +#include "ostream.hpp" #include "reapack.hpp" #include "registry.hpp" #include "report.hpp" @@ -30,119 +31,168 @@ #include "tabbar.hpp" #include <boost/algorithm/string/replace.hpp> -#include <sstream> using namespace std; -enum { ACTION_HISTORY = 300, ACTION_CONTENTS }; +enum { ACTION_ABOUT_PKG = 300 }; -About::About(IndexPtr index) - : Dialog(IDD_ABOUT_DIALOG), m_index(index), - m_currentCat(-255) +AboutDialog::AboutDialog() + : Dialog(IDD_ABOUT_DIALOG), m_currentIndex(-255) { RichEdit::Init(); } -void About::onInit() +void AboutDialog::onInit() { Dialog::onInit(); - m_about = createControl<RichEdit>(IDC_ABOUT); - - m_cats = createControl<ListView>(IDC_CATEGORIES, ListView::Columns{ - {AUTO_STR("Category"), 142} - }); + auto_char title[255] = {}; + const auto_string &name = make_autostring(what()); + auto_snprintf(title, auto_size(title), AUTO_STR("About %s"), name.c_str()); + SetWindowText(handle(), title); - m_cats->onSelect(bind(&About::updatePackages, this)); + m_tabs = createControl<TabBar>(IDC_TABS, TabBar::Tabs{}); - m_packages = createControl<ListView>(IDC_PACKAGES, ListView::Columns{ - {AUTO_STR("Name"), 382}, - {AUTO_STR("Version"), 80}, - {AUTO_STR("Author"), 90}, - }); + m_menu = createMenu(); + m_menu->sortByColumn(0); + m_menu->onSelect(bind(&AboutDialog::callUpdateList, this)); - m_packages->sortByColumn(0); - m_packages->onActivate(bind(&About::packageHistory, this)); - m_packages->onContextMenu(bind(&About::fillContextMenu, - this, placeholders::_1, placeholders::_2)); + m_list = createList(); + m_list->sortByColumn(0); - m_installedFiles = getControl(IDC_LIST); - - m_tabs = createControl<TabBar>(IDC_TABS, TabBar::Tabs{ - {AUTO_STR("Description"), {m_about->handle()}}, - {AUTO_STR("Packages"), {m_cats->handle(), m_packages->handle()}}, - {AUTO_STR("Installed Files"), {m_installedFiles}}, - }); + m_report = getControl(IDC_REPORT); populate(); + m_menu->sort(); + callUpdateList(); #ifdef LVSCW_AUTOSIZE_USEHEADER - m_cats->resizeColumn(m_cats->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER); - m_packages->resizeColumn(m_packages->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER); + m_menu->resizeColumn(m_menu->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER); + m_list->resizeColumn(m_list->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER); #endif } -void About::onCommand(const int id, int) +void AboutDialog::onCommand(const int id, int) { switch(id) { - case IDC_WEBSITE: - selectLink(id, m_websiteLinks); - break; - case IDC_DONATE: - selectLink(id, m_donationLinks); - break; - case IDC_INSTALL: - close(InstallResult); - break; - case ACTION_CONTENTS: - packageContents(); - break; - case ACTION_HISTORY: - packageHistory(); - break; case IDOK: case IDCANCEL: close(); break; default: - if(id >> 8 == IDC_WEBSITE) - openLink(m_websiteLinks[id & 0xff]); - else if(id >> 8 == IDC_DONATE) - openLink(m_donationLinks[id & 0xff]); + if(m_links.count(id)) + selectLink(id); + else if(m_links.count(id >> 8)) + openLink(m_links[id >> 8][id & 0xff]); break; } } -bool About::fillContextMenu(Menu &menu, const int) const +void AboutDialog::callUpdateList() { - if(m_packages->currentIndex() < 0) - return false; + const int index = m_menu->currentIndex(); - menu.addAction(AUTO_STR("Package &Contents"), ACTION_CONTENTS); - menu.addAction(AUTO_STR("Package &History"), ACTION_HISTORY); + // do nothing when the selection is cleared, except for the initial execution + if((index < 0 && m_currentIndex >= -1) || index == m_currentIndex) + return; - return true; + InhibitControl lock(m_list); + m_list->clear(); + + updateList(index); + m_currentIndex = index; + + m_list->sort(); } -void About::populate() +void AboutDialog::addLinks(const int ctrl, const vector<const Link *> &links) { - auto_char title[32] = {}; - const auto_string &name = make_autostring(m_index->name()); - auto_snprintf(title, auto_size(title), AUTO_STR("About %s"), name.c_str()); - SetWindowText(handle(), title); + if(links.empty()) + return; - auto_char btnLabel[32] = {}; - auto_snprintf(btnLabel, auto_size(btnLabel), - AUTO_STR("Install/update %s"), name.c_str()); - SetWindowText(getControl(IDC_INSTALL), btnLabel); + m_links[ctrl] = links; + show(getControl(ctrl)); +} + +void AboutDialog::selectLink(const int ctrl) +{ + const auto &links = m_links[ctrl]; + const int count = (int)links.size(); + + m_tabs->setFocus(); + + if(count == 1) { + openLink(links.front()); + return; + } + + Menu menu; + + for(int i = 0; i < count; i++) { + const string &name = boost::replace_all_copy(links[i]->name, "&", "&&"); + menu.addAction(make_autostring(name).c_str(), i | (ctrl << 8)); + } + + RECT rect; + GetWindowRect(getControl(ctrl), &rect); + menu.show(rect.left, rect.bottom - 1, handle()); +} + +void AboutDialog::openLink(const Link *link) +{ + const auto_string &url = make_autostring(link->url); + ShellExecute(nullptr, AUTO_STR("open"), url.c_str(), nullptr, nullptr, SW_SHOW); +} + +AboutRemote::AboutRemote(IndexPtr index) + : AboutDialog(), m_index(index) +{ +} + +const string &AboutRemote::what() const +{ + return m_index->name(); +} + +ListView *AboutRemote::createMenu() +{ + return createControl<ListView>(IDC_MENU, ListView::Columns{ + {AUTO_STR("Category"), 142} + }); +} + +ListView *AboutRemote::createList() +{ + return createControl<ListView>(IDC_LIST, ListView::Columns{ + {AUTO_STR("Name"), 382}, + {AUTO_STR("Version"), 80}, + {AUTO_STR("Author"), 90}, + }); +} - m_websiteLinks = m_index->links(Index::WebsiteLink); - if(m_websiteLinks.empty()) - hide(getControl(IDC_WEBSITE)); +void AboutRemote::onCommand(const int id, const int event) +{ + switch(id) { + case IDC_INSTALL: + close(InstallResult); + break; + case ACTION_ABOUT_PKG: + aboutPackage(); + break; + default: + AboutDialog::onCommand(id, event); + break; + } +} - m_donationLinks = m_index->links(Index::DonationLink); - if(m_donationLinks.empty()) - hide(getControl(IDC_DONATE)); +void AboutRemote::populate() +{ + HWND installBtn = getControl(IDC_INSTALL); + auto_char btnLabel[32] = {}; + auto_snprintf(btnLabel, auto_size(btnLabel), + AUTO_STR("Install/update %s"), make_autostring(what()).c_str()); + SetWindowText(installBtn, btnLabel); + show(installBtn); string aboutText = m_index->aboutText(); @@ -151,59 +201,50 @@ void About::populate() boost::replace_all(aboutText, "[[REAPACK_BUILDTIME]]", ReaPack::BUILDTIME); } - if(!m_about->setRichText(aboutText)) { - // if description is invalid or empty, don't display it - m_tabs->removeTab(0); - m_tabs->setCurrentIndex(0); - } + RichEdit *desc = createControl<RichEdit>(IDC_ABOUT); + if(desc->setRichText(aboutText)) + tabs()->addTab({AUTO_STR("Description"), {desc->handle()}}); - m_cats->addRow({AUTO_STR("<All Packages>")}); + tabs()->addTab({AUTO_STR("Packages"), {menu()->handle(), list()->handle()}}); + tabs()->addTab({AUTO_STR("Installed Files"), {report()}}); - for(const Category *cat : m_index->categories()) - m_cats->addRow({make_autostring(cat->name())}); + list()->onActivate(bind(&AboutRemote::aboutPackage, this)); - m_cats->sortByColumn(0); + addLinks(IDC_WEBSITE, m_index->links(Index::WebsiteLink)); + addLinks(IDC_DONATE, m_index->links(Index::DonationLink)); - updatePackages(); + menu()->addRow({AUTO_STR("<All Packages>")}); + + for(const Category *cat : m_index->categories()) + menu()->addRow({make_autostring(cat->name())}); updateInstalledFiles(); + + list()->onContextMenu(bind(&AboutRemote::fillContextMenu, + this, placeholders::_1, placeholders::_2)); } -void About::updatePackages() +void AboutRemote::updateList(const int index) { - const int index = m_cats->currentIndex(); - - // do nothing when the selection is cleared, except for the initial execution - if(index == -1 && m_currentCat >= -1) - return; - // -1: all packages, >0 selected category const int catIndex = max(-1, index - 1); - if(catIndex == m_currentCat) - return; - else if(catIndex < 0) + if(catIndex < 0) m_packagesData = &m_index->packages(); else m_packagesData = &m_index->category(catIndex)->packages(); - InhibitControl lock(m_packages); - m_packages->clear(); - for(const Package *pkg : *m_packagesData) { const Version *lastVer = pkg->lastVersion(); const auto_string &name = make_autostring(pkg->name()); const auto_string &version = make_autostring(lastVer->name()); const auto_string &author = make_autostring(lastVer->displayAuthor()); - m_packages->addRow({name, version, author}); + list()->addRow({name, version, author}); } - - m_currentCat = catIndex; - m_packages->sort(); } -void About::updateInstalledFiles() +void AboutRemote::updateInstalledFiles() { set<Registry::File> allFiles; @@ -222,12 +263,12 @@ void About::updateInstalledFiles() AUTO_STR("Retry later when all installation task are completed.\r\n") AUTO_STR("\r\nError description: %s"), desc.c_str()); - SetWindowText(m_installedFiles, msg); + SetWindowText(report(), msg); return; } if(allFiles.empty()) { - SetWindowText(m_installedFiles, + SetWindowText(report(), AUTO_STR( "This repository does not own any file on your computer at this time.\r\n") @@ -244,57 +285,105 @@ void About::updateInstalledFiles() stream << "\r\n"; } - SetWindowText(m_installedFiles, make_autostring(stream.str()).c_str()); + SetWindowText(report(), make_autostring(stream.str()).c_str()); } } -void About::selectLink(const int ctrl, const vector<const Link *> &links) +bool AboutRemote::fillContextMenu(Menu &menu, const int) const { - const int count = (int)links.size(); + if(list()->currentIndex() < 0) + return false; - m_tabs->setFocus(); + menu.addAction(AUTO_STR("Package &Overview"), ACTION_ABOUT_PKG); - if(count == 1) { - openLink(links.front()); - return; - } + return true; +} - Menu menu; +void AboutRemote::aboutPackage() +{ + const int index = list()->currentIndex(); - for(int i = 0; i < count; i++) { - const string &name = boost::replace_all_copy(links[i]->name, "&", "&&"); - menu.addAction(make_autostring(name).c_str(), i | (ctrl << 8)); - } + if(index < 0) + return; - RECT rect; - GetWindowRect(getControl(ctrl), &rect); - menu.show(rect.left, rect.bottom - 1, handle()); + const Package *pkg = m_packagesData->at(index); + Dialog::Show<AboutPackage>(instance(), handle(), pkg); } -void About::openLink(const Link *link) +AboutPackage::AboutPackage(const Package *pkg) + : AboutDialog(), m_package(pkg) { - const auto_string &url = make_autostring(link->url); - ShellExecute(nullptr, AUTO_STR("open"), url.c_str(), nullptr, nullptr, SW_SHOW); } -void About::packageHistory() +const string &AboutPackage::what() const { - const int index = m_packages->currentIndex(); + return m_package->name(); +} - if(index < 0) - return; +ListView *AboutPackage::createMenu() +{ + return createControl<ListView>(IDC_MENU, ListView::Columns{ + {AUTO_STR("Version"), 142} + }); +} - const Package *pkg = m_packagesData->at(index); - Dialog::Show<History>(instance(), handle(), pkg); +ListView *AboutPackage::createList() +{ + return createControl<ListView>(IDC_LIST, ListView::Columns{ + {AUTO_STR("File"), 251}, + {AUTO_STR("Source"), 251}, + {AUTO_STR("Main"), 50}, + }); } -void About::packageContents() +void AboutPackage::populate() { - const int index = m_packages->currentIndex(); + // RichEdit *desc = createControl<RichEdit>(IDC_ABOUT); + // if(desc->setRichText(aboutText)) + // tabs()->addTab({AUTO_STR("Documentation"), {desc->handle()}}); + + tabs()->addTab({AUTO_STR("History"), {menu()->handle(), + report()}}); + tabs()->addTab({AUTO_STR("Contents"), {menu()->handle(), + list()->handle()}}); + + RECT rect; + GetWindowRect(list()->handle(), &rect); + ScreenToClient(handle(), (LPPOINT)&rect); + ScreenToClient(handle(), ((LPPOINT)&rect)+1); + + SetWindowPos(report(), nullptr, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOACTIVATE); + + for(const Version *ver : m_package->versions()) + menu()->addRow({make_autostring(ver->name())}); + menu()->setSortCallback(0, [&] (const int a, const int b) { + return m_package->version(a)->compare(*m_package->version(b)); + }); + menu()->sortByColumn(0, ListView::DescendingOrder); + menu()->setSelected(menu()->rowCount() - 1, true); +} + +void AboutPackage::updateList(const int index) +{ if(index < 0) return; - const Package *pkg = m_packagesData->at(index); - Dialog::Show<Contents>(instance(), handle(), pkg); + const Version *ver = m_package->version(index); + OutputStream stream; + stream << *ver; + SetWindowText(report(), make_autostring(stream.str()).c_str()); + + const auto &sources = ver->sources(); + for(auto it = sources.begin(); it != sources.end();) { + const Path &path = it->first; + const Source *src = it->second; + + list()->addRow({make_autostring(path.join()), make_autostring(src->url()), + make_autostring(src->isMain() ? "Yes" : "No")}); + + // skip duplicate files + do { it++; } while(it != sources.end() && path == it->first); + } } diff --git a/src/about.hpp b/src/about.hpp @@ -20,6 +20,7 @@ #include "dialog.hpp" +#include <map> #include <memory> #include <vector> @@ -33,38 +34,80 @@ struct Link; typedef std::shared_ptr<const Index> IndexPtr; -class About : public Dialog { +class AboutDialog : public Dialog { +public: + AboutDialog(); + +protected: + void onInit() override; + void onCommand(int, int) override; + + virtual const std::string &what() const = 0; + virtual ListView *createMenu() = 0; + virtual ListView *createList() = 0; + + virtual void populate() = 0; + virtual void updateList(int) = 0; + + TabBar *tabs() const { return m_tabs; } + ListView *menu() const { return m_menu; } + ListView *list() const { return m_list; } + HWND report() const { return m_report; } + + void addLinks(int control, const std::vector<const Link *> &); + +private: + void selectLink(int control); + void openLink(const Link *); + void callUpdateList(); + + int m_currentIndex; + TabBar *m_tabs; + ListView *m_menu; + ListView *m_list; + HWND m_report; + + std::map<int, std::vector<const Link *> > m_links; +}; + +class AboutRemote : public AboutDialog { public: enum { InstallResult = 100 }; - About(IndexPtr); + AboutRemote(IndexPtr); protected: - void onInit() override; + const std::string &what() const override; + ListView *createMenu() override; + ListView *createList() override; + void onCommand(int, int) override; + void populate() override; + void updateList(int) override; private: bool fillContextMenu(Menu &, int index) const; - void populate(); - void updatePackages(); void updateInstalledFiles(); - void selectLink(int control, const std::vector<const Link *> &); - void openLink(const Link *); - void packageHistory(); - void packageContents(); + void aboutPackage(); IndexPtr m_index; - int m_currentCat; + const std::vector<const Package *> *m_packagesData; +}; - TabBar *m_tabs; - RichEdit *m_about; - ListView *m_cats; - ListView *m_packages; - HWND m_installedFiles; +class AboutPackage : public AboutDialog { +public: + AboutPackage(const Package *); - std::vector<const Link *> m_websiteLinks; - std::vector<const Link *> m_donationLinks; - const std::vector<const Package *> *m_packagesData; +protected: + const std::string &what() const override; + ListView *createMenu() override; + ListView *createList() override; + + void populate() override; + void updateList(int) override; + +private: + const Package *m_package; }; #endif diff --git a/src/browser.cpp b/src/browser.cpp @@ -44,9 +44,8 @@ enum Action { ACTION_UNINSTALL, ACTION_UNINSTALL_ALL, ACTION_PIN, - ACTION_CONTENTS, - ACTION_HISTORY, - ACTION_ABOUT, + ACTION_ABOUT_PKG, + ACTION_ABOUT_REMOTE, ACTION_RESET_ALL, ACTION_SHOWDESCS, ACTION_REFRESH, @@ -79,7 +78,7 @@ void Browser::onInit() SendMessage(m_view, CB_ADDSTRING, 0, (LPARAM)AUTO_STR("Uninstalled")); SendMessage(m_view, CB_SETCURSEL, 0, 0); - m_list = createControl<ListView>(IDC_PACKAGES, ListView::Columns{ + m_list = createControl<ListView>(IDC_LIST, ListView::Columns{ {AUTO_STR("Status"), 23, ListView::NoLabelFlag}, {AUTO_STR("Package"), 380}, {AUTO_STR("Category"), 150}, @@ -90,7 +89,7 @@ void Browser::onInit() {AUTO_STR("Last Update"), 120, ListView::CollapseFlag}, }); - m_list->onActivate([=] { history(m_list->itemUnderMouse()); }); + m_list->onActivate([=] { aboutPackage(m_list->itemUnderMouse()); }); m_list->onSelect([=] { setEnabled(m_list->hasSelection(), m_actionsBtn); }); m_list->onContextMenu(bind(&Browser::fillContextMenu, this, placeholders::_1, placeholders::_2)); @@ -183,14 +182,11 @@ void Browser::onCommand(const int id, const int event) case ACTION_PIN: togglePin(m_currentIndex); break; - case ACTION_CONTENTS: - contents(m_currentIndex); + case ACTION_ABOUT_PKG: + aboutPackage(m_currentIndex); break; - case ACTION_HISTORY: - history(m_currentIndex); - break; - case ACTION_ABOUT: - about(m_currentIndex); + case ACTION_ABOUT_REMOTE: + aboutRemote(m_currentIndex); break; case ACTION_RESET_ALL: selectionDo(bind(&Browser::resetActions, this, placeholders::_1)); @@ -361,16 +357,13 @@ void Browser::fillMenu(Menu &menu) menu.addSeparator(); menu.setEnabled(!entry->test(ObsoleteFlag), - menu.addAction(AUTO_STR("Package &Contents"), ACTION_CONTENTS)); - - menu.setEnabled(!entry->test(ObsoleteFlag), - menu.addAction(AUTO_STR("Package &History"), ACTION_HISTORY)); + menu.addAction(AUTO_STR("Package &Overview..."), ACTION_ABOUT_PKG)); auto_char aboutLabel[32] = {}; const auto_string &name = make_autostring(getValue(RemoteColumn, *entry)); auto_snprintf(aboutLabel, auto_size(aboutLabel), AUTO_STR("&About %s..."), name.c_str()); - menu.addAction(aboutLabel, ACTION_ABOUT); + menu.addAction(aboutLabel, ACTION_ABOUT_REMOTE); } void Browser::updateDisplayLabel() @@ -800,23 +793,13 @@ auto Browser::getEntry(const int listIndex) -> Entry * return &m_entries[m_visibleEntries[listIndex]]; } -void Browser::history(const int index) +void Browser::aboutPackage(const int index) { - const Entry *entry = getEntry(index); - - if(entry && entry->package) - Dialog::Show<History>(instance(), handle(), entry->package); -} - -void Browser::contents(const int index) -{ - const Entry *entry = getEntry(index); - - if(entry) - Dialog::Show<Contents>(instance(), handle(), entry->package); + if(const Entry *entry = getEntry(index)) + m_reapack->about(entry->package, handle()); } -void Browser::about(const int index) +void Browser::aboutRemote(const int index) { if(const Entry *entry = getEntry(index)) m_reapack->about(getRemote(*entry), handle()); diff --git a/src/browser.hpp b/src/browser.hpp @@ -131,9 +131,8 @@ private: void installVersion(int index, size_t verIndex); void uninstall(int index, bool toggle = true); void togglePin(int index); - void history(int index); - void contents(int index); - void about(int index); + void aboutRemote(int index); + void aboutPackage(int index); std::vector<IndexPtr> m_indexes; ReaPack *m_reapack; diff --git a/src/ostream.cpp b/src/ostream.cpp @@ -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/>. + */ + +#include "ostream.hpp" + +#include "version.hpp" + +#include <locale> + +using namespace std; + +OutputStream::OutputStream() +{ + // enable number formatting (ie. "1,234" instead of "1234") + m_stream.imbue(locale("")); +} + +void OutputStream::indented(const string &text) +{ + istringstream stream(text); + string line; + + while(getline(stream, line, '\n')) + m_stream << "\x20\x20" << line.substr(line.find_first_not_of('\x20')) << "\r\n"; +} + +OutputStream &OutputStream::operator<<(const Version &ver) +{ + m_stream << 'v' << ver.name(); + + if(!ver.author().empty()) + m_stream << " by " << ver.author(); + + const string &date = ver.time().toString(); + if(!date.empty()) + m_stream << " – " << date; + + m_stream << "\r\n"; + + const string &changelog = ver.changelog(); + indented(changelog.empty() ? "No changelog" : changelog); + + return *this; +} + diff --git a/src/ostream.hpp b/src/ostream.hpp @@ -0,0 +1,42 @@ +/* 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_OSTREAM_HPP +#define REAPACK_OSTREAM_HPP + +#include <sstream> + +class Version; + +class OutputStream { +public: + OutputStream(); + + std::ostringstream::pos_type tellp() { return m_stream.tellp(); } + std::string str() const { return m_stream.str(); } + + void indented(const std::string &); + + template<typename T> + OutputStream &operator<<(T t) { m_stream << t; return *this; } + OutputStream &operator<<(const Version &); + +private: + std::ostringstream m_stream; +}; + +#endif diff --git a/src/reapack.cpp b/src/reapack.cpp @@ -238,9 +238,9 @@ void ReaPack::about(const Remote &remote, HWND parent) if(!index) return; - const auto ret = Dialog::Show<About>(m_instance, parent, index); + const auto ret = Dialog::Show<AboutRemote>(m_instance, parent, index); - if(ret == About::InstallResult) { + if(ret == AboutRemote::InstallResult) { Transaction *tx = setupTransaction(); if(!tx) @@ -254,6 +254,14 @@ void ReaPack::about(const Remote &remote, HWND parent) }, parent); } +void ReaPack::about(const Package *pkg, HWND parent) +{ + const auto ret = Dialog::Show<AboutPackage>(m_instance, parent, pkg); + + switch(ret) { + } +} + void ReaPack::browsePackages() { if(m_browser) { diff --git a/src/reapack.hpp b/src/reapack.hpp @@ -71,8 +71,8 @@ public: void importRemote(); void manageRemotes(); void aboutSelf(); - void about(const std::string &, HWND parent); void about(const Remote &, HWND parent); + void about(const Package *, HWND parent); void browsePackages(); void refreshManager(); void refreshBrowser(); diff --git a/src/report.cpp b/src/report.cpp @@ -24,21 +24,15 @@ #include "transaction.hpp" #include <boost/range/adaptor/reversed.hpp> -#include <locale> using namespace std; -static const string SEP(10, '='); -const char * const ReportDialog::NL = "\r\n"; - -ReportDialog::ReportDialog() - : Dialog(IDD_REPORT_DIALOG) +Report::Report(const Receipt &receipt) + : Dialog(IDD_REPORT_DIALOG), m_receipt(receipt) { - // enable number formatting (ie. "1,234" instead of "1234") - m_stream.imbue(locale("")); } -void ReportDialog::onInit() +void Report::onInit() { Dialog::onInit(); @@ -48,46 +42,13 @@ void ReportDialog::onInit() SetDlgItemText(handle(), IDC_REPORT, str.c_str()); } -void ReportDialog::printHeader(const char *title) +void Report::printHeader(const char *title) { if(m_stream.tellp()) - m_stream << NL; - - m_stream << SEP << ' ' << title << ": " << SEP << NL << NL; -} - -void ReportDialog::printVersion(const Version *ver) -{ - stream() << 'v' << ver->name(); + m_stream << "\r\n"; - if(!ver->author().empty()) - stream() << " by " << ver->author(); - - const string &date = ver->time().toString(); - if(!date.empty()) - stream() << " – " << date; - - stream() << NL; -} - -void ReportDialog::printChangelog(const Version *ver) -{ - const string &changelog = ver->changelog(); - printIndented(changelog.empty() ? "No changelog" : changelog); -} - -void ReportDialog::printIndented(const string &text) -{ - istringstream stream(text); - string line; - - while(getline(stream, line, '\n')) - m_stream << "\x20\x20" << line.substr(line.find_first_not_of('\x20')) << NL; -} - -Report::Report(const Receipt &receipt) - : ReportDialog(), m_receipt(receipt) -{ + const string sep(10, '='); + m_stream << sep << ' ' << title << ": " << sep << "\r\n\r\n"; } void Report::fillReport() @@ -97,20 +58,20 @@ void Report::fillReport() const size_t removals = m_receipt.removals().size(); const size_t errors = m_receipt.errors().size(); - stream() + m_stream << installs << " installed packages, " << updates << " updates, " << removals << " removed files and " << errors << " errors" - << NL + << "\r\n" ; if(m_receipt.isRestartNeeded()) { - stream() - << NL - << "Notice: One or more native REAPER extensions were installed." << NL + m_stream + << "\r\n" + << "Notice: One or more native REAPER extensions were installed.\r\n" << "The newly installed files won't be loaded until REAPER is restarted." - << NL; + << "\r\n"; } if(errors) @@ -131,31 +92,30 @@ void Report::printInstalls() printHeader("Installed packages"); for(const InstallTicket &ticket : m_receipt.installs()) - stream() << ticket.version->fullName() << NL; + m_stream << ticket.version->fullName() << "\r\n"; } void Report::printUpdates() { printHeader("Updates"); - const auto start = stream().tellp(); + const auto start = m_stream.tellp(); for(const InstallTicket &ticket : m_receipt.updates()) { const Package *pkg = ticket.version->package(); const Registry::Entry &regEntry = ticket.regEntry; const VersionSet &versions = pkg->versions(); - if(stream().tellp() != start) - stream() << NL; + if(m_stream.tellp() != start) + m_stream << "\r\n"; - stream() << pkg->fullName() << ':' << NL; + m_stream << pkg->fullName() << ":\r\n"; for(const Version *ver : versions | boost::adaptors::reversed) { if(*ver <= regEntry.version) break; - printVersion(ver); - printChangelog(ver); + m_stream << *ver; } } } @@ -164,14 +124,14 @@ void Report::printErrors() { printHeader("Errors"); - const auto start = stream().tellp(); + const auto start = m_stream.tellp(); for(const Receipt::Error &err : m_receipt.errors()) { - if(stream().tellp() != start) - stream() << NL; + if(m_stream.tellp() != start) + m_stream << "\r\n"; - stream() << err.title << ':' << NL; - printIndented(err.message); + m_stream << err.title << ":\r\n"; + m_stream.indented(err.message); } } @@ -180,57 +140,5 @@ void Report::printRemovals() printHeader("Removed files"); for(const Path &path : m_receipt.removals()) - stream() << path.join() << NL; -} - -History::History(const Package *pkg) - : ReportDialog(), m_package(pkg) -{ -} - -void History::fillReport() -{ - SetWindowText(handle(), AUTO_STR("Package History")); - SetWindowText(getControl(IDC_LABEL), - make_autostring(m_package->name()).c_str()); - - for(const Version *ver : m_package->versions() | boost::adaptors::reversed) { - if(stream().tellp()) - stream() << NL; - - printVersion(ver); - printChangelog(ver); - } -} - -Contents::Contents(const Package *pkg) - : ReportDialog(), m_package(pkg) -{ -} - -void Contents::fillReport() -{ - SetWindowText(handle(), AUTO_STR("Package Contents")); - SetWindowText(getControl(IDC_LABEL), - make_autostring(m_package->name()).c_str()); - - for(const Version *ver : m_package->versions() | boost::adaptors::reversed) { - if(stream().tellp()) - stream() << NL; - - printVersion(ver); - - const auto &sources = ver->sources(); - for(auto it = sources.begin(); it != sources.end();) { - const Path &path = it->first; - const string &file = path.join(); - const Source *src = it->second; - - printIndented(src->isMain() ? file + '*' : file); - printIndented("Source: " + src->url()); - - // skip duplicate files - do { it++; } while(it != sources.end() && path == it->first); - } - } + m_stream << path.join() << "\r\n"; } diff --git a/src/report.hpp b/src/report.hpp @@ -20,71 +20,32 @@ #include "dialog.hpp" +#include "ostream.hpp" #include "receipt.hpp" #include "registry.hpp" -#include <sstream> - class Package; class Version; -class ReportDialog : public Dialog { +class Report : public Dialog { public: - ReportDialog(); + Report(const Receipt &); protected: void onInit() override; - virtual void fillReport() = 0; - - static const char * const NL; - std::ostringstream &stream() { return m_stream; } + virtual void fillReport(); void printHeader(const char *); - void printVersion(const Version *); - void printChangelog(const Version *); - void printIndented(const std::string &); - -private: - std::ostringstream m_stream; -}; - -class Report : public ReportDialog { -public: - Report(const Receipt &); - -protected: - void fillReport() override; -private: void printInstalls(); void printUpdates(); void printErrors(); void printRemovals(); - Receipt m_receipt; -}; - -class History : public ReportDialog { -public: - History(const Package *); - -protected: - void fillReport() override; - private: - const Package *m_package; -}; - -class Contents : public ReportDialog { -public: - Contents(const Package *); - -protected: - void fillReport() override; - -private: - const Package *m_package; + Receipt m_receipt; + OutputStream m_stream; }; #endif diff --git a/src/resource.hpp b/src/resource.hpp @@ -54,19 +54,18 @@ #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_FILTER 223 -#define IDC_CLEAR 224 -#define IDC_DISPLAY 225 -#define IDC_SELECT 226 -#define IDC_UNSELECT 227 -#define IDC_OPTIONS 228 -#define IDC_ACTIONS 229 -#define IDC_BROWSE 230 -#define IDC_PROXY 231 -#define IDC_VERIFYPEER 232 +#define IDC_MENU 219 +#define IDC_GROUPBOX 220 +#define IDC_URL 221 +#define IDC_FILTER 222 +#define IDC_CLEAR 223 +#define IDC_DISPLAY 224 +#define IDC_SELECT 225 +#define IDC_UNSELECT 226 +#define IDC_OPTIONS 227 +#define IDC_ACTIONS 228 +#define IDC_BROWSE 228 +#define IDC_PROXY 229 +#define IDC_VERIFYPEER 230 #endif diff --git a/src/resource.rc b/src/resource.rc @@ -41,28 +41,28 @@ BEGIN PUSHBUTTON "&Apply", IDAPPLY, 284, 161, 40, 14 END -IDD_ABOUT_DIALOG DIALOGEX 0, 0, 460, 270 +IDD_ABOUT_DIALOG DIALOGEX 0, 0, 460, 267 STYLE DIALOG_STYLE FONT DIALOG_FONT BEGIN CONTROL "", IDC_TABS, WC_TABCONTROL, 0, 0, 2, 460, 243 #ifdef _WIN32 CONTROL "", IDC_ABOUT, MSFTEDIT_CLASS, WS_VSCROLL | ES_MULTILINE | - ES_READONLY | NOT WS_TABSTOP, 10, 20, 440, 220 + ES_READONLY | NOT WS_TABSTOP | NOT WS_VISIBLE, 10, 20, 440, 220 #else EDITTEXT IDC_ABOUT, 10, 20, 440, 220, - WS_VSCROLL | ES_MULTILINE | ES_READONLY | NOT WS_TABSTOP + WS_VSCROLL | ES_MULTILINE | ES_READONLY | NOT WS_TABSTOP | NOT WS_VISIBLE #endif - CONTROL "", IDC_CATEGORIES, WC_LISTVIEW, LVS_REPORT | LVS_SINGLESEL | - LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP, 10, 20, 96, 220 - CONTROL "", IDC_PACKAGES, WC_LISTVIEW, LVS_REPORT | LVS_SINGLESEL | - LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP, 110, 20, 340, 220 - EDITTEXT IDC_LIST, 10, 20, 440, 220, - WS_VSCROLL | ES_MULTILINE | ES_READONLY | NOT WS_TABSTOP - PUSHBUTTON "&Website", IDC_WEBSITE, 5, 250, 45, 14 - PUSHBUTTON "&Donate...", IDC_DONATE, 53, 250, 45, 14 - PUSHBUTTON "", IDC_INSTALL, 286, 250, 120, 14 - DEFPUSHBUTTON "&Close", IDOK, 409, 250, 45, 14 + CONTROL "", IDC_MENU, WC_LISTVIEW, LVS_REPORT | LVS_SINGLESEL | + LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP | NOT WS_VISIBLE, 10, 20, 96, 220 + CONTROL "", IDC_LIST, WC_LISTVIEW, LVS_REPORT | LVS_SINGLESEL | + LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP | NOT WS_VISIBLE, 110, 20, 340, 220 + EDITTEXT IDC_REPORT, 10, 20, 440, 220, + WS_VSCROLL | ES_MULTILINE | ES_READONLY | NOT WS_TABSTOP | NOT WS_VISIBLE + PUSHBUTTON "&Website", IDC_WEBSITE, 5, 248, 45, 14, NOT WS_VISIBLE + PUSHBUTTON "&Donate...", IDC_DONATE, 53, 248, 45, 14, NOT WS_VISIBLE + PUSHBUTTON "", IDC_INSTALL, 286, 248, 120, 14, NOT WS_VISIBLE + DEFPUSHBUTTON "&Close", IDOK, 409, 248, 45, 14 END IDD_IMPORT_DIALOG DIALOGEX 0, 0, 290, 69 @@ -90,7 +90,7 @@ BEGIN LTEXT "Display:", IDC_LABEL2, 286, 7, 30, 10 COMBOBOX IDC_TABS, 314, 5, 65, 54, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "", IDC_DISPLAY, 385, 4, 110, 14 - CONTROL "", IDC_PACKAGES, WC_LISTVIEW, LVS_REPORT | LVS_SHOWSELALWAYS | + CONTROL "", IDC_LIST, WC_LISTVIEW, LVS_REPORT | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP, 5, 22, 490, 205 PUSHBUTTON "&Select all", IDC_SELECT, 5, 231, 50, 14 PUSHBUTTON "&Unselect all", IDC_UNSELECT, 58, 231, 50, 14 diff --git a/src/tabbar.cpp b/src/tabbar.cpp @@ -32,9 +32,6 @@ int TabBar::addTab(const Tab &tab) { int index = m_size++; - for(HWND control : tab.page) - ShowWindow(control, SW_HIDE); - m_pages.push_back(tab.page); TCITEM item{}; @@ -62,13 +59,11 @@ void TabBar::setCurrentIndex(const int index) void TabBar::removeTab(const int index) { - if(!TabCtrl_DeleteItem(handle(), index)) - return; - - for(HWND control : m_pages[index]) - ShowWindow(control, SW_HIDE); + if(currentIndex() == index) + setCurrentIndex(index == 0 ? index + 1 : index - 1); - m_pages.erase(m_pages.begin() + index); + if(TabCtrl_DeleteItem(handle(), index)) + m_pages.erase(m_pages.begin() + index); } void TabBar::setFocus()