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-read.png</file>
|
||||||
<file>./graphics/Faenza/actions/64/mail-mark-unread.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-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-send.png</file>
|
||||||
<file>./graphics/Faenza/actions/64/mail-sent.png</file>
|
<file>./graphics/Faenza/actions/64/mail-sent.png</file>
|
||||||
<file>./graphics/Faenza/actions/64/media-playback-start.png</file>
|
<file>./graphics/Faenza/actions/64/media-playback-start.png</file>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>text/CHANGELOG</file>
|
<file>text/CHANGELOG</file>
|
||||||
<file>text/COPYING_BSD</file>
|
<file>text/COPYING_BSD</file>
|
||||||
|
<file>text/COPYING_MIT</file>
|
||||||
<file>text/COPYING_GNU_GPL</file>
|
<file>text/COPYING_GNU_GPL</file>
|
||||||
<file>text/COPYING_GNU_GPL_HTML</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;
|
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 {
|
QVariant MessagesModel::data(int row, int column, int role) const {
|
||||||
return data(index(row, column), role);
|
return data(index(row, column), role);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer {
|
||||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
|
|
||||||
// Returns message at given index.
|
// Returns message at given index.
|
||||||
|
|
||||||
|
QList<Message> messagesAt(QList<int> row_indices) const;
|
||||||
Message messageAt(int row_index) const;
|
Message messageAt(int row_index) const;
|
||||||
int messageId(int row_index) const;
|
int messageId(int row_index) const;
|
||||||
RootItem::Importance messageImportance(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."));
|
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.
|
// 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>"
|
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(
|
"<b>Qt:</b> %6 (compiled against %7)<br>").arg(
|
||||||
|
|
|
@ -160,7 +160,7 @@ p, li { white-space: pre-wrap; }
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>685</width>
|
<width>685</width>
|
||||||
<height>184</height>
|
<height>157</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="autoFillBackground">
|
<property name="autoFillBackground">
|
||||||
|
@ -235,8 +235,8 @@ p, li { white-space: pre-wrap; }
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>98</width>
|
<width>685</width>
|
||||||
<height>69</height>
|
<height>157</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="label">
|
<attribute name="label">
|
||||||
|
@ -288,6 +288,68 @@ p, li { white-space: pre-wrap; }
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
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;">
|
</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>
|
<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>
|
||||||
<property name="textInteractionFlags">
|
<property name="textInteractionFlags">
|
||||||
|
|
|
@ -312,7 +312,7 @@ void FormMain::updateRecycleBinMenu() {
|
||||||
no_action->setEnabled(false);
|
no_action->setEnabled(false);
|
||||||
root_menu->addAction(no_action);
|
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")),
|
QAction* no_action = new QAction(qApp->icons()->fromTheme(QSL("dialog-error")),
|
||||||
tr("No actions possible"),
|
tr("No actions possible"),
|
||||||
m_ui->m_menuRecycleBin);
|
m_ui->m_menuRecycleBin);
|
||||||
|
|
|
@ -430,7 +430,7 @@ QMenu* FeedsView::initializeContextMenuBin(RootItem* clicked_item) {
|
||||||
m_contextMenuBin->clear();
|
m_contextMenuBin->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
m_contextMenuBin->addActions(QList<QAction*>() <<
|
m_contextMenuBin->addActions(QList<QAction*>() <<
|
||||||
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
|
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
|
||||||
|
@ -453,7 +453,7 @@ QMenu* FeedsView::initializeContextMenuService(RootItem* clicked_item) {
|
||||||
m_contextMenuService->clear();
|
m_contextMenuService->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
m_contextMenuService->addActions(QList<QAction*>() <<
|
m_contextMenuService->addActions(QList<QAction*>() <<
|
||||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||||
|
@ -511,7 +511,7 @@ QMenu* FeedsView::initializeContextMenuCategories(RootItem* clicked_item) {
|
||||||
m_contextMenuCategories->clear();
|
m_contextMenuCategories->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
m_contextMenuCategories->addActions(QList<QAction*>() <<
|
m_contextMenuCategories->addActions(QList<QAction*>() <<
|
||||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||||
|
@ -538,7 +538,7 @@ QMenu* FeedsView::initializeContextMenuFeeds(RootItem* clicked_item) {
|
||||||
m_contextMenuFeeds->clear();
|
m_contextMenuFeeds->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
m_contextMenuFeeds->addActions(QList<QAction*>() <<
|
m_contextMenuFeeds->addActions(QList<QAction*>() <<
|
||||||
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
|
||||||
|
@ -565,7 +565,7 @@ QMenu* FeedsView::initializeContextMenuImportant(RootItem* clicked_item) {
|
||||||
m_contextMenuImportant->clear();
|
m_contextMenuImportant->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
m_contextMenuImportant->addActions(QList<QAction*>() <<
|
m_contextMenuImportant->addActions(QList<QAction*>() <<
|
||||||
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
|
qApp->mainForm()->m_ui->m_actionViewSelectedItemsNewspaperMode <<
|
||||||
|
@ -598,7 +598,7 @@ QMenu* FeedsView::initializeContextMenuOtherItem(RootItem* clicked_item) {
|
||||||
m_contextMenuOtherItems->clear();
|
m_contextMenuOtherItems->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> specific_actions = clicked_item->contextMenu();
|
QList<QAction*> specific_actions = clicked_item->contextMenuFeedsList();
|
||||||
|
|
||||||
if (!specific_actions.isEmpty()) {
|
if (!specific_actions.isEmpty()) {
|
||||||
m_contextMenuOtherItems->addSeparator();
|
m_contextMenuOtherItems->addSeparator();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "gui/messagesview.h"
|
#include "gui/messagesview.h"
|
||||||
|
|
||||||
|
#include "3rd-party/boolinq/boolinq.h"
|
||||||
#include "core/messagesmodel.h"
|
#include "core/messagesmodel.h"
|
||||||
#include "core/messagesproxymodel.h"
|
#include "core/messagesproxymodel.h"
|
||||||
#include "gui/dialogs/formmain.h"
|
#include "gui/dialogs/formmain.h"
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
#include "miscellaneous/settings.h"
|
#include "miscellaneous/settings.h"
|
||||||
#include "network-web/networkfactory.h"
|
#include "network-web/networkfactory.h"
|
||||||
#include "network-web/webfactory.h"
|
#include "network-web/webfactory.h"
|
||||||
|
#include "services/abstract/serviceroot.h"
|
||||||
|
|
||||||
#include <QFileIconProvider>
|
#include <QFileIconProvider>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
@ -213,12 +215,33 @@ void MessagesView::initializeContextMenu() {
|
||||||
|
|
||||||
m_contextMenu->addMenu(menu);
|
m_contextMenu->addMenu(menu);
|
||||||
m_contextMenu->addActions(
|
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 <<
|
QList<QAction*>()
|
||||||
qApp->mainForm()->m_ui->m_actionDeleteSelectedMessages);
|
<< 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);
|
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) {
|
void MessagesView::mousePressEvent(QMouseEvent* event) {
|
||||||
|
|
|
@ -19,14 +19,8 @@ class MessagesView : public QTreeView {
|
||||||
explicit MessagesView(QWidget* parent = nullptr);
|
explicit MessagesView(QWidget* parent = nullptr);
|
||||||
virtual ~MessagesView();
|
virtual ~MessagesView();
|
||||||
|
|
||||||
// Model accessors.
|
MessagesProxyModel* model() const;
|
||||||
inline MessagesProxyModel* model() const {
|
MessagesModel* sourceModel() const;
|
||||||
return m_proxyModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline MessagesModel* sourceModel() const {
|
|
||||||
return m_sourceModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reloadFontSettings();
|
void reloadFontSettings();
|
||||||
|
|
||||||
|
@ -110,4 +104,12 @@ class MessagesView : public QTreeView {
|
||||||
bool m_columnsAdjusted;
|
bool m_columnsAdjusted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline MessagesProxyModel* MessagesView::model() const {
|
||||||
|
return m_proxyModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MessagesModel* MessagesView::sourceModel() const {
|
||||||
|
return m_sourceModel;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // MESSAGESVIEW_H
|
#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()) {
|
if (m_contextMenu.isEmpty()) {
|
||||||
QAction* restore_action = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")),
|
QAction* restore_action = new QAction(qApp->icons()->fromTheme(QSL("view-refresh")),
|
||||||
tr("Restore recycle bin"),
|
tr("Restore recycle bin"),
|
||||||
|
|
|
@ -14,7 +14,7 @@ class RecycleBin : public RootItem {
|
||||||
|
|
||||||
QString additionalTooltip() const;
|
QString additionalTooltip() const;
|
||||||
|
|
||||||
QList<QAction*> contextMenu();
|
QList<QAction*> contextMenuFeedsList();
|
||||||
QList<Message> undeletedMessages() const;
|
QList<Message> undeletedMessages() const;
|
||||||
|
|
||||||
bool markAsReadUnread(ReadStatus status);
|
bool markAsReadUnread(ReadStatus status);
|
||||||
|
|
|
@ -44,7 +44,7 @@ QString RootItem::additionalTooltip() const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> RootItem::contextMenu() {
|
QList<QAction*> RootItem::contextMenuFeedsList() {
|
||||||
return QList<QAction*>();
|
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.
|
// Returns list of specific actions which can be done with the item.
|
||||||
// Do not include general actions here like actions: Mark as read, Update, ...
|
// 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.
|
// 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?
|
// Can properties of this item be edited?
|
||||||
virtual bool canBeEdited() const;
|
virtual bool canBeEdited() const;
|
||||||
|
|
|
@ -68,11 +68,11 @@ bool ServiceRoot::downloadAttachmentOnMyOwn(const QUrl& url) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> ServiceRoot::contextMenu() {
|
QList<QAction*> ServiceRoot::contextMenuFeedsList() {
|
||||||
return serviceMenu();
|
return serviceMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> ServiceRoot::contextMenuForMessages(const QList<Message*>& messages) {
|
QList<QAction*> ServiceRoot::contextMenuMessagesList(const QList<Message>& messages) {
|
||||||
Q_UNUSED(messages)
|
Q_UNUSED(messages)
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,9 +53,11 @@ class ServiceRoot : public RootItem {
|
||||||
// NOTE: Caller does NOT take ownership of created menu!
|
// NOTE: Caller does NOT take ownership of created menu!
|
||||||
virtual QList<QAction*> addItemMenu();
|
virtual QList<QAction*> addItemMenu();
|
||||||
|
|
||||||
// Returns actions to display as context menu.
|
// NOTE: Caller does NOT take ownership of created menu!
|
||||||
virtual QList<QAction*> contextMenu();
|
virtual QList<QAction*> contextMenuFeedsList();
|
||||||
virtual QList<QAction*> contextMenuForMessages(const QList<Message*>& messages);
|
|
||||||
|
// 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
|
// Returns list of specific actions to be shown in main window menu
|
||||||
// bar in sections "Services -> 'this service'".
|
// bar in sections "Services -> 'this service'".
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
|
|
||||||
#include <QFileDialog>
|
#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) {
|
if (network == nullptr) {
|
||||||
m_network = new GmailNetworkFactory(this);
|
m_network = new GmailNetworkFactory(this);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +37,10 @@ void GmailServiceRoot::updateTitle() {
|
||||||
setTitle(m_network->username() + QSL(" (Gmail)"));
|
setTitle(m_network->username() + QSL(" (Gmail)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GmailServiceRoot::replyToEmail() {
|
||||||
|
FormAddEditEmail(this, qApp->mainFormWidget()).execForReply(&m_replyToMessage);
|
||||||
|
}
|
||||||
|
|
||||||
RootItem* GmailServiceRoot::obtainNewTreeForSyncIn() const {
|
RootItem* GmailServiceRoot::obtainNewTreeForSyncIn() const {
|
||||||
auto* root = new RootItem();
|
auto* root = new RootItem();
|
||||||
GmailFeed* inbox = new GmailFeed(tr("Inbox"), QSL(GMAIL_SYSTEM_LABEL_INBOX), qApp->icons()->fromTheme(QSL("mail-inbox")), root);
|
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() {
|
QList<QAction*> GmailServiceRoot::serviceMenu() {
|
||||||
if (m_serviceMenu.isEmpty()) {
|
if (m_serviceMenu.isEmpty()) {
|
||||||
ServiceRoot::serviceMenu();
|
ServiceRoot::serviceMenu();
|
||||||
|
|
|
@ -22,6 +22,7 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
||||||
void setNetwork(GmailNetworkFactory* network);
|
void setNetwork(GmailNetworkFactory* network);
|
||||||
GmailNetworkFactory* network() const;
|
GmailNetworkFactory* network() const;
|
||||||
|
|
||||||
|
QList<QAction*> contextMenuMessagesList(const QList<Message>& messages);
|
||||||
QList<QAction*> serviceMenu();
|
QList<QAction*> serviceMenu();
|
||||||
bool isSyncable() const;
|
bool isSyncable() const;
|
||||||
bool canBeEdited() const;
|
bool canBeEdited() const;
|
||||||
|
@ -41,6 +42,9 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
||||||
public slots:
|
public slots:
|
||||||
void updateTitle();
|
void updateTitle();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void replyToEmail();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
RootItem* obtainNewTreeForSyncIn() const;
|
RootItem* obtainNewTreeForSyncIn() const;
|
||||||
|
|
||||||
|
@ -50,6 +54,8 @@ class GmailServiceRoot : public ServiceRoot, public CacheForServiceRoot {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GmailNetworkFactory* m_network;
|
GmailNetworkFactory* m_network;
|
||||||
|
QAction* m_actionReply;
|
||||||
|
Message m_replyToMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void GmailServiceRoot::setNetwork(GmailNetworkFactory* network) {
|
inline void GmailServiceRoot::setNetwork(GmailNetworkFactory* network) {
|
||||||
|
|
|
@ -112,15 +112,8 @@ void FormAddEditEmail::onOkClicked() {
|
||||||
msg.set_plain(m_ui.m_txtMessage->toPlainText().toStdString());
|
msg.set_plain(m_ui.m_txtMessage->toPlainText().toStdString());
|
||||||
msg.set_header("Content-Type", "text/plain; charset=utf-8");
|
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 {
|
try {
|
||||||
m_root->network()->sendEmail(msg);
|
m_root->network()->sendEmail(msg, m_originalMessage);
|
||||||
accept();
|
accept();
|
||||||
}
|
}
|
||||||
catch (const ApplicationException& ex) {
|
catch (const ApplicationException& ex) {
|
||||||
|
@ -141,7 +134,9 @@ void FormAddEditEmail::addRecipientRow(const QString& recipient) {
|
||||||
|
|
||||||
mail_rec->setPossibleRecipients(rec);
|
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);
|
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;
|
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();
|
QString bearer = m_oauth2->bearer().toLocal8Bit();
|
||||||
|
|
||||||
if (bearer.isEmpty()) {
|
if (bearer.isEmpty()) {
|
||||||
throw ApplicationException(tr("you aren't logged in"));
|
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());
|
QString rfc_email = QString::fromStdString(msg.to_string());
|
||||||
QByteArray input_data = rfc_email.toUtf8();
|
QByteArray input_data = rfc_email.toUtf8();
|
||||||
QList<QPair<QByteArray, QByteArray>> headers;
|
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,
|
bool GmailNetworkFactory::obtainAndDecodeFullMessages(const QList<Message>& lite_messages,
|
||||||
const QString& feed_id,
|
const QString& feed_id,
|
||||||
QList<Message>& full_messages) {
|
QList<Message>& full_messages) {
|
||||||
|
|
|
@ -36,7 +36,7 @@ class GmailNetworkFactory : public QObject {
|
||||||
void setBatchSize(int batch_size);
|
void setBatchSize(int batch_size);
|
||||||
|
|
||||||
// Sends e-mail, returns its ID.
|
// 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.
|
// Returns all possible recipients.
|
||||||
QStringList getAllRecipients();
|
QStringList getAllRecipients();
|
||||||
|
@ -53,6 +53,7 @@ class GmailNetworkFactory : public QObject {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool fillFullMessage(Message& msg, const QJsonObject& json, const QString& feed_id);
|
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);
|
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);
|
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)) {
|
if (!SystemFactory::isVersionEqualOrNewer(result.version(), OWNCLOUD_MIN_VERSION)) {
|
||||||
m_ui->m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
|
m_ui->m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
|
||||||
tr(
|
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(),
|
result.version(),
|
||||||
OWNCLOUD_MIN_VERSION),
|
OWNCLOUD_MIN_VERSION),
|
||||||
tr("Selected Nextcloud News server is running unsupported version."));
|
tr("Selected Nextcloud News server is running unsupported version."));
|
||||||
|
|
|
@ -120,6 +120,8 @@ OwnCloudStatusResponse OwnCloudNetworkFactory::status() {
|
||||||
headers);
|
headers);
|
||||||
OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw));
|
OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw));
|
||||||
|
|
||||||
|
qDebug().noquote().nospace() << "Raw status data is:" << result_raw;
|
||||||
|
|
||||||
if (network_reply.first != QNetworkReply::NoError) {
|
if (network_reply.first != QNetworkReply::NoError) {
|
||||||
qWarning("Nextcloud: Obtaining status info failed with error %d.", network_reply.first);
|
qWarning("Nextcloud: Obtaining status info failed with error %d.", network_reply.first);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ StandardFeed::~StandardFeed() {
|
||||||
qDebug("Destroying Feed instance.");
|
qDebug("Destroying Feed instance.");
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QAction*> StandardFeed::contextMenu() {
|
QList<QAction*> StandardFeed::contextMenuFeedsList() {
|
||||||
return serviceRoot()->getContextMenuForFeed(this);
|
return serviceRoot()->getContextMenuForFeed(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ class StandardFeed : public Feed {
|
||||||
|
|
||||||
StandardServiceRoot* serviceRoot() const;
|
StandardServiceRoot* serviceRoot() const;
|
||||||
|
|
||||||
QList<QAction*> contextMenu();
|
QList<QAction*> contextMenuFeedsList();
|
||||||
|
|
||||||
QString additionalTooltip() const;
|
QString additionalTooltip() const;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue