commit f32c826a00dd174e6ac31f0e132c2f0341d2d2f7
parent 1e7a26d986ef051b363078f35945708b195f76be
Author: cfillion <cfillion@users.noreply.github.com>
Date: Tue, 21 Jun 2016 03:34:36 -0400
browser: add Last Update column (collapsed by default)
Diffstat:
13 files changed, 279 insertions(+), 58 deletions(-)
diff --git a/src/browser.cpp b/src/browser.cpp
@@ -87,6 +87,7 @@ void Browser::onInit()
{AUTO_STR("Author"), 95},
{AUTO_STR("Type"), 70},
{AUTO_STR("Repository"), 120, ListView::CollapseFlag},
+ {AUTO_STR("Last Update"), 120, ListView::CollapseFlag},
});
m_list->onActivate([=] { history(m_list->itemUnderMouse()); });
@@ -95,6 +96,17 @@ void Browser::onInit()
this, placeholders::_1, placeholders::_2));
m_list->sortByColumn(1);
+ m_list->setSortCallback(7 /* last update */, [&] (const int ai, const int bi) {
+ const Entry &a = m_entries[ai];
+ const Entry &b = m_entries[bi];
+
+ if(!a.latest)
+ return -1;
+ else if(!b.latest)
+ return 1;
+
+ return a.latest->time().compare(b.latest->time());
+ });
const auto config = m_reapack->config()->browser();
m_list->restore(config->list, 1);
@@ -640,11 +652,12 @@ ListView::Row Browser::makeRow(const Entry &entry) const
const string &author = getValue(AuthorColumn, entry);
const string &type = getValue(TypeColumn, entry);
const string &remote = getValue(RemoteColumn, entry);
+ const string &date = getValue(TimeColumn, entry);
return {
make_autostring(state), make_autostring(name), make_autostring(category),
make_autostring(version), make_autostring(author), make_autostring(type),
- make_autostring(remote),
+ make_autostring(remote), make_autostring(date),
};
}
@@ -708,6 +721,8 @@ string Browser::getValue(const Column col, const Entry &entry) const
return pkg ? pkg->displayType() : Package::displayType(regEntry.type);
case RemoteColumn:
return pkg ? pkg->category()->index()->name() : regEntry.remote;
+ case TimeColumn:
+ return ver ? ver->time().toString() : string();
}
return {}; // for MSVC
diff --git a/src/browser.hpp b/src/browser.hpp
@@ -86,6 +86,7 @@ private:
AuthorColumn,
TypeColumn,
RemoteColumn,
+ TimeColumn,
};
enum View {
diff --git a/src/listview.cpp b/src/listview.cpp
@@ -141,19 +141,22 @@ void ListView::sort()
static const auto compare = [](LPARAM aRow, LPARAM bRow, LPARAM param)
{
ListView *view = reinterpret_cast<ListView *>(param);
-
const int column = view->m_sort->column;
+
+ int ret;
+
const auto it = view->m_sortFuncs.find(column);
if(it != view->m_sortFuncs.end())
- return it->second((int)aRow, (int)bRow);
+ ret = it->second((int)aRow, (int)bRow);
+ else {
+ auto_string a = view->m_rows[aRow][column];
+ boost::algorithm::to_lower(a);
- auto_string a = view->m_rows[aRow][column];
- boost::algorithm::to_lower(a);
+ auto_string b = view->m_rows[bRow][column];
+ boost::algorithm::to_lower(b);
- auto_string b = view->m_rows[bRow][column];
- boost::algorithm::to_lower(b);
-
- const int ret = a.compare(b);
+ ret = a.compare(b);
+ }
switch(view->m_sort->order) {
case AscendingOrder:
diff --git a/src/report.cpp b/src/report.cpp
@@ -61,7 +61,7 @@ void ReportDialog::printVersion(const Version *ver)
if(!ver->author().empty())
stream() << " by " << ver->author();
- const string &date = ver->displayTime();
+ const string &date = ver->time().toString();
if(!date.empty())
stream() << " – " << date;
diff --git a/src/time.cpp b/src/time.cpp
@@ -0,0 +1,91 @@
+/* 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 "time.hpp"
+
+#include <iomanip>
+#include <sstream>
+
+using namespace std;
+
+Time::Time(const string &iso8601) : m_tm()
+{
+ tm time = {};
+
+ istringstream stream(iso8601);
+ stream >> std::get_time(&time, "%Y-%m-%dT%H:%M:%S");
+
+ if(stream.good())
+ swap(m_tm, time);
+}
+
+Time::Time(int year, int month, int day, int hour, int minute, int second)
+ : m_tm()
+{
+ m_tm.tm_year = year - 1900;
+ m_tm.tm_mon = month - 1;
+ m_tm.tm_mday = day;
+
+ m_tm.tm_hour = hour;
+ m_tm.tm_min = minute;
+ m_tm.tm_sec = second;
+}
+
+string Time::toString() const
+{
+ if(!isValid())
+ return {};
+
+ char buf[32] = {};
+ strftime(buf, sizeof(buf), "%B %d %Y", &m_tm);
+ return buf;
+}
+
+int Time::compare(const Time &o) const
+{
+ if(year() > o.year())
+ return 1;
+ else if(year() < o.year())
+ return -1;
+
+ if(month() > o.month())
+ return 1;
+ else if(month() < o.month())
+ return -1;
+
+ if(day() > o.day())
+ return 1;
+ else if(day() < o.day())
+ return -1;
+
+ if(hour() > o.hour())
+ return 1;
+ else if(hour() < o.hour())
+ return -1;
+
+ if(minute() > o.minute())
+ return 1;
+ else if(minute() < o.minute())
+ return -1;
+
+ if(second() > o.second())
+ return 1;
+ else if(second() < o.second())
+ return -1;
+
+ return 0;
+}
diff --git a/src/time.hpp b/src/time.hpp
@@ -0,0 +1,54 @@
+/* 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_TIME_HPP
+#define REAPACK_TIME_HPP
+
+#include <ctime>
+#include <string>
+
+class Time {
+public:
+ Time(const std::string &);
+ Time(int year, int month, int day, int hour = 0, int minute = 0, int second = 0);
+ Time(const std::tm &tm = {}) : m_tm(tm) {}
+
+ bool isValid() const { return m_tm.tm_year > 0; }
+ operator bool() const { return isValid(); }
+
+ int year() const { return m_tm.tm_year + 1900; }
+ int month() const { return m_tm.tm_mon + 1; }
+ int day() const { return m_tm.tm_mday; }
+ int hour() const { return m_tm.tm_hour; }
+ int minute() const { return m_tm.tm_min; }
+ int second() const { return m_tm.tm_sec; }
+
+ std::string toString() const;
+
+ int compare(const Time &) const;
+ bool operator<(const Time &o) const { return compare(o) < 0; }
+ bool operator<=(const Time &o) const { return compare(o) <= 0; }
+ bool operator>(const Time &o) const { return compare(o) > 0; }
+ bool operator>=(const Time &o) const { return compare(o) >= 0; }
+ bool operator==(const Time &o) const { return compare(o) == 0; }
+ bool operator!=(const Time &o) const { return compare(o) != 0; }
+
+private:
+ std::tm m_tm;
+};
+
+#endif
diff --git a/src/version.cpp b/src/version.cpp
@@ -23,7 +23,6 @@
#include <boost/lexical_cast.hpp>
#include <cctype>
-#include <iomanip>
#include <regex>
using namespace std;
@@ -138,23 +137,10 @@ void Version::setChangelog(const string &changelog)
void Version::setTime(const char *iso8601)
{
- tm time = {};
+ const Time time(iso8601);
- istringstream stream(iso8601);
- stream >> std::get_time(&time, "%Y-%m-%d");
-
- if(stream.good())
- swap(m_time, time);
-}
-
-string Version::displayTime() const
-{
- if(m_time.tm_year == 0)
- return {};
-
- char buf[32] = {};
- strftime(buf, sizeof(buf), "%B %d %Y", &m_time);
- return buf;
+ if(time)
+ m_time = time;
}
const Source *Version::source(const size_t index) const
diff --git a/src/version.hpp b/src/version.hpp
@@ -20,12 +20,13 @@
#include <boost/variant.hpp>
#include <cstdint>
-#include <ctime>
#include <map>
#include <set>
#include <string>
#include <vector>
+#include "time.hpp"
+
class Package;
class Source;
class Path;
@@ -55,8 +56,7 @@ public:
std::string displayAuthor() const;
void setTime(const char *iso8601);
- const std::tm &time() const { return m_time; }
- std::string displayTime() const;
+ const Time &time() const { return m_time; }
void setChangelog(const std::string &);
const std::string &changelog() const { return m_changelog; }
@@ -88,7 +88,7 @@ private:
std::string m_author;
std::string m_changelog;
- std::tm m_time;
+ Time m_time;
const Package *m_package;
SourceList m_mainSources;
diff --git a/test/helper/io.cpp b/test/helper/io.cpp
@@ -23,6 +23,12 @@ ostream &operator<<(ostream &os, const set<Path> &list)
return os;
}
+ostream &operator<<(ostream &os, const Time &time)
+{
+ os << time.toString();
+ return os;
+}
+
ostream &operator<<(ostream &os, const Version &ver)
{
os << ver.name();
diff --git a/test/helper/io.hpp b/test/helper/io.hpp
@@ -5,10 +5,12 @@
#include <set>
class Path;
+class Time;
class Version;
std::ostream &operator<<(std::ostream &, const Path &);
std::ostream &operator<<(std::ostream &, const std::set<Path> &);
+std::ostream &operator<<(std::ostream &, const Time &);
std::ostream &operator<<(std::ostream &, const Version &);
#endif
diff --git a/test/index_v1.cpp b/test/index_v1.cpp
@@ -84,10 +84,10 @@ TEST_CASE("read version time", M) {
IndexPtr ri = Index::load("time");
CHECK(ri->packages().size() == 1);
- const tm &time = ri->category(0)->package(0)->version(0)->time();
- REQUIRE(time.tm_year == 2016 - 1900);
- REQUIRE(time.tm_mon == 2 - 1);
- REQUIRE(time.tm_mday == 12);
+ const Time &time = ri->category(0)->package(0)->version(0)->time();
+ REQUIRE(time.year() == 2016);
+ REQUIRE(time.month() == 2);
+ REQUIRE(time.day() == 12);
}
TEST_CASE("invalid version tag", M) {
diff --git a/test/time.cpp b/test/time.cpp
@@ -0,0 +1,80 @@
+#include <catch.hpp>
+
+#include "helper/io.hpp"
+
+#include <time.hpp>
+
+using namespace std;
+
+static const char *M = "[time]";
+
+TEST_CASE("valid time", M) {
+ const Time time("2016-02-12T01:16:40Z");
+ REQUIRE(time.year() == 2016);
+ REQUIRE(time.month() == 2);
+ REQUIRE(time.day() == 12);
+ REQUIRE(time.hour() == 1);
+ REQUIRE(time.minute() == 16);
+ REQUIRE(time.second() == 40);
+ REQUIRE(time == Time(2016, 2, 12, 1, 16, 40));
+ REQUIRE(time.isValid());
+ REQUIRE(time);
+}
+
+TEST_CASE("garbage time string", M) {
+ const Time time("hello world");
+ REQUIRE_FALSE(time.isValid());
+ REQUIRE_FALSE(time);
+ REQUIRE(time == Time());
+ REQUIRE(time.toString() == string());
+}
+
+TEST_CASE("out of range time string", M) {
+ const Time time("2016-99-99T99:99:99Z");
+ REQUIRE_FALSE(time.isValid());
+ REQUIRE_FALSE(time);
+ REQUIRE(time == Time());
+ REQUIRE(time.toString() == string());
+}
+
+TEST_CASE("compare times", M) {
+ SECTION("equality") {
+ REQUIRE(Time(2016,1,2,3,4,5).compare(Time(2016,1,2,3,4,5)) == 0);
+
+ REQUIRE(Time(2016,1,2,3,4,5) == Time(2016,1,2,3,4,5));
+ REQUIRE_FALSE(Time(2016,1,2,3,4,5) == Time(2016,1,2,3,4));
+ }
+
+ SECTION("inequality") {
+ REQUIRE_FALSE(Time(2016,1,2,3,4,5) != Time(2016,1,2,3,4,5));
+ REQUIRE(Time(2016,1,2,3,4,5) != Time(2017,5,4,3,2,1));
+ }
+
+ SECTION("less than") {
+ REQUIRE(Time(2015, 2, 3).compare(Time(2016, 2, 3)) == -1);
+
+ REQUIRE(Time(2015, 2, 3) < Time(2016, 2, 3));
+ REQUIRE_FALSE(Time(2016, 2, 3) < Time(2016, 2, 3));
+ REQUIRE_FALSE(Time(2016, 2, 1) < Time(2015, 2, 2));
+ }
+
+ SECTION("less than or equal") {
+ REQUIRE(Time(2015, 2, 3) <= Time(2016, 2, 3));
+ REQUIRE(Time(2016, 2, 3) <= Time(2016, 2, 3));
+ REQUIRE_FALSE(Time(2016, 2, 1) <= Time(2015, 2, 2));
+ }
+
+ SECTION("greater than") {
+ REQUIRE(Time(2016, 2, 3).compare(Time(2015, 2, 3)) == 1);
+
+ REQUIRE_FALSE(Time(2015, 2, 30) > Time(2016, 2, 3));
+ REQUIRE_FALSE(Time(2016, 2, 3) > Time(2016, 2, 3));
+ REQUIRE(Time(2016, 2, 3) > Time(2015, 2, 3));
+ }
+
+ SECTION("greater than or equal") {
+ REQUIRE_FALSE(Time(2015, 2, 30) >= Time(2016, 2, 3));
+ REQUIRE(Time(2016, 2, 3) >= Time(2016, 2, 3));
+ REQUIRE(Time(2016, 2, 3) >= Time(2015, 2, 3));
+ }
+}
diff --git a/test/version.cpp b/test/version.cpp
@@ -23,7 +23,7 @@ TEST_CASE("construct null version", M) {
REQUIRE(ver.size() == 0);
REQUIRE(ver.isStable());
- REQUIRE(ver.displayTime().empty());
+ REQUIRE_FALSE(ver.time().isValid());
REQUIRE(ver.package() == nullptr);
REQUIRE(ver.mainSources().empty());
}
@@ -317,29 +317,12 @@ TEST_CASE("version author", M) {
TEST_CASE("version date", M) {
Version ver("1.0");
- CHECK(ver.time().tm_year == 0);
- CHECK(ver.time().tm_mon == 0);
- CHECK(ver.time().tm_mday == 0);
- CHECK(ver.displayTime() == "");
- SECTION("valid") {
- ver.setTime("2016-02-12T01:16:40Z");
- REQUIRE(ver.time().tm_year == 2016 - 1900);
- REQUIRE(ver.time().tm_mon == 2 - 1);
- REQUIRE(ver.time().tm_mday == 12);
- REQUIRE(ver.displayTime() != "");
- }
+ ver.setTime("2016-02-12T01:16:40Z");
+ REQUIRE(ver.time().year() == 2016);
- SECTION("garbage") {
- ver.setTime("hello world");
- REQUIRE(ver.time().tm_year == 0);
- REQUIRE(ver.displayTime() == "");
- }
-
- SECTION("out of range") {
- ver.setTime("2016-99-99T99:99:99Z");
- REQUIRE(ver.displayTime() == "");
- }
+ ver.setTime("hello world");
+ REQUIRE(ver.time().year() == 2016);
}
TEST_CASE("copy version constructor", M) {
@@ -356,7 +339,7 @@ TEST_CASE("copy version constructor", M) {
REQUIRE(copy1.isStable() == original.isStable());
REQUIRE(copy1.author() == original.author());
REQUIRE(copy1.changelog() == original.changelog());
- REQUIRE(copy1.displayTime() == original.displayTime());
+ REQUIRE(copy1.time() == original.time());
REQUIRE(copy1.package() == nullptr);
REQUIRE(copy1.mainSources().empty());
REQUIRE(copy1.sources().empty());