debug next status
This commit is contained in:
parent
0b2e1df83d
commit
a3d142034f
27 changed files with 1130 additions and 46 deletions
|
@ -37,6 +37,7 @@
|
|||
<file>./graphics/Faenza/actions/64/mail-mark-read.png</file>
|
||||
<file>./graphics/Faenza/actions/64/mail-mark-unread.png</file>
|
||||
<file>./graphics/Faenza/actions/64/mail-message-new.png</file>
|
||||
<file>./graphics/Faenza/actions/64/mail-reply-sender.png</file>
|
||||
<file>./graphics/Faenza/actions/64/mail-send.png</file>
|
||||
<file>./graphics/Faenza/actions/64/mail-sent.png</file>
|
||||
<file>./graphics/Faenza/actions/64/media-playback-start.png</file>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<qresource prefix="/">
|
||||
<file>text/CHANGELOG</file>
|
||||
<file>text/COPYING_BSD</file>
|
||||
<file>text/COPYING_MIT</file>
|
||||
<file>text/COPYING_GNU_GPL</file>
|
||||
<file>text/COPYING_GNU_GPL_HTML</file>
|
||||
|
||||
|
|
19
resources/text/COPYING_MIT
Executable file
19
resources/text/COPYING_MIT
Executable file
|
@ -0,0 +1,19 @@
|
|||
Copyright (C) 2019 by Anton Bukov (k06aaa@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
882
src/librssguard/3rd-party/boolinq/boolinq.h
vendored
Executable file
882
src/librssguard/3rd-party/boolinq/boolinq.h
vendored
Executable file
|
@ -0,0 +1,882 @@
|
|||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
//
|
||||
|
||||
namespace boolinq {
|
||||
|
||||
struct LinqEndException {};
|
||||
|
||||
enum BytesDirection {
|
||||
BytesFirstToLast,
|
||||
BytesLastToFirst,
|
||||
};
|
||||
|
||||
enum BitsDirection {
|
||||
BitsHighToLow,
|
||||
BitsLowToHigh,
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
class Linq {
|
||||
std::function<T(S &)> nextFunc;
|
||||
S storage;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
Linq() : nextFunc(), storage()
|
||||
{
|
||||
}
|
||||
|
||||
Linq(S storage, std::function<T(S &)> nextFunc) : nextFunc(nextFunc), storage(storage)
|
||||
{
|
||||
}
|
||||
|
||||
T next()
|
||||
{
|
||||
return nextFunc(storage);
|
||||
}
|
||||
|
||||
void for_each_i(std::function<void(T, int)> apply) const
|
||||
{
|
||||
Linq<S, T> linq = *this;
|
||||
try {
|
||||
for (int i = 0; ; i++) {
|
||||
apply(linq.next(), i);
|
||||
}
|
||||
}
|
||||
catch (LinqEndException &) {}
|
||||
}
|
||||
|
||||
void for_each(std::function<void(T)> apply) const
|
||||
{
|
||||
return for_each_i([apply](T value, int index) { return apply(value); });
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int>, T> where_i(std::function<bool(T, int)> filter) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, int>, T>(
|
||||
std::make_tuple(*this, 0),
|
||||
[filter](std::tuple<Linq<S, T>, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
int &index = std::get<1>(tuple);
|
||||
|
||||
while (true) {
|
||||
T ret = linq.next();
|
||||
if (filter(ret, index++)) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int>, T> where(std::function<bool(T)> filter) const
|
||||
{
|
||||
return where_i([filter](T value, int index) { return filter(value); });
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int>, T> take(int count) const
|
||||
{
|
||||
return where_i([count](T /*value*/, int i) {
|
||||
if (i == count) {
|
||||
throw LinqEndException();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int>, T> takeWhile_i(std::function<bool(T, int)> predicate) const
|
||||
{
|
||||
return where_i([predicate](T value, int i) {
|
||||
if (!predicate(value, i)) {
|
||||
throw LinqEndException();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int>, T> takeWhile(std::function<bool(T)> predicate) const
|
||||
{
|
||||
return takeWhile_i([predicate](T value, int /*i*/) { return predicate(value); });
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int>, T> skip(int count) const
|
||||
{
|
||||
return where_i([count](T value, int i) { return i >= count; });
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int, bool>, T> skipWhile_i(std::function<bool(T, int)> predicate) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, int, bool>, T>(
|
||||
std::make_tuple(*this, 0, false),
|
||||
[predicate](std::tuple<Linq<S, T>, int, bool> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
int &index = std::get<1>(tuple);
|
||||
bool &flag = std::get<2>(tuple);
|
||||
|
||||
if (flag) {
|
||||
return linq.next();
|
||||
}
|
||||
while (true) {
|
||||
T ret = linq.next();
|
||||
if (!predicate(ret, index++)) {
|
||||
flag = true;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, int, bool>, T> skipWhile(std::function<bool(T)> predicate) const
|
||||
{
|
||||
return skipWhile_i([predicate](T value, int /*i*/) { return predicate(value); });
|
||||
}
|
||||
|
||||
template<typename ... Types>
|
||||
Linq<std::tuple<Linq<S, T>, std::vector<T>, int>, T> append(Types ... newValues) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, std::vector<T>, int>, T>(
|
||||
std::make_tuple(*this, std::vector<T>{ newValues... }, -1),
|
||||
[](std::tuple<Linq<S, T>, std::vector<T>, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
std::vector<T> &values = std::get<1>(tuple);
|
||||
int &index = std::get<2>(tuple);
|
||||
|
||||
if (index == -1) {
|
||||
try {
|
||||
return linq.next();
|
||||
}
|
||||
catch (LinqEndException &) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (index < values.size()) {
|
||||
return values[index++];
|
||||
}
|
||||
|
||||
throw LinqEndException();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename ... Types>
|
||||
Linq<std::tuple<Linq<S, T>, std::vector<T>, int>, T> prepend(Types ... newValues) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, std::vector<T>, int>, T>(
|
||||
std::make_tuple(*this, std::vector<T>{ newValues... }, 0),
|
||||
[](std::tuple<Linq<S, T>, std::vector<T>, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
std::vector<T> &values = std::get<1>(tuple);
|
||||
int &index = std::get<2>(tuple);
|
||||
|
||||
if (index < values.size()) {
|
||||
return values[index++];
|
||||
}
|
||||
return linq.next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename F, typename _TRet = typename std::result_of<F(T, int)>::type>
|
||||
Linq<std::tuple<Linq<S, T>, int>, _TRet> select_i(F apply) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, int>, _TRet>(
|
||||
std::make_tuple(*this, 0),
|
||||
[apply](std::tuple<Linq<S, T>, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
int &index = std::get<1>(tuple);
|
||||
|
||||
return apply(linq.next(), index++);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename F, typename _TRet = typename std::result_of<F(T)>::type>
|
||||
Linq<std::tuple<Linq<S, T>, int>, _TRet> select(F apply) const
|
||||
{
|
||||
return select_i([apply](T value, int /*index*/) { return apply(value); });
|
||||
}
|
||||
|
||||
template<typename TRet>
|
||||
Linq<std::tuple<Linq<S, T>, int>, TRet> cast() const
|
||||
{
|
||||
return select_i([](T value, int /*i*/) { return TRet(value); });
|
||||
}
|
||||
|
||||
template<typename S2, typename T2>
|
||||
Linq<std::tuple<Linq<S, T>, Linq<S2, T2>, bool>, T> concat(const Linq<S2, T2> & rhs) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, Linq<S2, T2>, bool>, T>(
|
||||
std::make_tuple(*this, rhs, false),
|
||||
[](std::tuple<Linq<S, T>, Linq<S2, T2>, bool> &tuple){
|
||||
Linq<S, T> &first = std::get<0>(tuple);
|
||||
Linq<S2, T2> &second = std::get<1>(tuple);
|
||||
bool &flag = std::get<2>(tuple);
|
||||
|
||||
if (!flag) {
|
||||
try {
|
||||
return first.next();
|
||||
}
|
||||
catch (LinqEndException &) {}
|
||||
}
|
||||
return second.next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
typename F,
|
||||
typename _TRet = typename std::result_of<F(T, int)>::type,
|
||||
typename _TRetVal = typename _TRet::value_type
|
||||
>
|
||||
Linq<std::tuple<Linq<S, T>, _TRet, int, bool>, _TRetVal> selectMany_i(F apply) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, _TRet, int, bool>, _TRetVal>(
|
||||
std::make_tuple(*this, _TRet(), 0, true),
|
||||
[apply](std::tuple<Linq<S, T>, _TRet, int, bool> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
_TRet ¤t = std::get<1>(tuple);
|
||||
int &index = std::get<2>(tuple);
|
||||
bool &finished = std::get<3>(tuple);
|
||||
|
||||
while (true) {
|
||||
if (finished) {
|
||||
current = apply(linq.next(), index++);
|
||||
finished = false;
|
||||
}
|
||||
try {
|
||||
return current.next();
|
||||
}
|
||||
catch (LinqEndException &) {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<
|
||||
typename F,
|
||||
typename _TRet = typename std::result_of<F(T)>::type,
|
||||
typename _TRetVal = typename _TRet::value_type
|
||||
>
|
||||
Linq<std::tuple<Linq<S, T>, _TRet, int, bool>, _TRetVal> selectMany(F apply) const
|
||||
{
|
||||
return selectMany_i([apply](T value, int index) { return apply(value); });
|
||||
}
|
||||
|
||||
template<
|
||||
typename F,
|
||||
typename _TKey = typename std::result_of<F(T)>::type,
|
||||
typename _TValue = Linq<std::tuple<Linq<S, T>, int>, T> // where(predicate)
|
||||
>
|
||||
Linq<std::tuple<Linq<S, T>, Linq<S, T>, std::unordered_set<_TKey> >, std::pair<_TKey, _TValue> > groupBy(F apply) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, Linq<S, T>, std::unordered_set<_TKey> >, std::pair<_TKey, _TValue> >(
|
||||
std::make_tuple(*this, *this, std::unordered_set<_TKey>()),
|
||||
[apply](std::tuple<Linq<S, T>, Linq<S, T>, std::unordered_set<_TKey> > &tuple){
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
Linq<S, T> &linqCopy = std::get<1>(tuple);
|
||||
std::unordered_set<_TKey> &set = std::get<2>(tuple);
|
||||
|
||||
_TKey key = apply(linq.next());
|
||||
if (set.insert(key).second) {
|
||||
return std::make_pair(key, linqCopy.where([apply, key](T v){
|
||||
return apply(v) == key;
|
||||
}));
|
||||
}
|
||||
throw LinqEndException();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename F, typename _TRet = typename std::result_of<F(T)>::type>
|
||||
Linq<std::tuple<Linq<S, T>, std::unordered_set<_TRet> >, T> distinct(F transform) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, std::unordered_set<_TRet> >, T>(
|
||||
std::make_tuple(*this, std::unordered_set<_TRet>()),
|
||||
[transform](std::tuple<Linq<S, T>, std::unordered_set<_TRet> > &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
std::unordered_set<_TRet> &set = std::get<1>(tuple);
|
||||
|
||||
while (true) {
|
||||
T value = linq.next();
|
||||
if (set.insert(transform(value)).second) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, std::unordered_set<T> >, T> distinct() const
|
||||
{
|
||||
return distinct([](T value) { return value; });
|
||||
}
|
||||
|
||||
template<typename F, typename _TIter = typename std::vector<T>::const_iterator>
|
||||
Linq<std::tuple<std::vector<T>, _TIter, bool>, T> orderBy(F transform) const
|
||||
{
|
||||
std::vector<T> items = toStdVector();
|
||||
std::sort(items.begin(), items.end(), [transform](const T &a, const T &b) {
|
||||
return transform(a) < transform(b);
|
||||
});
|
||||
|
||||
return Linq<std::tuple<std::vector<T>, _TIter, bool>, T>(
|
||||
std::make_tuple(items, _TIter(), false),
|
||||
[](std::tuple<std::vector<T>, _TIter, bool> &tuple) {
|
||||
std::vector<T> &vec = std::get<0>(tuple);
|
||||
_TIter &it = std::get<1>(tuple);
|
||||
bool &flag = std::get<2>(tuple);
|
||||
|
||||
if (!flag) {
|
||||
flag = true;
|
||||
it = vec.cbegin();
|
||||
}
|
||||
if (it == vec.cend()) {
|
||||
throw LinqEndException();
|
||||
}
|
||||
return *(it++);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Linq<std::tuple<std::vector<T>, typename std::vector<T>::const_iterator, bool>, T> orderBy() const
|
||||
{
|
||||
return orderBy([](T value) { return value; });
|
||||
}
|
||||
|
||||
template<typename _TIter = typename std::list<T>::const_reverse_iterator>
|
||||
Linq<std::tuple<std::list<T>, _TIter, bool>, T> reverse() const
|
||||
{
|
||||
return Linq<std::tuple<std::list<T>, _TIter, bool>, T>(
|
||||
std::make_tuple(toStdList(), _TIter(), false),
|
||||
[](std::tuple<std::list<T>, _TIter, bool> &tuple) {
|
||||
std::list<T> &list = std::get<0>(tuple);
|
||||
_TIter &it = std::get<1>(tuple);
|
||||
bool &flag = std::get<2>(tuple);
|
||||
|
||||
if (!flag) {
|
||||
flag = true;
|
||||
it = list.crbegin();
|
||||
}
|
||||
if (it == list.crend()) {
|
||||
throw LinqEndException();
|
||||
}
|
||||
return *(it++);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Aggregators
|
||||
|
||||
template<typename TRet>
|
||||
TRet aggregate(TRet start, std::function<TRet(TRet, T)> accumulate) const
|
||||
{
|
||||
Linq<S, T> linq = *this;
|
||||
try {
|
||||
while (true) {
|
||||
start = accumulate(start, linq.next());
|
||||
}
|
||||
}
|
||||
catch (LinqEndException &) {}
|
||||
return start;
|
||||
}
|
||||
|
||||
template<typename F, typename _TRet = typename std::result_of<F(T)>::type>
|
||||
_TRet sum(F transform) const
|
||||
{
|
||||
return aggregate<_TRet>(_TRet(), [transform](_TRet accumulator, T value) {
|
||||
return accumulator + transform(value);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename TRet = T>
|
||||
TRet sum() const
|
||||
{
|
||||
return sum([](T value) { return TRet(value); });
|
||||
}
|
||||
|
||||
template<typename F, typename _TRet = typename std::result_of<F(T)>::type>
|
||||
_TRet avg(F transform) const
|
||||
{
|
||||
int count = 0;
|
||||
_TRet res = sum([transform, &count](T value) {
|
||||
count++;
|
||||
return transform(value);
|
||||
});
|
||||
return res / count;
|
||||
}
|
||||
|
||||
template<typename TRet = T>
|
||||
TRet avg() const
|
||||
{
|
||||
return avg([](T value) { return TRet(value); });
|
||||
}
|
||||
|
||||
int count() const
|
||||
{
|
||||
int index = 0;
|
||||
for_each([&index](T /*a*/) { index++; });
|
||||
return index;
|
||||
}
|
||||
|
||||
int count(std::function<bool(T)> predicate) const
|
||||
{
|
||||
return where(predicate).count();
|
||||
}
|
||||
|
||||
int count(const T &item) const
|
||||
{
|
||||
return count([item](T value) { return item == value; });
|
||||
}
|
||||
|
||||
// Bool aggregators
|
||||
|
||||
bool any(std::function<bool(T)> predicate) const
|
||||
{
|
||||
Linq<S, T> linq = *this;
|
||||
try {
|
||||
while (true) {
|
||||
if (predicate(linq.next()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (LinqEndException &) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool any() const
|
||||
{
|
||||
return any([](T value) { return static_cast<bool>(value); });
|
||||
}
|
||||
|
||||
bool all(std::function<bool(T)> predicate) const
|
||||
{
|
||||
return !any([predicate](T value) { return !predicate(value); });
|
||||
}
|
||||
|
||||
bool all() const
|
||||
{
|
||||
return all([](T value) { return static_cast<bool>(value); });
|
||||
}
|
||||
|
||||
bool contains(const T &item) const
|
||||
{
|
||||
return any([&item](T value) { return value == item; });
|
||||
}
|
||||
|
||||
// Election aggregators
|
||||
|
||||
T elect(std::function<T(T, T)> accumulate) const
|
||||
{
|
||||
T result;
|
||||
for_each_i([accumulate, &result](T value, int i) {
|
||||
if (i == 0) {
|
||||
result = value;
|
||||
} else {
|
||||
result = accumulate(result, value);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
T max(F transform) const
|
||||
{
|
||||
return elect([transform](const T &a, const T &b) {
|
||||
return (transform(a) < transform(b)) ? b : a;
|
||||
});
|
||||
}
|
||||
|
||||
T max() const
|
||||
{
|
||||
return max([](T value) { return value; });
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
T min(F transform) const
|
||||
{
|
||||
return elect([transform](const T &a, const T &b) {
|
||||
return (transform(a) < transform(b)) ? a : b;
|
||||
});
|
||||
}
|
||||
|
||||
T min() const
|
||||
{
|
||||
return min([](T value) { return value; });
|
||||
}
|
||||
|
||||
// Single object returners
|
||||
|
||||
T elementAt(int index) const
|
||||
{
|
||||
return skip(index).next();
|
||||
}
|
||||
|
||||
T first(std::function<bool(T)> predicate) const
|
||||
{
|
||||
return where(predicate).next();
|
||||
}
|
||||
|
||||
T first() const
|
||||
{
|
||||
return Linq<S, T>(*this).next();
|
||||
}
|
||||
|
||||
T firstOrDefault(std::function<bool(T)> predicate, T const& defaultValue = T()) const
|
||||
{
|
||||
try {
|
||||
return where(predicate).next();
|
||||
}
|
||||
catch (LinqEndException &) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
T firstOrDefault(T const& defaultValue = T()) const
|
||||
{
|
||||
try {
|
||||
return Linq<S, T>(*this).next();
|
||||
}
|
||||
catch (LinqEndException &) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
T last(std::function<bool(T)> predicate) const
|
||||
{
|
||||
T res;
|
||||
int index = -1;
|
||||
where(predicate).for_each_i([&res, &index](T value, int i) {
|
||||
res = value;
|
||||
index = i;
|
||||
});
|
||||
|
||||
if (index == -1) {
|
||||
throw LinqEndException();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
T last() const
|
||||
{
|
||||
return last([](T /*value*/) { return true; });
|
||||
}
|
||||
|
||||
T lastOrDefault(std::function<bool(T)> predicate, T const& defaultValue = T()) const
|
||||
{
|
||||
T res = defaultValue;
|
||||
where(predicate).for_each([&res](T value) {
|
||||
res = value;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
T lastOrDefault(T const& defaultValue = T()) const
|
||||
{
|
||||
return lastOrDefault([](T /*value*/) { return true; }, defaultValue);
|
||||
}
|
||||
|
||||
// Export to containers
|
||||
|
||||
std::vector<T> toStdVector() const
|
||||
{
|
||||
std::vector<T> items;
|
||||
for_each([&items](T value) {
|
||||
items.push_back(value);
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
std::list<T> toStdList() const
|
||||
{
|
||||
std::list<T> items;
|
||||
for_each([&items](T value) {
|
||||
items.push_back(value);
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
std::deque<T> toStdDeque() const
|
||||
{
|
||||
std::deque<T> items;
|
||||
for_each([&items](T value) {
|
||||
items.push_back(value);
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
std::set<T> toStdSet() const
|
||||
{
|
||||
std::set<T> items;
|
||||
for_each([&items](T value) {
|
||||
items.insert(value);
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
std::unordered_set<T> toStdUnorderedSet() const
|
||||
{
|
||||
std::unordered_set<T> items;
|
||||
for_each([&items](T value) {
|
||||
items.insert(value);
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
// Bits and bytes
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, BytesDirection, T, int>, int> bytes(BytesDirection direction = BytesFirstToLast) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, BytesDirection, T, int>, int>(
|
||||
std::make_tuple(*this, direction, T(), sizeof(T)),
|
||||
[](std::tuple<Linq<S, T>, BytesDirection, T, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
BytesDirection &bytesDirection = std::get<1>(tuple);
|
||||
T &value = std::get<2>(tuple);
|
||||
int &index = std::get<3>(tuple);
|
||||
|
||||
if (index == sizeof(T)) {
|
||||
value = linq.next();
|
||||
index = 0;
|
||||
}
|
||||
|
||||
unsigned char *ptr = reinterpret_cast<unsigned char *>(&value);
|
||||
|
||||
int byteIndex = index;
|
||||
if (bytesDirection == BytesLastToFirst) {
|
||||
byteIndex = sizeof(T) - 1 - byteIndex;
|
||||
}
|
||||
|
||||
index++;
|
||||
return ptr[byteIndex];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename TRet>
|
||||
Linq<std::tuple<Linq<S, T>, BytesDirection, int>, TRet> unbytes(BytesDirection direction = BytesFirstToLast) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, BytesDirection, int>, TRet>(
|
||||
std::make_tuple(*this, direction, 0),
|
||||
[](std::tuple<Linq<S, T>, BytesDirection, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
BytesDirection &bytesDirection = std::get<1>(tuple);
|
||||
int &index = std::get<2>(tuple);
|
||||
|
||||
TRet value;
|
||||
unsigned char *ptr = reinterpret_cast<unsigned char *>(&value);
|
||||
|
||||
for (int i = 0; i < sizeof(TRet); i++) {
|
||||
int byteIndex = i;
|
||||
if (bytesDirection == BytesLastToFirst) {
|
||||
byteIndex = sizeof(TRet) - 1 - byteIndex;
|
||||
}
|
||||
|
||||
ptr[byteIndex] = linq.next();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Linq<std::tuple<Linq<S, T>, BytesDirection, BitsDirection, T, int>, int> bits(BitsDirection bitsDir = BitsHighToLow, BytesDirection bytesDir = BytesFirstToLast) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, BytesDirection, BitsDirection, T, int>, int>(
|
||||
std::make_tuple(*this, bytesDir, bitsDir, T(), sizeof(T) * CHAR_BIT),
|
||||
[](std::tuple<Linq<S, T>, BytesDirection, BitsDirection, T, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
BytesDirection &bytesDirection = std::get<1>(tuple);
|
||||
BitsDirection &bitsDirection = std::get<2>(tuple);
|
||||
T &value = std::get<3>(tuple);
|
||||
int &index = std::get<4>(tuple);
|
||||
|
||||
if (index == sizeof(T) * CHAR_BIT) {
|
||||
value = linq.next();
|
||||
index = 0;
|
||||
}
|
||||
|
||||
unsigned char *ptr = reinterpret_cast<unsigned char *>(&value);
|
||||
|
||||
int byteIndex = index / CHAR_BIT;
|
||||
if (bytesDirection == BytesLastToFirst) {
|
||||
byteIndex = sizeof(T) - 1 - byteIndex;
|
||||
}
|
||||
|
||||
int bitIndex = index % CHAR_BIT;
|
||||
if (bitsDirection == BitsHighToLow) {
|
||||
bitIndex = CHAR_BIT - 1 - bitIndex;
|
||||
}
|
||||
|
||||
index++;
|
||||
return (ptr[byteIndex] >> bitIndex) & 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename TRet = unsigned char>
|
||||
Linq<std::tuple<Linq<S, T>, BytesDirection, BitsDirection, int>, TRet> unbits(BitsDirection bitsDir = BitsHighToLow, BytesDirection bytesDir = BytesFirstToLast) const
|
||||
{
|
||||
return Linq<std::tuple<Linq<S, T>, BytesDirection, BitsDirection, int>, TRet>(
|
||||
std::make_tuple(*this, bytesDir, bitsDir, 0),
|
||||
[](std::tuple<Linq<S, T>, BytesDirection, BitsDirection, int> &tuple) {
|
||||
Linq<S, T> &linq = std::get<0>(tuple);
|
||||
BytesDirection &bytesDirection = std::get<1>(tuple);
|
||||
BitsDirection &bitsDirection = std::get<2>(tuple);
|
||||
int &index = std::get<3>(tuple);
|
||||
|
||||
TRet value = TRet();
|
||||
unsigned char *ptr = reinterpret_cast<unsigned char *>(&value);
|
||||
|
||||
for (int i = 0; i < sizeof(TRet) * CHAR_BIT; i++) {
|
||||
int byteIndex = i / CHAR_BIT;
|
||||
if (bytesDirection == BytesLastToFirst) {
|
||||
byteIndex = sizeof(TRet) - 1 - byteIndex;
|
||||
}
|
||||
|
||||
int bitIndex = i % CHAR_BIT;
|
||||
if (bitsDirection == BitsHighToLow) {
|
||||
bitIndex = CHAR_BIT - 1 - bitIndex;
|
||||
}
|
||||
|
||||
ptr[byteIndex] &= ~(1 << bitIndex);
|
||||
ptr[byteIndex] |= bool(linq.next()) << bitIndex;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
std::ostream &operator<<(std::ostream &stream, Linq<S, T> linq)
|
||||
{
|
||||
try {
|
||||
while (true) {
|
||||
stream << linq.next() << ' ';
|
||||
}
|
||||
}
|
||||
catch (LinqEndException &) {}
|
||||
return stream;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Linq Creators
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T>
|
||||
Linq<std::pair<T, T>, typename std::iterator_traits<T>::value_type> from(const T & begin, const T & end)
|
||||
{
|
||||
return Linq<std::pair<T, T>, typename std::iterator_traits<T>::value_type>(
|
||||
std::make_pair(begin, end),
|
||||
[](std::pair<T, T> &pair) {
|
||||
if (pair.first == pair.second) {
|
||||
throw LinqEndException();
|
||||
}
|
||||
return *(pair.first++);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Linq<std::pair<T, T>, typename std::iterator_traits<T>::value_type> from(const T & it, int n)
|
||||
{
|
||||
return from(it, it + n);
|
||||
}
|
||||
|
||||
template<typename T, int N>
|
||||
Linq<std::pair<const T *, const T *>, T> from(T (&array)[N])
|
||||
{
|
||||
return from((const T *)(&array), (const T *)(&array) + N);
|
||||
}
|
||||
|
||||
template<template<class> class TV, typename TT>
|
||||
auto from(const TV<TT> & container)
|
||||
-> decltype(from(container.cbegin(), container.cend()))
|
||||
{
|
||||
return from(container.cbegin(), container.cend());
|
||||
}
|
||||
|
||||
// std::list, std::vector, std::dequeue
|
||||
template<template<class, class> class TV, typename TT, typename TU>
|
||||
auto from(const TV<TT, TU> & container)
|
||||
-> decltype(from(container.cbegin(), container.cend()))
|
||||
{
|
||||
return from(container.cbegin(), container.cend());
|
||||
}
|
||||
|
||||
// std::set
|
||||
template<template<class, class, class> class TV, typename TT, typename TS, typename TU>
|
||||
auto from(const TV<TT, TS, TU> & container)
|
||||
-> decltype(from(container.cbegin(), container.cend()))
|
||||
{
|
||||
return from(container.cbegin(), container.cend());
|
||||
}
|
||||
|
||||
// std::map
|
||||
template<template<class, class, class, class> class TV, typename TK, typename TT, typename TS, typename TU>
|
||||
auto from(const TV<TK, TT, TS, TU> & container)
|
||||
-> decltype(from(container.cbegin(), container.cend()))
|
||||
{
|
||||
return from(container.cbegin(), container.cend());
|
||||
}
|
||||
|
||||
// std::array
|
||||
template<template<class, size_t> class TV, typename TT, size_t TL>
|
||||
auto from(const TV<TT, TL> & container)
|
||||
-> decltype(from(container.cbegin(), container.cend()))
|
||||
{
|
||||
return from(container.cbegin(), container.cend());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Linq<std::pair<T, int>, T> repeat(const T & value, int count) {
|
||||
return Linq<std::pair<T, int>, T>(
|
||||
std::make_pair(value, count),
|
||||
[](std::pair<T, int> &pair) {
|
||||
if (pair.second > 0) {
|
||||
pair.second--;
|
||||
return pair.first;
|
||||
}
|
||||
throw LinqEndException();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Linq<std::tuple<T, T, T>, T> range(const T & start, const T & end, const T & step) {
|
||||
return Linq<std::tuple<T, T, T>, T>(
|
||||
std::make_tuple(start, end, step),
|
||||
[](std::tuple<T, T, T> &tuple) {
|
||||
T &start = std::get<0>(tuple);
|
||||
T &end = std::get<1>(tuple);
|
||||
T &step = std::get<2>(tuple);
|
||||
|
||||
T value = start;
|
||||
if (value < end) {
|
||||
start += step;
|
||||
return value;
|
||||
}
|
||||
throw LinqEndException();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -214,6 +214,16 @@ Qt::ItemFlags MessagesModel::flags(const QModelIndex& index) const {
|
|||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren;
|
||||
}
|
||||
|
||||
QList<Message> MessagesModel::messagesAt(QList<int> row_indices) const {
|
||||
QList<Message> msgs;
|
||||
|
||||
for (int idx : row_indices) {
|
||||
msgs << messageAt(idx);
|
||||
}
|
||||
|
||||
return msgs;
|
||||
}
|
||||
|
||||
QVariant MessagesModel::data(int row, int column, int role) const {
|
||||
return data(index(row, column), role);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer {
|
|||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
|
||||
// Returns message at given index.
|
||||
|
||||
QList<Message> messagesAt(QList<int> row_indices) const;
|
||||
Message messageAt(int row_index) const;
|
||||
int messageId(int row_index) const;
|
||||
RootItem::Importance messageImportance(int row_index) const;
|
||||
|
|
|
@ -68,6 +68,13 @@ void FormAbout::loadLicenseAndInformation() {
|
|||
m_ui.m_txtLicenseBsd->setText(tr("License not found."));
|
||||
}
|
||||
|
||||
try {
|
||||
m_ui.m_txtLicenseMit->setText(IOFactory::readFile(APP_INFO_PATH + QL1S("/COPYING_MIT")));
|
||||
}
|
||||
catch (...) {
|
||||
m_ui.m_txtLicenseMit->setText(tr("License not found."));
|
||||
}
|
||||
|
||||
// Set other informative texts.
|
||||
m_ui.m_lblDesc->setText(tr("<b>%8</b><br>" "<b>Version:</b> %1 (built on %2/%3)<br>" "<b>Revision:</b> %4<br>" "<b>Build date:</b> %5<br>"
|
||||
"<b>Qt:</b> %6 (compiled against %7)<br>").arg(
|
||||
|
|
|
@ -160,7 +160,7 @@ p, li { white-space: pre-wrap; }
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>685</width>
|
||||
<height>184</height>
|
||||
<height>157</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
|
@ -235,8 +235,8 @@ p, li { white-space: pre-wrap; }
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>98</width>
|
||||
<height>69</height>
|
||||
<width>685</width>
|
||||
<height>157</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
|
@ -288,6 +288,68 @@ p, li { white-space: pre-wrap; }
|
|||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:8.25pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_MIT">
|
||||
<attribute name="label">
|
||||
<string>MIT License (applies to boolinq source code)</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="m_txtLicenseMit">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>DejaVu Sans Mono</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="autoFormatting">
|
||||
<set>QTextEdit::AutoNone</set>
|
||||
</property>
|
||||
<property name="undoRedoEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="lineWrapMode">
|
||||
<enum>QTextEdit::WidgetWidth</enum>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string notr="true"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:8.25pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
|
|
|
@ -312,7 +312,7 @@ void FormMain::updateRecycleBinMenu() {
|
|||
no_action->setEnabled(false);
|
||||
root_menu->addAction(no_action);
|
||||
}
|
||||
else if ((context_menu = bin->contextMenu()).isEmpty()) {
|
||||
else if ((context_menu = bin->contextMenuFeedsList()).isEmpty()) {
|
||||
QAction* no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")),
|
||||
tr("No actions possible"),
|
||||
m_ui->m_menuRecycleBin);
|
||||
|
|
|
@ -430,7 +430,7 @@ QMenu* FeedsView::initializeContextMenuBin(RootItem* clicked_item) {
|
|||
m_contextMenuBin->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||
|
||||
m_contextMenuBin->addActions(QList<QAction*>() <<
|
||||
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
|
||||
|
@ -453,7 +453,7 @@ QMenu* FeedsView::initializeContextMenuService(RootItem* clicked_item) {
|
|||
m_contextMenuService->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||
|
||||
m_contextMenuService->addActions(QList<QAction*>() <<
|
||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||
|
@ -511,7 +511,7 @@ QMenu* FeedsView::initializeContextMenuCategories(RootItem* clicked_item) {
|
|||
m_contextMenuCategories->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||
|
||||
m_contextMenuCategories->addActions(QList<QAction*>() <<
|
||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||
|
@ -538,7 +538,7 @@ QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) {
|
|||
m_contextMenuFeeds->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||
|
||||
m_contextMenuFeeds->addActions(QList<QAction*>() <<
|
||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||
|
@ -565,7 +565,7 @@ QMenu* FeedsView::initializeContextMenuImportant(RootItem* clicked_item) {
|
|||
m_contextMenuImportant->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||
|
||||
m_contextMenuImportant->addActions(QList<QAction*>() <<
|
||||
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
|
||||
|
@ -598,7 +598,7 @@ QMenu* FeedsView::initializeContextMenuOtherItem(RootItem* clicked_item) {
|
|||
m_contextMenuOtherItems->clear();
|
||||
}
|
||||
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
||||
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||
|
||||
if (!specific_actions.isEmpty()) {
|
||||
m_contextMenuOtherItems->addSeparator();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "gui/messagesview.h"
|
||||
|
||||
#include "3rd-party/boolinq/boolinq.h"
|
||||
#include "core/messagesmodel.h"
|
||||
#include "core/messagesproxymodel.h"
|
||||
#include "gui/dialogs/formmain.h"
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include "miscellaneous/settings.h"
|
||||
#include "network-web/networkfactory.h"
|
||||
#include "network-web/webfactory.h"
|
||||
#include "services/abstract/serviceroot.h"
|
||||
|
||||
#include <QFileIconProvider>
|
||||
#include <QKeyEvent>
|
||||
|
@ -213,12 +215,33 @@ void MessagesView::initializeContextMenu() {
|
|||
|
||||
m_contextMenu->addMenu(menu);
|
||||
m_contextMenu->addActions(
|
||||
QList<QAction*>() << qApp->mainForm()->m_ui->m_actionSendMessageViaEmail << qApp->mainForm()->m_ui->m_actionOpenSelectedSourceArticlesExternally << qApp->mainForm()->m_ui->m_actionOpenSelectedMessagesInternally << qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsRead << qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsUnread << qApp->mainForm()->m_ui->m_actionSwitchImportanceOfSelectedMessages <<
|
||||
qApp->mainForm()->m_ui->m_actionDeleteSelectedMessages);
|
||||
QList<QAction*>()
|
||||
<< qApp->mainForm()->m_ui->m_actionSendMessageViaEmail
|
||||
<< qApp->mainForm()->m_ui->m_actionOpenSelectedSourceArticlesExternally
|
||||
<< qApp->mainForm()->m_ui->m_actionOpenSelectedMessagesInternally
|
||||
<< qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsRead
|
||||
<< qApp->mainForm()->m_ui->m_actionMarkSelectedMessagesAsUnread
|
||||
<< qApp->mainForm()->m_ui->m_actionSwitchImportanceOfSelectedMessages
|
||||
<< qApp->mainForm()->m_ui->m_actionDeleteSelectedMessages);
|
||||
|
||||
if (m_sourceModel->loadedItem() != nullptr && m_sourceModel->loadedItem()->kind() == RootItemKind::Bin) {
|
||||
if (m_sourceModel->loadedItem() != nullptr) {
|
||||
if (m_sourceModel->loadedItem()->kind() == RootItemKind::Bin) {
|
||||
m_contextMenu->addAction(qApp->mainForm()->m_ui->m_actionRestoreSelectedMessages);
|
||||
}
|
||||
|
||||
QModelIndexList selected_indexes = selectionModel()->selectedRows();
|
||||
const QModelIndexList mapped_indexes = m_proxyModel->mapListToSource(selected_indexes);
|
||||
auto rows = boolinq::from(mapped_indexes).select([](const QModelIndex& idx) {
|
||||
return idx.row();
|
||||
}).toStdList();
|
||||
auto messages = m_sourceModel->messagesAt(QList<int>::fromStdList(rows));
|
||||
auto extra_context_menu = m_sourceModel->loadedItem()->getParentServiceRoot()->contextMenuMessagesList(messages);
|
||||
|
||||
if (!extra_context_menu.isEmpty()) {
|
||||
m_contextMenu->addSeparator();
|
||||
m_contextMenu->addActions(extra_context_menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MessagesView::mousePressEvent(QMouseEvent* event) {
|
||||
|
|
|
@ -19,14 +19,8 @@ class MessagesView : public QTreeView {
|
|||
explicit MessagesView(QWidget* parent = nullptr);
|
||||
virtual ~MessagesView();
|
||||
|
||||
// Model accessors.
|
||||
inline MessagesProxyModel* model() const {
|
||||
return m_proxyModel;
|
||||
}
|
||||
|
||||
inline MessagesModel* sourceModel() const {
|
||||
return m_sourceModel;
|
||||
}
|
||||
MessagesProxyModel* model() const;
|
||||
MessagesModel* sourceModel() const;
|
||||
|
||||
void reloadFontSettings();
|
||||
|
||||
|
@ -110,4 +104,12 @@ class MessagesView : public QTreeView {
|
|||
bool m_columnsAdjusted;
|
||||
};
|
||||
|
||||
inline MessagesProxyModel* MessagesView::model() const {
|
||||
return m_proxyModel;
|
||||
}
|
||||
|
||||
inline MessagesModel* MessagesView::sourceModel() const {
|
||||
return m_sourceModel;
|
||||
}
|
||||
|
||||
#endif // MESSAGESVIEW_H
|
||||
|
|
|
@ -46,7 +46,7 @@ void RecycleBin::updateCounts(bool update_total_count) {
|
|||
}
|
||||
}
|
||||
|
||||
QList<QAction*> RecycleBin::contextMenu() {
|
||||
QList<QAction*> RecycleBin::contextMenuFeedsList() {
|
||||
if (m_contextMenu.isEmpty()) {
|
||||
QAction* restore_action = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")),
|
||||
tr("Restore recycle bin"),
|
||||
|
|
|
@ -14,7 +14,7 @@ class RecycleBin : public RootItem {
|
|||
|
||||
QString additionalTooltip() const;
|
||||
|
||||
QList<QAction*> contextMenu();
|
||||
QList<QAction*> contextMenuFeedsList();
|
||||
QList<Message> undeletedMessages() const;
|
||||
|
||||
bool markAsReadUnread(ReadStatus status);
|
||||
|
|
|
@ -44,7 +44,7 @@ QString RootItem::additionalTooltip() const {
|
|||
return QString();
|
||||
}
|
||||
|
||||
QList<QAction*> RootItem::contextMenu() {
|
||||
QList<QAction*> RootItem::contextMenuFeedsList() {
|
||||
return QList<QAction*>();
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ class RSSGUARD_DLLSPEC RootItem : public QObject {
|
|||
// Returns list of specific actions which can be done with the item.
|
||||
// Do not include general actions here like actions: Mark as read, Update, ...
|
||||
// NOTE: Ownership of returned actions is not switched to caller, free them when needed.
|
||||
virtual QList<QAction*> contextMenu();
|
||||
virtual QList<QAction*> contextMenuFeedsList();
|
||||
|
||||
// Can properties of this item be edited?
|
||||
virtual bool canBeEdited() const;
|
||||
|
|
|
@ -68,11 +68,11 @@ bool ServiceRoot::downloadAttachmentOnMyOwn(const QUrl& url) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
QList<QAction*> ServiceRoot::contextMenu() {
|
||||
QList<QAction*> ServiceRoot::contextMenuFeedsList() {
|
||||
return serviceMenu();
|
||||
}
|
||||
|
||||
QList<QAction*> ServiceRoot::contextMenuForMessages(const QList<Message*>& messages) {
|
||||
QList<QAction*> ServiceRoot::contextMenuMessagesList(const QList<Message>& messages) {
|
||||
Q_UNUSED(messages)
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -53,9 +53,11 @@ class ServiceRoot : public RootItem {
|
|||
// NOTE: Caller does NOT take ownership of created menu!
|
||||
virtual QList<QAction*> addItemMenu();
|
||||
|
||||
// Returns actions to display as context menu.
|
||||
virtual QList<QAction*> contextMenu();
|
||||
virtual QList<QAction*> contextMenuForMessages(const QList<Message*>& messages);
|
||||
// NOTE: Caller does NOT take ownership of created menu!
|
||||
virtual QList<QAction*> contextMenuFeedsList();
|
||||
|
||||
// NOTE: Caller does NOT take ownership of created menu!
|
||||
virtual QList<QAction*> contextMenuMessagesList(const QList<Message>& messages);
|
||||
|
||||
// Returns list of specific actions to be shown in main window menu
|
||||
// bar in sections "Services -> 'this service'".
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
|
||||
#include <QFileDialog>
|
||||
|
||||
GmailServiceRoot::GmailServiceRoot(GmailNetworkFactory* network, RootItem* parent) : ServiceRoot(parent), m_network(network) {
|
||||
GmailServiceRoot::GmailServiceRoot(GmailNetworkFactory* network, RootItem* parent)
|
||||
: ServiceRoot(parent), m_network(network), m_actionReply(nullptr) {
|
||||
if (network == nullptr) {
|
||||
m_network = new GmailNetworkFactory(this);
|
||||
}
|
||||
|
@ -36,6 +37,10 @@ void GmailServiceRoot::updateTitle() {
|
|||
setTitle(m_network->username() + QSL(" (Gmail)"));
|
||||
}
|
||||
|
||||
void GmailServiceRoot::replyToEmail() {
|
||||
FormAddEditEmail(this, qApp->mainFormWidget()).execForReply(&m_replyToMessage);
|
||||
}
|
||||
|
||||
RootItem* GmailServiceRoot::obtainNewTreeForSyncIn() const {
|
||||
auto* root = new RootItem();
|
||||
GmailFeed* inbox = new GmailFeed(tr("Inbox"), QSL(GMAIL_SYSTEM_LABEL_INBOX), qApp->icons()->fromTheme(QSL("mail-inbox")), root);
|
||||
|
@ -128,6 +133,22 @@ bool GmailServiceRoot::downloadAttachmentOnMyOwn(const QUrl& url) const {
|
|||
}
|
||||
}
|
||||
|
||||
QList<QAction*> GmailServiceRoot::contextMenuMessagesList(const QList<Message>& messages) {
|
||||
if (messages.size() == 1) {
|
||||
if (m_actionReply == nullptr) {
|
||||
m_actionReply = new QAction(qApp->icons()->fromTheme(QSL("mail-reply-sender")), tr("Reply"), this);
|
||||
|
||||
m_replyToMessage = messages.at(0);
|
||||
connect(m_actionReply, &QAction::triggered, this, &GmailServiceRoot::replyToEmail);
|
||||
}
|
||||
|
||||
return { m_actionReply };
|
||||
}
|
||||
else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QList<QAction*> GmailServiceRoot::serviceMenu() {
|
||||
if (m_serviceMenu.isEmpty()) {
|
||||
ServiceRoot::serviceMenu();
|
||||
|
|
|
@ -22,6 +22,7 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
|||
void setNetwork(GmailNetworkFactory* network);
|
||||
GmailNetworkFactory* network() const;
|
||||
|
||||
QList<QAction*> contextMenuMessagesList(const QList<Message>& messages);
|
||||
QList<QAction*> serviceMenu();
|
||||
bool isSyncable() const;
|
||||
bool canBeEdited() const;
|
||||
|
@ -41,6 +42,9 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
|||
public slots:
|
||||
void updateTitle();
|
||||
|
||||
private slots:
|
||||
void replyToEmail();
|
||||
|
||||
protected:
|
||||
RootItem* obtainNewTreeForSyncIn() const;
|
||||
|
||||
|
@ -50,6 +54,8 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
|||
|
||||
private:
|
||||
GmailNetworkFactory* m_network;
|
||||
QAction* m_actionReply;
|
||||
Message m_replyToMessage;
|
||||
};
|
||||
|
||||
inline void GmailServiceRoot::setNetwork(GmailNetworkFactory* network) {
|
||||
|
|
|
@ -112,15 +112,8 @@ void FormAddEditEmail::onOkClicked() {
|
|||
msg.set_plain(m_ui.m_txtMessage->toPlainText().toStdString());
|
||||
msg.set_header("Content-Type", "text/plain; charset=utf-8");
|
||||
|
||||
if (m_originalMessage == nullptr) {
|
||||
// Send completely new message.
|
||||
}
|
||||
else {
|
||||
// TODO: Reply to existing message.
|
||||
}
|
||||
|
||||
try {
|
||||
m_root->network()->sendEmail(msg);
|
||||
m_root->network()->sendEmail(msg, m_originalMessage);
|
||||
accept();
|
||||
}
|
||||
catch (const ApplicationException& ex) {
|
||||
|
@ -141,7 +134,9 @@ void FormAddEditEmail::addRecipientRow(const QString& recipient) {
|
|||
|
||||
mail_rec->setPossibleRecipients(rec);
|
||||
}
|
||||
catch (const ApplicationException& ex) {}
|
||||
catch (const ApplicationException& ex) {
|
||||
qWarning("Failed to get recipients: '%s'.", qPrintable(ex.message()));
|
||||
}
|
||||
|
||||
m_ui.m_layout->insertRow(m_ui.m_layout->count() - 5, mail_rec);
|
||||
}
|
||||
|
|
|
@ -52,13 +52,31 @@ void GmailNetworkFactory::setBatchSize(int batch_size) {
|
|||
m_batchSize = batch_size;
|
||||
}
|
||||
|
||||
QString GmailNetworkFactory::sendEmail(const Mimesis::Message& msg) {
|
||||
QString GmailNetworkFactory::sendEmail(Mimesis::Message msg, Message* reply_to_message) {
|
||||
QString bearer = m_oauth2->bearer().toLocal8Bit();
|
||||
|
||||
if (bearer.isEmpty()) {
|
||||
throw ApplicationException(tr("you aren't logged in"));
|
||||
}
|
||||
|
||||
if (reply_to_message != nullptr) {
|
||||
// We need to obtain some extra information.
|
||||
|
||||
auto metadata = getMessageMetadata(reply_to_message->m_customId, {
|
||||
QSL("References"),
|
||||
QSL("Message-ID")
|
||||
});
|
||||
|
||||
/*if (metadata.contains(QSL("References"))) {
|
||||
|
||||
}*/
|
||||
|
||||
if (metadata.contains(QSL("Message-ID"))) {
|
||||
msg["References"] = metadata.value(QSL("Message-ID")).toString().toStdString();
|
||||
msg["In-Reply-To"] = metadata.value(QSL("Message-ID")).toString().toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
QString rfc_email = QString::fromStdString(msg.to_string());
|
||||
QByteArray input_data = rfc_email.toUtf8();
|
||||
QList<QPair<QByteArray, QByteArray>> headers;
|
||||
|
@ -501,6 +519,36 @@ QStringList GmailNetworkFactory::getAllRecipients() {
|
|||
}
|
||||
}
|
||||
|
||||
QVariantMap GmailNetworkFactory::getMessageMetadata(const QString& msg_id, const QStringList& metadata) {
|
||||
QString bearer = m_oauth2->bearer();
|
||||
|
||||
if (bearer.isEmpty()) {
|
||||
throw ApplicationException(tr("you are not logged in"));
|
||||
}
|
||||
|
||||
QList<QPair<QByteArray, QByteArray>> headers;
|
||||
QByteArray output;
|
||||
int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt();
|
||||
|
||||
headers.append(QPair<QByteArray, QByteArray>(QString(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
|
||||
bearer.toLocal8Bit()));
|
||||
|
||||
QString query = QString("%1/%2?format=metadata&metadataHeaders=%3").arg(GMAIL_API_MSGS_LIST,
|
||||
msg_id,
|
||||
metadata.join(QSL("&metadataHeaders=")));
|
||||
NetworkResult res = NetworkFactory::performNetworkOperation(query,
|
||||
timeout,
|
||||
QByteArray(),
|
||||
output,
|
||||
QNetworkAccessManager::Operation::GetOperation,
|
||||
headers);
|
||||
|
||||
if (res.first == QNetworkReply::NetworkError::NoError) {}
|
||||
else {
|
||||
throw ApplicationException(tr("failed to get metadata"));
|
||||
}
|
||||
}
|
||||
|
||||
bool GmailNetworkFactory::obtainAndDecodeFullMessages(const QList<Message>& lite_messages,
|
||||
const QString& feed_id,
|
||||
QList<Message>& full_messages) {
|
||||
|
|
|
@ -36,7 +36,7 @@ class GmailNetworkFactory : public QObject {
|
|||
void setBatchSize(int batch_size);
|
||||
|
||||
// Sends e-mail, returns its ID.
|
||||
QString sendEmail(const Mimesis::Message& msg);
|
||||
QString sendEmail(Mimesis::Message msg, Message* reply_to_message = nullptr);
|
||||
|
||||
// Returns all possible recipients.
|
||||
QStringList getAllRecipients();
|
||||
|
@ -53,6 +53,7 @@ class GmailNetworkFactory : public QObject {
|
|||
|
||||
private:
|
||||
bool fillFullMessage(Message& msg, const QJsonObject& json, const QString& feed_id);
|
||||
QVariantMap getMessageMetadata(const QString& msg_id, const QStringList& metadata);
|
||||
bool obtainAndDecodeFullMessages(const QList<Message>& lite_messages, const QString& feed_id, QList<Message>& full_messages);
|
||||
QList<Message> decodeLiteMessages(const QString& messages_json_data, const QString& stream_id, QString& next_page_token);
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ void FormEditOwnCloudAccount::performTest() {
|
|||
if (!SystemFactory::isVersionEqualOrNewer(result.version(), OWNCLOUD_MIN_VERSION)) {
|
||||
m_ui->m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
|
||||
tr(
|
||||
"Selected Nextcloud News server is running unsupported version (%1). At least version %2 is required.").arg(
|
||||
"Selected Nextcloud News server is running unsupported version %1. At least version %2 is required.").arg(
|
||||
result.version(),
|
||||
OWNCLOUD_MIN_VERSION),
|
||||
tr("Selected Nextcloud News server is running unsupported version."));
|
||||
|
|
|
@ -120,6 +120,8 @@ OwnCloudStatusResponse OwnCloudNetworkFactory::status() {
|
|||
headers);
|
||||
OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw));
|
||||
|
||||
qDebug().noquote().nospace() << "Raw status data is:" << result_raw;
|
||||
|
||||
if (network_reply.first != QNetworkReply::NoError) {
|
||||
qWarning("Nextcloud: Obtaining status info failed with error %d.", network_reply.first);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ StandardFeed::~StandardFeed() {
|
|||
qDebug("Destroying Feed instance.");
|
||||
}
|
||||
|
||||
QList<QAction*> StandardFeed::contextMenu() {
|
||||
QList<QAction*> StandardFeed::contextMenuFeedsList() {
|
||||
return serviceRoot()->getContextMenuForFeed(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class StandardFeed : public Feed {
|
|||
|
||||
StandardServiceRoot* serviceRoot() const;
|
||||
|
||||
QList<QAction*> contextMenu();
|
||||
QList<QAction*> contextMenuFeedsList();
|
||||
|
||||
QString additionalTooltip() const;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue