diff --git a/configuration/akregator_config_plugins.cpp b/configuration/akregator_config_plugins.cpp index 850fe0da..9ece6db1 100644 --- a/configuration/akregator_config_plugins.cpp +++ b/configuration/akregator_config_plugins.cpp @@ -1,75 +1,74 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akregator_config_plugins.h" #include "akregatorconfig.h" #include "akregatorconfigurepluginlistwidget.h" #include #include #include #include #include -#include - +#include using namespace Akregator; K_PLUGIN_FACTORY(KCMAkregatorPluginsConfigFactory, registerPlugin(); ) KCMAkregatorPluginsConfig::KCMAkregatorPluginsConfig(QWidget *parent, const QVariantList &args) : KCModule(parent, args) { QHBoxLayout *lay = new QHBoxLayout(this); lay->setMargin(0); KAboutData *about = new KAboutData(QStringLiteral("kcmakrpluginsconfig"), i18n("Configure Plugins"), QString(), QString(), KAboutLicense::GPL, i18n("(c), 2016-2017 Laurent Montel")); about->addAuthor(i18n("Laurent Montel"), QString(), QStringLiteral("montel@kde.org")); setAboutData(about); mConfigurePluginWidget = new PimCommon::ConfigurePluginsWidget(new AkregatorConfigurePluginListWidget(this), this); mConfigurePluginWidget->setObjectName(QStringLiteral("configurePluginWidget")); connect(mConfigurePluginWidget, &PimCommon::ConfigurePluginsWidget::changed, this, &KCMAkregatorPluginsConfig::slotConfigChanged); lay->addWidget(mConfigurePluginWidget); } void KCMAkregatorPluginsConfig::slotConfigChanged() { Q_EMIT changed(true); } void KCMAkregatorPluginsConfig::save() { mConfigurePluginWidget->save(); } void KCMAkregatorPluginsConfig::load() { mConfigurePluginWidget->doLoadFromGlobalSettings(); } void KCMAkregatorPluginsConfig::defaults() { mConfigurePluginWidget->doResetToDefaultsOther(); } #include "akregator_config_plugins.moc" diff --git a/plugins/mk4storage/metakit/src/custom.cpp b/plugins/mk4storage/metakit/src/custom.cpp index fa13263e..c4a28d2d 100644 --- a/plugins/mk4storage/metakit/src/custom.cpp +++ b/plugins/mk4storage/metakit/src/custom.cpp @@ -1,1034 +1,1033 @@ // custom.cpp -- // This is part of Metakit, see http://www.equi4.com/metakit.html /** @file * Implementation of many custom viewer classes */ #include "header.h" #include "custom.h" #include "format.h" -#include ///////////////////////////////////////////////////////////////////////////// class c4_CustomHandler: public c4_Handler { c4_CustomSeq *_seq; public: c4_CustomHandler(const c4_Property &prop_, c4_CustomSeq *seq_); virtual ~c4_CustomHandler(); int ItemSize(int index_) override; const void *Get(int index_, int &length_) override; void Set(int index_, const c4_Bytes &buf_) override; void Insert(int index_, const c4_Bytes &buf_, int count_) override; void Remove(int index_, int count_) override; }; ///////////////////////////////////////////////////////////////////////////// c4_CustomHandler::c4_CustomHandler(const c4_Property &prop_, c4_CustomSeq *seq_) : c4_Handler(prop_), _seq(seq_) { d4_assert(_seq != 0); } c4_CustomHandler::~c4_CustomHandler() {} int c4_CustomHandler::ItemSize(int index_) { c4_Bytes &buf = _seq->Buffer(); int colnum = _seq->PropIndex(Property().GetId()); d4_assert(colnum >= 0); if (!_seq->DoGet(index_, colnum, buf)) { return 0; } return buf.Size(); } const void *c4_CustomHandler::Get(int index_, int &length_) { c4_Bytes &buf = _seq->Buffer(); int colnum = _seq->PropIndex(Property().GetId()); d4_assert(colnum >= 0); if (!_seq->DoGet(index_, colnum, buf)) { ClearBytes(buf); } length_ = buf.Size(); return buf.Contents(); } void c4_CustomHandler::Set(int index_, const c4_Bytes &buf_) { int colnum = _seq->PropIndex(Property().GetId()); d4_assert(colnum >= 0); _seq->DoSet(index_, colnum, buf_); } void c4_CustomHandler::Insert(int, const c4_Bytes &, int) { d4_assert(0); //! not yet } void c4_CustomHandler::Remove(int, int) { d4_assert(0); //! not yet } c4_Handler *c4_CustomSeq::CreateHandler(const c4_Property &prop_) { return d4_new c4_CustomHandler(prop_, this); } ///////////////////////////////////////////////////////////////////////////// c4_CustomSeq::c4_CustomSeq(c4_CustomViewer *viewer_): c4_HandlerSeq(0), _viewer (viewer_), _inited(false) { d4_assert(_viewer != 0); // set up handlers to match a template obtained from the viewer c4_View v = viewer_->GetTemplate(); for (int i = 0; i < v.NumProperties(); ++i) { PropIndex(v.NthProperty(i)); } _inited = true; } c4_CustomSeq::~c4_CustomSeq() { delete _viewer; } int c4_CustomSeq::NumRows()const { return _inited ? _viewer->GetSize() : 0; } bool c4_CustomSeq::RestrictSearch(c4_Cursor cursor_, int &pos_, int &count_) { if (count_ > 0) { int n; int o = _viewer->Lookup(cursor_, n); // a -1 result means: "don't know, please scan all" if (o < 0) { return count_ > 0; } if (n > 0) { if (pos_ < o) { count_ -= o - pos_; pos_ = o; } if (pos_ + count_ > o + n) { count_ = o + n - pos_; } if (count_ > 0) { return true; } } } count_ = 0; return false; } void c4_CustomSeq::InsertAt(int p_, c4_Cursor c_, int n_) { _viewer->InsertRows(p_, c_, n_); } void c4_CustomSeq::RemoveAt(int p_, int n_) { _viewer->RemoveRows(p_, n_); } void c4_CustomSeq::Move(int, int) { d4_assert(false); //! not yet } bool c4_CustomSeq::DoGet(int row_, int col_, c4_Bytes &buf_)const { d4_assert(_inited); return _viewer->GetItem(row_, col_, buf_); } void c4_CustomSeq::DoSet(int row_, int col_, const c4_Bytes &buf_) { d4_assert(_inited); d4_dbgdef(const bool f =)_viewer->SetItem(row_, col_, buf_); d4_assert(f); } ///////////////////////////////////////////////////////////////////////////// /** @class c4_CustomViewer * * Abstract base class for definition of custom views. * * A custom view is a view which can be accessed like any other view, using * row and property operations, but which is fully managed by a customized * "viewer" class. The viewer will eventually handle all requests for the * view, such as defining its structure and size, as well as providing the * actual data values when requested. * * Custom views cannot propagate changes. * * To implement a custom view, you must derive your viewer from this base * class and define each of the virtual members. Then create a new object * of this type on the heap and pass it to the c4_View constructor. Your * viewer will automatically be destroyed when the last reference to its * view goes away. See the DBF2MK sample code for an example of a viewer. */ c4_CustomViewer::~c4_CustomViewer() {} /// Locate a row in this view, try to use native searches int c4_CustomViewer::Lookup(c4_Cursor, int &count_) { count_ = GetSize(); return 0; // not implemented, return entire view range } /// Store one data item, supplied as a generic data value bool c4_CustomViewer::SetItem(int, int, const c4_Bytes &) { return false; // default is not modifiable } /// Insert one or more copies of a row (if possible) bool c4_CustomViewer::InsertRows(int, c4_Cursor, int) { return false; // default is not modifiable } /// Remove one or more rows (this is not always possible) bool c4_CustomViewer::RemoveRows(int, int) { return false; // default is not modifiable } ///////////////////////////////////////////////////////////////////////////// class c4_SliceViewer: public c4_CustomViewer { c4_View _parent; int _first, _limit, _step; public: c4_SliceViewer(c4_Sequence &seq_, int first_, int limit_, int step_); virtual ~c4_SliceViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; bool SetItem(int row_, int col_, const c4_Bytes &buf_) override; bool InsertRows(int pos_, c4_Cursor value_, int count_ = 1) override; bool RemoveRows(int pos_, int count_ = 1) override; }; c4_SliceViewer::c4_SliceViewer(c4_Sequence &seq_, int first_, int limit_, int step_): _parent(&seq_), _first(first_), _limit(limit_), _step(step_) { d4_assert(_step != 0); } c4_SliceViewer::~c4_SliceViewer() {} c4_View c4_SliceViewer::GetTemplate() { return _parent.Clone(); // could probably return _parent just as well } int c4_SliceViewer::GetSize() { int n = _limit >= 0 ? _limit : _parent.GetSize(); if (n < _first) { n = _first; } int k = _step < 0 ? - _step : _step; return (n - _first + k - 1) / k; } bool c4_SliceViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1); return _parent.GetItem(row_, col_, buf_); } bool c4_SliceViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) { row_ = _first + _step * (_step > 0 ? row_ : row_ - GetSize() + 1); _parent.SetItem(row_, col_, buf_); return true; } bool c4_SliceViewer::InsertRows(int pos_, c4_Cursor value_, int count_) { if (_step != 1) { return false; } pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1); if (_limit >= 0) { _limit += count_; } _parent.InsertAt(pos_, *value_, count_); return true; } bool c4_SliceViewer::RemoveRows(int pos_, int count_) { if (_step != 1) { return false; } pos_ = _first + _step * (_step > 0 ? pos_ : pos_ - GetSize() + 1); if (_limit >= 0) { _limit -= count_; } _parent.RemoveAt(pos_, count_); return true; } c4_CustomViewer *f4_CustSlice(c4_Sequence &seq_, int first_, int limit_, int step_) { return d4_new c4_SliceViewer(seq_, first_, limit_, step_); } ///////////////////////////////////////////////////////////////////////////// class c4_ProductViewer: public c4_CustomViewer { c4_View _parent, _argView, _template; public: c4_ProductViewer(c4_Sequence &seq_, const c4_View &view_); virtual ~c4_ProductViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; }; c4_ProductViewer::c4_ProductViewer(c4_Sequence &seq_, const c4_View &view_): _parent(&seq_), _argView(view_), _template(_parent.Clone()) { for (int i = 0; i < _argView.NumProperties(); ++i) { _template.AddProperty(_argView.NthProperty(i)); } } c4_ProductViewer::~c4_ProductViewer() {} c4_View c4_ProductViewer::GetTemplate() { return _template; } int c4_ProductViewer::GetSize() { return _parent.GetSize() * _argView.GetSize(); } bool c4_ProductViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { c4_View v = _parent; if (col_ < v.NumProperties()) { row_ /= _argView.GetSize(); } else { v = _argView; row_ %= _argView.GetSize(); col_ = v.FindProperty(_template.NthProperty(col_).GetId()); d4_assert(col_ >= 0); } return v.GetItem(row_, col_, buf_); } c4_CustomViewer *f4_CustProduct(c4_Sequence &seq_, const c4_View &view_) { return d4_new c4_ProductViewer(seq_, view_); } ///////////////////////////////////////////////////////////////////////////// class c4_RemapWithViewer: public c4_CustomViewer { c4_View _parent, _argView; public: c4_RemapWithViewer(c4_Sequence &seq_, const c4_View &view_); virtual ~c4_RemapWithViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; bool SetItem(int row_, int col_, const c4_Bytes &buf_) override; }; c4_RemapWithViewer::c4_RemapWithViewer(c4_Sequence &seq_, const c4_View &view_) : _parent(&seq_), _argView(view_) {} c4_RemapWithViewer::~c4_RemapWithViewer() {} c4_View c4_RemapWithViewer::GetTemplate() { return _parent.Clone(); // could probably return _parent just as well } int c4_RemapWithViewer::GetSize() { return _argView.GetSize(); } bool c4_RemapWithViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { const c4_Property &map = _argView.NthProperty(0); d4_assert(map.Type() == 'I'); row_ = ((const c4_IntProp &)map)(_argView[row_]); return _parent.GetItem(row_, col_, buf_); } bool c4_RemapWithViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) { const c4_Property &map = _argView.NthProperty(0); d4_assert(map.Type() == 'I'); row_ = ((const c4_IntProp &)map)(_argView[row_]); _parent.SetItem(row_, col_, buf_); return true; } c4_CustomViewer *f4_CustRemapWith(c4_Sequence &seq_, const c4_View &view_) { return d4_new c4_RemapWithViewer(seq_, view_); } ///////////////////////////////////////////////////////////////////////////// class c4_PairViewer: public c4_CustomViewer { c4_View _parent, _argView, _template; public: c4_PairViewer(c4_Sequence &seq_, const c4_View &view_); virtual ~c4_PairViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; bool SetItem(int row_, int col_, const c4_Bytes &buf_) override; bool InsertRows(int pos_, c4_Cursor value_, int count_ = 1) override; bool RemoveRows(int pos_, int count_ = 1) override; }; c4_PairViewer::c4_PairViewer(c4_Sequence &seq_, const c4_View &view_): _parent (&seq_), _argView(view_), _template(_parent.Clone()) { for (int i = 0; i < _argView.NumProperties(); ++i) { _template.AddProperty(_argView.NthProperty(i)); } } c4_PairViewer::~c4_PairViewer() {} c4_View c4_PairViewer::GetTemplate() { return _template; } int c4_PairViewer::GetSize() { return _parent.GetSize(); } bool c4_PairViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { c4_View v = _parent; if (col_ >= v.NumProperties()) { v = _argView; col_ = v.FindProperty(_template.NthProperty(col_).GetId()); d4_assert(col_ >= 0); } return v.GetItem(row_, col_, buf_); } bool c4_PairViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) { c4_View v = _parent; if (col_ >= v.NumProperties()) { v = _argView; col_ = v.FindProperty(_template.NthProperty(col_).GetId()); d4_assert(col_ >= 0); } v.SetItem(row_, col_, buf_); return true; } bool c4_PairViewer::InsertRows(int pos_, c4_Cursor value_, int count_) { _parent.InsertAt(pos_, *value_, count_); _argView.InsertAt(pos_, *value_, count_); return true; } bool c4_PairViewer::RemoveRows(int pos_, int count_) { _parent.RemoveAt(pos_, count_); _argView.RemoveAt(pos_, count_); return true; } c4_CustomViewer *f4_CustPair(c4_Sequence &seq_, const c4_View &view_) { return d4_new c4_PairViewer(seq_, view_); } ///////////////////////////////////////////////////////////////////////////// class c4_ConcatViewer: public c4_CustomViewer { c4_View _parent, _argView; public: c4_ConcatViewer(c4_Sequence &seq_, const c4_View &view_); virtual ~c4_ConcatViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; bool SetItem(int row_, int col_, const c4_Bytes &buf_) override; }; c4_ConcatViewer::c4_ConcatViewer(c4_Sequence &seq_, const c4_View &view_): _parent(&seq_), _argView(view_) {} c4_ConcatViewer::~c4_ConcatViewer() {} c4_View c4_ConcatViewer::GetTemplate() { return _parent.Clone(); // could probably return _parent just as well } int c4_ConcatViewer::GetSize() { return _parent.GetSize() + _argView.GetSize(); } bool c4_ConcatViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { c4_View v = _parent; if (row_ >= _parent.GetSize()) { v = _argView; row_ -= _parent.GetSize(); col_ = v.FindProperty(_parent.NthProperty(col_).GetId()); if (col_ < 0) { return false; } } return v.GetItem(row_, col_, buf_); } bool c4_ConcatViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) { c4_View v = _parent; if (row_ >= _parent.GetSize()) { v = _argView; row_ -= _parent.GetSize(); col_ = v.FindProperty(_parent.NthProperty(col_).GetId()); d4_assert(col_ >= 0); } v.SetItem(row_, col_, buf_); return true; } c4_CustomViewer *f4_CustConcat(c4_Sequence &seq_, const c4_View &view_) { return d4_new c4_ConcatViewer(seq_, view_); } ///////////////////////////////////////////////////////////////////////////// class c4_RenameViewer: public c4_CustomViewer { c4_View _parent, _template; public: c4_RenameViewer(c4_Sequence &seq_, const c4_Property &old_, const c4_Property &new_); virtual ~c4_RenameViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; bool SetItem(int row_, int col_, const c4_Bytes &buf_) override; //virtual bool InsertRows(int pos_, c4_Cursor value_, int count_=1); //virtual bool RemoveRows(int pos_, int count_=1); }; c4_RenameViewer::c4_RenameViewer(c4_Sequence &seq_, const c4_Property &old_, const c4_Property &new_): _parent(&seq_) { for (int i = 0; i < _parent.NumProperties(); ++i) { const c4_Property &prop = _parent.NthProperty(i); _template.AddProperty(prop.GetId() == old_.GetId() ? new_ : prop); } } c4_RenameViewer::~c4_RenameViewer() {} c4_View c4_RenameViewer::GetTemplate() { return _template; } int c4_RenameViewer::GetSize() { return _parent.GetSize(); } bool c4_RenameViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { return _parent.GetItem(row_, col_, buf_); } bool c4_RenameViewer::SetItem(int row_, int col_, const c4_Bytes &buf_) { _parent.SetItem(row_, col_, buf_); return true; } c4_CustomViewer *f4_CustRename(c4_Sequence &seq_, const c4_Property &old_, const c4_Property &new_) { return d4_new c4_RenameViewer(seq_, old_, new_); } ///////////////////////////////////////////////////////////////////////////// class c4_GroupByViewer: public c4_CustomViewer { c4_View _parent, _keys, _sorted, _temp; c4_Property _result; c4_DWordArray _map; int ScanTransitions(int lo_, int hi_, t4_byte *flags_, const c4_View &match_)const; public: c4_GroupByViewer(c4_Sequence &seq_, const c4_View &keys_, const c4_Property &result_); virtual ~c4_GroupByViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; }; c4_GroupByViewer::c4_GroupByViewer(c4_Sequence &seq_, const c4_View &keys_, const c4_Property &result_): _parent(&seq_), _keys(keys_), _result(result_) { _sorted = _parent.SortOn(_keys); int n = _sorted.GetSize(); c4_Bytes temp; t4_byte *buf = temp.SetBufferClear(n); int groups = 0; if (n > 0) { ++buf[0]; // the first entry is always a transition groups = 1 + ScanTransitions(1, n, buf, _sorted.Project(_keys)); } // set up a map pointing to each transition _map.SetSize(groups + 1); int j = 0; for (int i = 0; i < n; ++i) if (buf[i]) { _map.SetAt(j++, i); } // also append an entry to point just past the end _map.SetAt(j, n); d4_assert(_map.GetAt(0) == 0); d4_assert(j == groups); } c4_GroupByViewer::~c4_GroupByViewer() {} int c4_GroupByViewer::ScanTransitions(int lo_, int hi_, t4_byte *flags_, const c4_View &match_)const { d4_assert(lo_ > 0); int m = hi_ - lo_; d4_assert(m >= 0); // done if nothing left or if entire range is identical if (m == 0 || match_[lo_ - 1] == match_[hi_ - 1]) { return 0; } // range has a transition, done if it is exactly of size one if (m == 1) { ++(flags_[lo_]); return 1; } // use binary splitting if the range has enough entries if (m >= 5) return ScanTransitions(lo_, lo_ + m / 2, flags_, match_) + ScanTransitions (lo_ + m / 2, hi_, flags_, match_); // else use a normal linear scan int n = 0; for (int i = lo_; i < hi_; ++i) if (match_[i] != match_[i - 1]) { ++(flags_[i]); ++n; } return n; } c4_View c4_GroupByViewer::GetTemplate() { c4_View v = _keys.Clone(); v.AddProperty(_result); return v; } int c4_GroupByViewer::GetSize() { d4_assert(_map.GetSize() > 0); return _map.GetSize() - 1; } bool c4_GroupByViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { if (col_ < _keys.NumProperties()) { return _sorted.GetItem(_map.GetAt(row_), col_, buf_); } d4_assert(col_ == _keys.NumProperties()); t4_i32 count; switch (_result.Type()) { case 'I': count = _map.GetAt(row_ + 1) - _map.GetAt(row_); buf_ = c4_Bytes(&count, sizeof count, true); break; case 'V': _temp = _sorted.Slice(_map.GetAt(row_), _map.GetAt(row_ + 1)) .ProjectWithout(_keys); buf_ = c4_Bytes(&_temp, sizeof _temp, true); break; default: d4_assert(0); } return true; } c4_CustomViewer *f4_CustGroupBy(c4_Sequence &seq_, const c4_View &template_, const c4_Property &result_) { return d4_new c4_GroupByViewer(seq_, template_, result_); } ///////////////////////////////////////////////////////////////////////////// class c4_JoinPropViewer: public c4_CustomViewer { c4_View _parent, _template; c4_ViewProp _sub; int _subPos, _subWidth; c4_DWordArray _base, _offset; public: c4_JoinPropViewer(c4_Sequence &seq_, const c4_ViewProp &sub_, bool outer_); virtual ~c4_JoinPropViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; }; c4_JoinPropViewer::c4_JoinPropViewer(c4_Sequence &seq_, const c4_ViewProp &sub_, bool outer_): _parent(&seq_), _sub(sub_), _subPos(_parent.FindProperty (sub_.GetId())), _subWidth(0) { d4_assert(_subPos >= 0); for (int k = 0; k < _parent.NumProperties(); ++k) { if (k != _subPos) { _template.AddProperty(_parent.NthProperty(k)); } else // if there are no rows, then this join does very little anyway //! OOPS: if this is an unattached view, then the subviews can differ if (_parent.GetSize() > 0) { c4_View view = sub_(_parent[0]); for (int l = 0; l < view.NumProperties(); ++l) { _template.AddProperty(view.NthProperty(l)); ++_subWidth; } } } _base.SetSize(0, 5); _offset.SetSize(0, 5); for (int i = 0; i < _parent.GetSize(); ++i) { c4_View v = _sub(_parent[i]); int n = v.GetSize(); if (n == 0 && outer_) { _base.Add(i); _offset.Add(~(t4_i32)0); // special null entry for outer joins } else for (int j = 0; j < n; ++j) { _base.Add(i); _offset.Add(j); } } } c4_JoinPropViewer::~c4_JoinPropViewer() {} c4_View c4_JoinPropViewer::GetTemplate() { return _template; } int c4_JoinPropViewer::GetSize() { return _base.GetSize(); } bool c4_JoinPropViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { c4_View v = _parent; int r = _base.GetAt(row_); if (col_ >= _subPos) { if (col_ >= _subPos + _subWidth) { col_ -= _subWidth - 1; } else { v = _sub(_parent[r]); r = _offset.GetAt(row_); if (r < 0) { return false; } // if this is a null row in an outer join col_ = v.FindProperty(_template.NthProperty(col_).GetId()); if (col_ < 0) { return false; } // if subview doesn't have all properties } } return v.GetItem(r, col_, buf_); } c4_CustomViewer *f4_CustJoinProp(c4_Sequence &seq_, const c4_ViewProp &sub_, bool outer_) { return d4_new c4_JoinPropViewer(seq_, sub_, outer_); } ///////////////////////////////////////////////////////////////////////////// class c4_JoinViewer: public c4_CustomViewer { c4_View _parent, _argView, _template; c4_DWordArray _base, _offset; public: c4_JoinViewer(c4_Sequence &seq_, const c4_View &keys_, const c4_View &view_, bool outer_); virtual ~c4_JoinViewer(); c4_View GetTemplate() override; int GetSize() override; bool GetItem(int row_, int col_, c4_Bytes &buf_) override; }; c4_JoinViewer::c4_JoinViewer(c4_Sequence &seq_, const c4_View &keys_, const c4_View &view_, bool outer_): _parent(&seq_), _argView(view_.SortOn(keys_)) { // why not in GetTemplate, since we don't need to know this... _template = _parent.Clone(); for (int l = 0; l < _argView.NumProperties(); ++l) { _template.AddProperty(_argView.NthProperty(l)); } c4_View sorted = _parent.SortOn(keys_).Project(keys_); c4_View temp = _argView.Project(keys_); _base.SetSize(0, 5); _offset.SetSize(0, 5); int j = 0, n = 0; for (int i = 0; i < sorted.GetSize(); ++i) { int orig = _parent.GetIndexOf(sorted[i]); d4_assert(orig >= 0); if (i > 0 && sorted[i] == sorted[i - 1]) { // if last key was same, repeat the same join int last = _offset.GetSize() - n; for (int k = 0; k < n; ++k) { _base.Add(orig); _offset.Add(_offset.GetAt(last + k)); } } else { // no, this is a new combination bool match = false; // advance until the temp view entry is >= this sorted entry while (j < temp.GetSize()) if (sorted[i] <= temp[j]) { match = sorted[i] == temp[j]; break; } else { ++j; } n = 0; if (match) { do { _base.Add(orig); _offset.Add(j); ++n; } while (++j < temp.GetSize() && temp[j] == temp[j - 1]); } else if (outer_) { // no match, add an entry anyway if this is an outer join _base.Add(orig); _offset.Add(~(t4_i32)0); // special null entry ++n; } } } } c4_JoinViewer::~c4_JoinViewer() {} c4_View c4_JoinViewer::GetTemplate() { return _template; } int c4_JoinViewer::GetSize() { return _base.GetSize(); } bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { c4_View v = _parent; int r = _base.GetAt(row_); if (col_ >= v.NumProperties()) { v = _argView; r = _offset.GetAt(row_); if (r < 0) { return false; } // if this is a null row in an outer join col_ = v.FindProperty(_template.NthProperty(col_).GetId()); if (col_ < 0) { return false; } // if second view doesn't have all properties } return v.GetItem(r, col_, buf_); } #if 0 bool c4_JoinViewer::GetItem(int row_, int col_, c4_Bytes &buf_) { c4_View v = _parent; int o = 0; int r = _offset.GetAt(row_); if (r < 0) { o = ~r; if (o == 0) { return false; } // if this is a null row in an outer join r -= o; } if (col_ >= v.NumProperties()) { v = _argView; r = _o; col_ = v.FindProperty(_template.NthProperty(col_)); if (col_ < 0) { return false; } // if second view doesn't have all properties } return v.GetItem(r, col_, buf_); } #endif c4_CustomViewer *f4_CustJoin(c4_Sequence &seq_, const c4_View &keys_, const c4_View &view_, bool outer_) { return d4_new c4_JoinViewer(seq_, keys_, view_, outer_); } ///////////////////////////////////////////////////////////////////////////// diff --git a/plugins/mk4storage/metakit/src/handler.h b/plugins/mk4storage/metakit/src/handler.h index 63ffaa35..85ae09b3 100644 --- a/plugins/mk4storage/metakit/src/handler.h +++ b/plugins/mk4storage/metakit/src/handler.h @@ -1,149 +1,148 @@ // handler.h -- // This is part of Metakit, the homepage is http://www.equi4.com/metakit.html /** @file * Definition of the main handler classes */ #ifndef __HANDLER_H__ #define __HANDLER_H__ -#include ///////////////////////////////////////////////////////////////////////////// // Declarations in this file class c4_Handler; // data representation handler // class c4_Sequence; class c4_HandlerSeq; // a sequence built from handlers class c4_Column; // not defined here class c4_Field; // not defined here class c4_Persist; // not defined here class c4_SaveContext; // not defined here ///////////////////////////////////////////////////////////////////////////// class c4_Handler { c4_Property _property; public: c4_Handler(const c4_Property &_prop); //: Constructor (this is an abstract base class). virtual ~c4_Handler(); virtual void Define(int, const t4_byte **); //: Called when the corresponding table has been fully defined. virtual void FlipBytes(); //: Called to reverse the internal byte order of foreign data. virtual void Commit(c4_SaveContext &ar_); //: Commit the associated column(s) to file. virtual void OldDefine(char, c4_Persist &); const c4_Property &Property()const; //: Returns the property associated with this handler. int PropId()const; //: Returns the id of the property associated with this handler. void ClearBytes(c4_Bytes &buf_)const; //: Returns the default value for items of this type. virtual int ItemSize(int index_) = 0; //: Return width of specified data item. void GetBytes(int index_, c4_Bytes &buf_, bool copySmall_ = false); //: Used for backward compatibility, should probably be replaced. virtual const void *Get(int index_, int &length_) = 0; //: Retrieves the data item at the specified index. virtual void Set(int index_, const c4_Bytes &buf_) = 0; //: Stores a new data item at the specified index. int Compare(int index_, const c4_Bytes &buf_); //: Compares an entry with a specified data item. virtual void Insert(int index_, const c4_Bytes &buf_, int count_) = 0; //: Inserts 1 or more data items at the specified index. virtual void Remove(int index_, int count_) = 0; //: Removes 1 or more data items at the specified index. void Move(int from_, int to_); //: Move a data item to another position. virtual c4_Column *GetNthMemoCol(int index_, bool alloc_ = false); //: Special access to underlying data of memo entries virtual bool IsPersistent()const; //: True if this handler might do I/O to satisfy fetches virtual void Unmapped(); //: Make sure this handler stops using file mappings virtual bool HasSubview(int index_); //: True if this subview has materialized into an object }; ///////////////////////////////////////////////////////////////////////////// class c4_HandlerSeq: public c4_Sequence { c4_PtrArray _handlers; c4_Persist *_persist; c4_Field *_field; c4_HandlerSeq *_parent; int _numRows; public: c4_HandlerSeq(c4_Persist *); c4_HandlerSeq(c4_HandlerSeq &owner_, c4_Handler *handler_); int NumRows()const override; void SetNumRows(int) override; int NumHandlers()const override; c4_Handler &NthHandler(int)const override; const c4_Sequence *HandlerContext(int)const override; int AddHandler(c4_Handler *) override; void DefineRoot(); void Restructure(c4_Field &, bool remove_); void DetachFromParent(); void DetachFromStorage(bool full_); void DetermineSpaceUsage(); c4_Field &Definition()const; const char *Description() override; c4_HandlerSeq &Parent()const; c4_Persist *Persist()const override; c4_Field &Field(int)const; int NumFields()const; char ColumnType(int index_)const; bool IsNested(int)const; void Prepare(const t4_byte **ptr_, bool selfDesc_); void OldPrepare(); void FlipAllBytes(); void ExchangeEntries(int srcPos_, c4_HandlerSeq &dst_, int dstPos_); c4_HandlerSeq &SubEntry(int, int)const; c4_Field *FindField(const c4_Handler *handler_); void UnmappedAll(); static void BuildMeta(int, int, c4_View &, const c4_Field &); protected: c4_Handler *CreateHandler(const c4_Property &) override; virtual ~c4_HandlerSeq(); }; ///////////////////////////////////////////////////////////////////////////// #if q4_INLINE #include "handler.inl" #endif ///////////////////////////////////////////////////////////////////////////// #endif diff --git a/plugins/mk4storage/storagemk4impl.cpp b/plugins/mk4storage/storagemk4impl.cpp index e76ac3d0..538723c5 100644 --- a/plugins/mk4storage/storagemk4impl.cpp +++ b/plugins/mk4storage/storagemk4impl.cpp @@ -1,386 +1,385 @@ /* This file is part of Akregator. Copyright (C) 2005 Stanislav Karchebny 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "storagemk4impl.h" #include "feedstoragemk4impl.h" #include #include #include #include #include #include -#include #include #include class Akregator::Backend::StorageMK4Impl::StorageMK4ImplPrivate { public: StorageMK4ImplPrivate() : modified(false), purl("url"), pFeedList("feedList"), pTagSet("tagSet"), punread("unread"), ptotalCount("totalCount"), plastFetch("lastFetch") {} c4_Storage *storage; Akregator::Backend::StorageMK4Impl *q; c4_View archiveView; bool autoCommit; bool modified; mutable QMap feeds; QStringList feedURLs; c4_StringProp purl, pFeedList, pTagSet; c4_IntProp punread, ptotalCount, plastFetch; QString archivePath; c4_Storage *feedListStorage; c4_View feedListView; Akregator::Backend::FeedStorageMK4Impl *createFeedStorage(const QString &url); }; Akregator::Backend::StorageMK4Impl::StorageMK4Impl() : d(new StorageMK4ImplPrivate) { d->q = this; setArchivePath(QString()); } Akregator::Backend::FeedStorageMK4Impl *Akregator::Backend::StorageMK4Impl::StorageMK4ImplPrivate::createFeedStorage(const QString &url) { if (!feeds.contains(url)) { Akregator::Backend::FeedStorageMK4Impl *fs = new Akregator::Backend::FeedStorageMK4Impl(url, q); feeds[url] = fs; c4_Row findrow; purl(findrow) = url.toLatin1(); int findidx = archiveView.Find(findrow); if (findidx == -1) { punread(findrow) = 0; ptotalCount(findrow) = 0; plastFetch(findrow) = 0; archiveView.Add(findrow); modified = true; } fs->convertOldArchive(); } return feeds[url]; } Akregator::Backend::FeedStorage *Akregator::Backend::StorageMK4Impl::archiveFor(const QString &url) { return d->createFeedStorage(url); } const Akregator::Backend::FeedStorage *Akregator::Backend::StorageMK4Impl::archiveFor(const QString &url) const { return d->createFeedStorage(url); } void Akregator::Backend::StorageMK4Impl::setArchivePath(const QString &archivePath) { if (archivePath.isNull()) { // if isNull, reset to default d->archivePath = defaultArchivePath(); } else { d->archivePath = archivePath; } } QString Akregator::Backend::StorageMK4Impl::archivePath() const { return d->archivePath; } QString Akregator::Backend::StorageMK4Impl::defaultArchivePath() { const QString ret = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("akregator/Archive"); QDir().mkpath(ret); return ret; } Akregator::Backend::StorageMK4Impl::~StorageMK4Impl() { close(); delete d; d = 0; } void Akregator::Backend::StorageMK4Impl::initialize(const QStringList &) {} bool Akregator::Backend::StorageMK4Impl::open(bool autoCommit) { QString filePath = d->archivePath + QLatin1String("/archiveindex.mk4"); d->storage = new c4_Storage(filePath.toLocal8Bit(), true); d->archiveView = d->storage->GetAs("archive[url:S,unread:I,totalCount:I,lastFetch:I]"); c4_View hash = d->storage->GetAs("archiveHash[_H:I,_R:I]"); d->archiveView = d->archiveView.Hash(hash, 1); // hash on url d->autoCommit = autoCommit; filePath = d->archivePath + QLatin1String("/feedlistbackup.mk4"); d->feedListStorage = new c4_Storage(filePath.toLocal8Bit(), true); d->feedListView = d->feedListStorage->GetAs("archive[feedList:S,tagSet:S]"); return true; } bool Akregator::Backend::StorageMK4Impl::autoCommit() const { return d->autoCommit; } bool Akregator::Backend::StorageMK4Impl::close() { QMap::Iterator it; QMap::Iterator end(d->feeds.end()); for (it = d->feeds.begin(); it != end; ++it) { it.value()->close(); delete it.value(); } if (d->autoCommit) { d->storage->Commit(); } delete d->storage; d->storage = 0; d->feedListStorage->Commit(); delete d->feedListStorage; d->feedListStorage = 0; return true; } bool Akregator::Backend::StorageMK4Impl::commit() { QMap::Iterator it; QMap::Iterator end(d->feeds.end()); for (it = d->feeds.begin(); it != end; ++it) { it.value()->commit(); } if (d->storage) { d->storage->Commit(); return true; } return false; } bool Akregator::Backend::StorageMK4Impl::rollback() { QMap::Iterator it; QMap::Iterator end(d->feeds.end()); for (it = d->feeds.begin(); it != end; ++it) { it.value()->rollback(); } if (d->storage) { d->storage->Rollback(); return true; } return false; } int Akregator::Backend::StorageMK4Impl::unreadFor(const QString &url) const { c4_Row findrow; d->purl(findrow) = url.toLatin1(); int findidx = d->archiveView.Find(findrow); return findidx != -1 ? d->punread(d->archiveView.GetAt(findidx)) : 0; } void Akregator::Backend::StorageMK4Impl::setUnreadFor(const QString &url, int unread) { c4_Row findrow; d->purl(findrow) = url.toLatin1(); int findidx = d->archiveView.Find(findrow); if (findidx == -1) { return; } findrow = d->archiveView.GetAt(findidx); d->punread(findrow) = unread; d->archiveView.SetAt(findidx, findrow); markDirty(); } int Akregator::Backend::StorageMK4Impl::totalCountFor(const QString &url) const { c4_Row findrow; d->purl(findrow) = url.toLatin1(); int findidx = d->archiveView.Find(findrow); return findidx != -1 ? d->ptotalCount(d->archiveView.GetAt(findidx)) : 0; } void Akregator::Backend::StorageMK4Impl::setTotalCountFor(const QString &url, int total) { c4_Row findrow; d->purl(findrow) = url.toLatin1(); int findidx = d->archiveView.Find(findrow); if (findidx == -1) { return; } findrow = d->archiveView.GetAt(findidx); d->ptotalCount(findrow) = total; d->archiveView.SetAt(findidx, findrow); markDirty(); } int Akregator::Backend::StorageMK4Impl::lastFetchFor(const QString &url) const { c4_Row findrow; d->purl(findrow) = url.toLatin1(); int findidx = d->archiveView.Find(findrow); return (findidx != -1 ? d->plastFetch(d->archiveView.GetAt(findidx)) : 0); } void Akregator::Backend::StorageMK4Impl::setLastFetchFor(const QString &url, int lastFetch) { c4_Row findrow; d->purl(findrow) = url.toLatin1(); int findidx = d->archiveView.Find(findrow); if (findidx == -1) { return; } findrow = d->archiveView.GetAt(findidx); d->plastFetch(findrow) = lastFetch; d->archiveView.SetAt(findidx, findrow); markDirty(); } void Akregator::Backend::StorageMK4Impl::markDirty() { if (!d->modified) { d->modified = true; // commit changes after 3 seconds QTimer::singleShot(3000, this, &StorageMK4Impl::slotCommit); } } void Akregator::Backend::StorageMK4Impl::slotCommit() { if (d->modified) { commit(); } d->modified = false; } QStringList Akregator::Backend::StorageMK4Impl::feeds() const { // TODO: cache list QStringList list; int size = d->archiveView.GetSize(); for (int i = 0; i < size; ++i) { list += QString::fromLatin1(d->purl(d->archiveView.GetAt(i))); } // fill with urls return list; } void Akregator::Backend::StorageMK4Impl::add(Storage *source) { QStringList feeds = source->feeds(); QStringList::ConstIterator end(feeds.constEnd()); for (QStringList::ConstIterator it = feeds.constBegin(); it != end; ++it) { FeedStorage *fa = archiveFor(*it); fa->add(source->archiveFor(*it)); } } void Akregator::Backend::StorageMK4Impl::clear() { QStringList feeds; int size = d->archiveView.GetSize(); for (int i = 0; i < size; ++i) { feeds += QString::fromLatin1(d->purl(d->archiveView.GetAt(i))); } QStringList::ConstIterator end(feeds.constEnd()); for (QStringList::ConstIterator it = feeds.constBegin(); it != end; ++it) { FeedStorage *fa = archiveFor(*it); fa->clear(); fa->commit(); // FIXME: delete file (should be 0 in size now) } d->storage->RemoveAll(); } void Akregator::Backend::StorageMK4Impl::storeFeedList(const QString &opmlStr) { if (d->feedListView.GetSize() == 0) { c4_Row row; d->pFeedList(row) = !opmlStr.isEmpty() ? opmlStr.toUtf8().data() : ""; d->pTagSet(row) = ""; d->feedListView.Add(row); } else { c4_Row row = d->feedListView.GetAt(0); d->pFeedList(row) = !opmlStr.isEmpty() ? opmlStr.toUtf8().data() : ""; d->feedListView.SetAt(0, row); } markDirty(); } QString Akregator::Backend::StorageMK4Impl::restoreFeedList() const { if (d->feedListView.GetSize() == 0) { return QString(); } c4_Row row = d->feedListView.GetAt(0); return QString::fromUtf8(d->pFeedList(row)); } void Akregator::Backend::StorageMK4Impl::storeTagSet(const QString &xmlStr) { if (d->feedListView.GetSize() == 0) { c4_Row row; d->pTagSet(row) = !xmlStr.isEmpty() ? xmlStr.toUtf8().data() : ""; d->pFeedList(row) = ""; d->feedListView.Add(row); } else { c4_Row row = d->feedListView.GetAt(0); d->pTagSet(row) = !xmlStr.isEmpty() ? xmlStr.toUtf8().data() : ""; d->feedListView.SetAt(0, row); } markDirty(); } QString Akregator::Backend::StorageMK4Impl::restoreTagSet() const { if (d->feedListView.GetSize() == 0) { return QString(); } c4_Row row = d->feedListView.GetAt(0); return QString::fromUtf8(d->pTagSet(row)); } diff --git a/src/actions/actionmanagerimpl.cpp b/src/actions/actionmanagerimpl.cpp index 34ae3cdd..fb139137 100644 --- a/src/actions/actionmanagerimpl.cpp +++ b/src/actions/actionmanagerimpl.cpp @@ -1,672 +1,671 @@ /* This file is part of Akregator. Copyright (C) 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "actionmanagerimpl.h" #include "akregatorconfig.h" #include "akregator_part.h" #include "articlelistview.h" #include "feed.h" #include "fetchqueue.h" #include "folder.h" #include "kernel.h" #include "mainwidget.h" #include "subscriptionlistview.h" #include "tabwidget.h" #include "trayicon.h" #include "treenode.h" #include "treenodevisitor.h" #include #include #include #include #include #include #include "akregator_debug.h" #include #include #include #include #include #include #include -#include #include #include using namespace Akregator; class ActionManagerImpl::NodeSelectVisitor : public TreeNodeVisitor { public: NodeSelectVisitor(ActionManagerImpl *manager) : m_manager(manager) { } bool visitFeed(Feed *node) override { QAction *remove = m_manager->action(QStringLiteral("feed_remove")); if (remove) { remove->setEnabled(true); } QAction *hp = m_manager->action(QStringLiteral("feed_homepage")); if (hp) { hp->setEnabled(!node->htmlUrl().isEmpty()); } m_manager->action(QStringLiteral("feed_fetch"))->setText(i18n("&Fetch Feed")); m_manager->action(QStringLiteral("feed_remove"))->setText(i18n("&Delete Feed")); m_manager->action(QStringLiteral("feed_modify"))->setText(i18n("&Edit Feed...")); m_manager->action(QStringLiteral("feed_mark_all_as_read"))->setText(i18n("&Mark Feed as Read")); return true; } bool visitFolder(Folder *node) override { QAction *remove = m_manager->action(QStringLiteral("feed_remove")); if (remove) { remove->setEnabled(node->parent()); // root nodes must not be deleted } QAction *hp = m_manager->action(QStringLiteral("feed_homepage")); if (hp) { hp->setEnabled(false); } m_manager->action(QStringLiteral("feed_fetch"))->setText(i18n("&Fetch Feeds")); m_manager->action(QStringLiteral("feed_remove"))->setText(i18n("&Delete Folder")); m_manager->action(QStringLiteral("feed_modify"))->setText(i18n("&Rename Folder")); m_manager->action(QStringLiteral("feed_mark_all_as_read"))->setText(i18n("&Mark Feeds as Read")); return true; } private: ActionManagerImpl *m_manager = nullptr; }; class ActionManagerImpl::ActionManagerImplPrivate { public: QString quickSearchLineText() const; NodeSelectVisitor *nodeSelectVisitor = nullptr; ArticleListView *articleList = nullptr; SubscriptionListView *subscriptionListView = nullptr; MainWidget *mainWidget = nullptr; Part *part = nullptr; TrayIcon *trayIcon = nullptr; KActionMenu *tagMenu = nullptr; KActionCollection *actionCollection = nullptr; TabWidget *tabWidget = nullptr; PimCommon::ShareServiceUrlManager *shareServiceManager = nullptr; WebEngineViewer::ZoomActionMenu *zoomActionMenu = nullptr; QAction *mQuickSearchAction = nullptr; }; void ActionManagerImpl::slotNodeSelected(TreeNode *node) { if (node != 0) { d->nodeSelectVisitor->visit(node); } } ActionManagerImpl::ActionManagerImpl(Part *part, QObject *parent) : ActionManager(parent) , d(new ActionManagerImplPrivate) { d->nodeSelectVisitor = new NodeSelectVisitor(this); d->part = part; d->subscriptionListView = 0; d->articleList = 0; d->trayIcon = 0; d->mainWidget = 0; d->tabWidget = 0; d->tagMenu = 0; d->actionCollection = part->actionCollection(); d->shareServiceManager = new PimCommon::ShareServiceUrlManager(this); initPart(); } ActionManagerImpl::~ActionManagerImpl() { delete d->nodeSelectVisitor; delete d; d = 0; } void ActionManagerImpl::setTrayIcon(TrayIcon *trayIcon) { if (trayIcon == 0) { d->trayIcon = 0; return; } if (d->trayIcon) { return; } else { d->trayIcon = trayIcon; } QMenu *traypop = trayIcon->contextMenu(); if (QAction *act = actionCollection()->action(QStringLiteral("feed_fetch_all"))) { traypop->addAction(act); } if (QAction *act = actionCollection()->action(QStringLiteral("options_configure"))) { traypop->addAction(act); } } void ActionManagerImpl::initPart() { QAction *action = d->actionCollection->addAction(QStringLiteral("file_import")); action->setText(i18n("&Import Feeds...")); action->setIcon(QIcon::fromTheme(QStringLiteral("document-import"))); connect(action, &QAction::triggered, d->part, &Part::fileImport); action = d->actionCollection->addAction(QStringLiteral("file_export")); action->setText(i18n("&Export Feeds...")); action->setIcon(QIcon::fromTheme(QStringLiteral("document-export"))); connect(action, &QAction::triggered, d->part, &Part::fileExport); QAction *configure = d->actionCollection->addAction(QStringLiteral("options_configure")); configure->setText(i18n("&Configure Akregator...")); configure->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); connect(configure, &QAction::triggered, d->part, &Part::showOptions); KStandardAction::configureNotifications(d->part, SLOT(showNotificationOptions()), d->actionCollection); // options_configure_notifications } void ActionManagerImpl::initMainWidget(MainWidget *mainWidget) { if (d->mainWidget) { return; } d->mainWidget = mainWidget; KActionCollection *coll = actionCollection(); // Feed/Feed Group popup menu QAction *action = coll->addAction(QStringLiteral("feed_homepage")); action->setText(i18n("&Open Homepage")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotOpenHomepage); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_H)); action = coll->addAction(QStringLiteral("reload_all_tabs")); action->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); action->setText(i18n("Reload All Tabs")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotReloadAllTabs); coll->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_F5)); action = coll->addAction(QStringLiteral("feed_add")); action->setIcon(QIcon::fromTheme(QStringLiteral("feed-subscribe"))); action->setText(i18n("&Add Feed...")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotFeedAdd); coll->setDefaultShortcut(action, QKeySequence(Qt::Key_Insert)); action = coll->addAction(QStringLiteral("feed_add_group")); action->setIcon(QIcon::fromTheme(QStringLiteral("folder-new"))); action->setText(i18n("Ne&w Folder...")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotFeedAddGroup); coll->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_Insert)); action = coll->addAction(QStringLiteral("feed_remove")); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); action->setText(i18n("&Delete Feed")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotFeedRemove); coll->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Delete)); action = coll->addAction(QStringLiteral("feed_modify")); action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); action->setText(i18n("&Edit Feed...")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotFeedModify); coll->setDefaultShortcut(action, QKeySequence(Qt::Key_F2)); // toolbar / View const MainWidget::ViewMode viewMode = static_cast(Settings::viewMode()); QActionGroup *group = new QActionGroup(this); action = coll->addAction(QStringLiteral("normal_view")); action->setCheckable(true); action->setChecked(viewMode == MainWidget::NormalView); group->addAction(action); action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-top-bottom"))); action->setText(i18n("&Normal View")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotNormalView); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_1)); action = coll->addAction(QStringLiteral("widescreen_view")); action->setCheckable(true); action->setChecked(viewMode == MainWidget::WidescreenView); group->addAction(action); action->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); action->setText(i18n("&Widescreen View")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotWidescreenView); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_2)); action = coll->addAction(QStringLiteral("combined_view")); action->setCheckable(true); action->setChecked(viewMode == MainWidget::CombinedView); group->addAction(action); action->setIcon(QIcon::fromTheme(QStringLiteral("view-list-text"))); action->setText(i18n("C&ombined View")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotCombinedView); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_3)); // toolbar / feed menu action = coll->addAction(QStringLiteral("feed_fetch")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); action->setText(i18n("&Fetch Feed")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotFetchCurrentFeed); coll->setDefaultShortcuts(action, KStandardShortcut::shortcut(KStandardShortcut::Reload)); action = coll->addAction(QStringLiteral("feed_fetch_all")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-bottom"))); action->setText(i18n("Fe&tch All Feeds")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotFetchAllFeeds); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_L)); QAction *stopAction = coll->addAction(QStringLiteral("feed_stop")); stopAction->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); stopAction->setText(i18n("C&ancel Feed Fetches")); connect(stopAction, &QAction::triggered, Kernel::self()->fetchQueue(), &FetchQueue::slotAbort); coll->setDefaultShortcut(stopAction, QKeySequence(Qt::Key_Escape)); stopAction->setEnabled(false); action = coll->addAction(QStringLiteral("feed_mark_all_as_read")); action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read"))); action->setText(i18n("&Mark Feed as Read")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotMarkAllRead); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_R)); action = coll->addAction(QStringLiteral("feed_mark_all_feeds_as_read")); action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read"))); action->setText(i18n("Ma&rk All Feeds as Read")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotMarkAllFeedsRead); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_R)); // Settings menu KToggleAction *sqf = coll->add(QStringLiteral("show_quick_filter")); sqf->setText(i18n("Show Quick Filter")); connect(sqf, &QAction::triggered, d->mainWidget, &MainWidget::slotToggleShowQuickFilter); sqf->setChecked(Settings::showQuickFilter()); action = coll->addAction(QStringLiteral("article_open")); action->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); action->setText(i18n("Open in Tab")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotOpenSelectedArticles); coll->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::Key_Return)); action = coll->addAction(QStringLiteral("article_open_in_background")); action->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); action->setText(i18n("Open in Background Tab")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotOpenSelectedArticlesInBackground); coll->setDefaultShortcut(action, QKeySequence(Qt::Key_Return)); action = coll->addAction(QStringLiteral("article_open_external")); action->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); action->setText(i18n("Open in External Browser")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotOpenSelectedArticlesInBrowser); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Return)); action = coll->addAction(QStringLiteral("article_copy_link_address")); action->setText(i18n("Copy Link Address")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotCopyLinkAddress); action = coll->addAction(QStringLiteral("go_prev_unread_article")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); action->setText(i18n("Pre&vious Unread Article")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotPrevUnreadArticle); coll->setDefaultShortcuts(action, QList() << QKeySequence(Qt::Key_Minus) << QKeySequence(Qt::Key_Minus + Qt::KeypadModifier)); action = coll->addAction(QStringLiteral("go_next_unread_article")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); action->setText(i18n("Ne&xt Unread Article")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotNextUnreadArticle); coll->setDefaultShortcuts(action, QList() << QKeySequence(Qt::Key_Plus) << QKeySequence(Qt::Key_Plus + Qt::KeypadModifier) << QKeySequence(Qt::Key_Equal) << QKeySequence(Qt::Key_Equal + Qt::KeypadModifier)); action = coll->addAction(QStringLiteral("article_delete")); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); action->setText(i18n("&Delete")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotArticleDelete); coll->setDefaultShortcut(action, QKeySequence(Qt::Key_Delete)); KActionMenu *statusMenu = coll->add(QStringLiteral("article_set_status")); statusMenu->setText(i18n("&Mark As")); statusMenu->setEnabled(false); action = coll->addAction(QStringLiteral("article_set_status_read")); action->setText(i18nc("as in: mark as read", "&Read")); action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-read"))); action->setToolTip(i18n("Mark selected article as read")); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_E)); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotSetSelectedArticleRead); statusMenu->addAction(action); action = coll->addAction(QStringLiteral("article_set_status_new")); action->setText(i18nc("as in: mark as new", "&New")); action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-unread-new"))); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_N)); action->setToolTip(i18n("Mark selected article as new")); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotSetSelectedArticleNew); statusMenu->addAction(action); action = coll->addAction(QStringLiteral("article_set_status_unread")); action->setText(i18nc("as in: mark as unread", "&Unread")); action->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-unread"))); action->setToolTip(i18n("Mark selected article as unread")); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_U)); connect(action, &QAction::triggered, d->mainWidget, &MainWidget::slotSetSelectedArticleUnread); statusMenu->addAction(action); KToggleAction *importantAction = coll->add(QStringLiteral("article_set_status_important")); importantAction->setText(i18n("&Mark as Important")); importantAction->setIcon(QIcon::fromTheme(QStringLiteral("mail-mark-important"))); const QList importantSC = {QKeySequence(Qt::CTRL + Qt::Key_I), QKeySequence(Qt::Key_I)}; coll->setDefaultShortcuts(importantAction, importantSC); importantAction->setCheckedState(KGuiItem(i18n("Remove &Important Mark"))); connect(importantAction, &QAction::triggered, d->mainWidget, &MainWidget::slotArticleToggleKeepFlag); action = coll->addAction(QStringLiteral("feedstree_move_up")); action->setText(i18n("Move Node Up")); connect(action, &QAction::triggered, mainWidget, &MainWidget::slotMoveCurrentNodeUp); coll->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Up)); action = coll->addAction(QStringLiteral("feedstree_move_down")); action->setText(i18n("Move Node Down")); connect(action, &QAction::triggered, mainWidget, &MainWidget::slotMoveCurrentNodeDown); coll->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Down)); action = coll->addAction(QStringLiteral("move_node_left")); action->setText(i18n("Move Node Left")); connect(action, &QAction::triggered, mainWidget, &MainWidget::slotMoveCurrentNodeLeft); coll->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Left)); action = coll->addAction(QStringLiteral("feedstree_move_right")); action->setText(i18n("Move Node Right")); connect(action, &QAction::triggered, mainWidget, &MainWidget::slotMoveCurrentNodeRight); coll->setDefaultShortcut(action, QKeySequence(Qt::SHIFT + Qt::ALT + Qt::Key_Right)); action = coll->addAction(QStringLiteral("file_sendlink")); action->setIcon(QIcon::fromTheme(QStringLiteral("mail-message-new"))); action->setText(i18n("Send &Link Address...")); connect(action, &QAction::triggered, mainWidget, &MainWidget::slotSendLink); action = coll->addAction(QStringLiteral("file_sendfile")); action->setIcon(QIcon::fromTheme(QStringLiteral("mail-message-new"))); action->setText(i18n("Send &File...")); connect(action, &QAction::triggered, mainWidget, &MainWidget::slotSendFile); coll->addAction(QStringLiteral("share_serviceurl"), d->shareServiceManager->menu()); connect(d->shareServiceManager, &PimCommon::ShareServiceUrlManager::serviceUrlSelected, this, &ActionManagerImpl::slotServiceUrlSelected); d->mQuickSearchAction = new QAction(i18n("Set Focus to Quick Search"), this); //If change shortcut change Panel::setQuickSearchClickMessage(...) message actionCollection()->setDefaultShortcut(d->mQuickSearchAction, QKeySequence(Qt::ALT + Qt::Key_Q)); actionCollection()->addAction(QStringLiteral("focus_to_quickseach"), d->mQuickSearchAction); connect(d->mQuickSearchAction, &QAction::triggered, mainWidget, &MainWidget::slotFocusQuickSearch); setArticleActionsEnabled(false); } void ActionManagerImpl::slotServiceUrlSelected(PimCommon::ShareServiceUrlManager::ServiceType type) { if (d->mainWidget) { QString title; QString link; d->mainWidget->currentArticleInfo(link, title); const QUrl url = d->shareServiceManager->generateServiceUrl(link, title, type); d->shareServiceManager->openUrl(url); } } void ActionManagerImpl::initArticleListView(ArticleListView *articleList) { if (d->articleList) { return; } else { d->articleList = articleList; } QAction *action = actionCollection()->addAction(QStringLiteral("go_previous_article")); action->setText(i18n("&Previous Article")); connect(action, &QAction::triggered, articleList, &ArticleListView::slotPreviousArticle); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Left)); action = actionCollection()->addAction(QStringLiteral("go_next_article")); action->setText(i18n("&Next Article")); connect(action, &QAction::triggered, articleList, &ArticleListView::slotNextArticle); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::Key_Right)); } void ActionManagerImpl::initSubscriptionListView(SubscriptionListView *subscriptionListView) { if (d->subscriptionListView) { return; } else { d->subscriptionListView = subscriptionListView; } KActionCollection *coll = actionCollection(); QAction *action = coll->addAction(QStringLiteral("go_prev_feed")); action->setText(i18n("&Previous Feed")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotPrevFeed); coll->setDefaultShortcut(action, QKeySequence(Qt::Key_P)); action = coll->addAction(QStringLiteral("go_next_feed")); action->setText(i18n("&Next Feed")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotNextFeed); coll->setDefaultShortcut(action, QKeySequence(Qt::Key_N)); action = coll->addAction(QStringLiteral("go_next_unread_feed")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); action->setText(i18n("N&ext Unread Feed")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotNextUnreadFeed); coll->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Plus)); action = coll->addAction(QStringLiteral("go_prev_unread_feed")); action->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); action->setText(i18n("Prev&ious Unread Feed")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotPrevUnreadFeed); coll->setDefaultShortcut(action, QKeySequence(Qt::ALT + Qt::Key_Minus)); action = coll->addAction(QStringLiteral("feedstree_home")); action->setText(i18n("Go to Top of Tree")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotItemBegin); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Home)); action = coll->addAction(QStringLiteral("feedstree_end")); action->setText(i18n("Go to Bottom of Tree")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotItemEnd); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_End)); action = coll->addAction(QStringLiteral("feedstree_left")); action->setText(i18n("Go Left in Tree")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotItemLeft); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Left)); action = coll->addAction(QStringLiteral("feedstree_right")); action->setText(i18n("Go Right in Tree")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotItemRight); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Right)); action = coll->addAction(QStringLiteral("feedstree_up")); action->setText(i18n("Go Up in Tree")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotItemUp); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Up)); action = coll->addAction(QStringLiteral("feedstree_down")); action->setText(i18n("Go Down in Tree")); connect(action, &QAction::triggered, subscriptionListView, &SubscriptionListView::slotItemDown); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Down)); } void ActionManagerImpl::initTabWidget(TabWidget *tabWidget) { if (d->tabWidget) { return; } else { d->tabWidget = tabWidget; } KActionCollection *coll = actionCollection(); QAction *action = coll->addAction(QStringLiteral("select_next_tab")); action->setText(i18n("Select Next Tab")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotNextTab); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Period)); action = coll->addAction(QStringLiteral("select_previous_tab")); action->setText(i18n("Select Previous Tab")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotPreviousTab); coll->setDefaultShortcut(action, QKeySequence(Qt::Key_Comma + Qt::CTRL)); action = coll->addAction(QStringLiteral("tab_detach")); action->setIcon(QIcon::fromTheme(QStringLiteral("tab-detach"))); action->setText(i18n("Detach Tab")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotDetachTab); coll->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B)); action = KStandardAction::copy(d->tabWidget, &TabWidget::slotCopy, coll); coll->addAction(QStringLiteral("viewer_copy"), action); action = KStandardAction::print(d->tabWidget, &TabWidget::slotPrint, coll); coll->addAction(QStringLiteral("viewer_print"), action); action = KStandardAction::printPreview(d->tabWidget, &TabWidget::slotPrintPreview, coll); coll->addAction(QStringLiteral("viewer_printpreview"), action); action = coll->addAction(QStringLiteral("tab_mute")); action->setText(i18n("Mute")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotMute); action = coll->addAction(QStringLiteral("tab_unmute")); action->setText(i18n("Unmute")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotUnMute); action = new QAction(i18n("Speak Text"), this); action->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-text-to-speech"))); coll->addAction(QStringLiteral("speak_text"), action); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotTextToSpeech); action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("&Find in Message..."), this); coll->addAction(QStringLiteral("find_in_messages"), action); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotFindTextInHtml); coll->setDefaultShortcut(action, KStandardShortcut::find().first()); action = coll->addAction(QStringLiteral("tab_copylinkaddress")); action->setText(i18n("Copy Link Address")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotCopyLinkAddress); action = coll->addAction(QStringLiteral("tab_remove")); action->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); action->setText(i18n("Close Tab")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotCloseTab); coll->setDefaultShortcuts(action, KStandardShortcut::close()); d->zoomActionMenu = new WebEngineViewer::ZoomActionMenu(this); connect(d->zoomActionMenu, &WebEngineViewer::ZoomActionMenu::zoomChanged, d->tabWidget, &TabWidget::slotZoomChanged); d->zoomActionMenu->setActionCollection(coll); d->zoomActionMenu->createZoomActions(); QString actionname; for (int i = 1; i < 10; ++i) { actionname.sprintf("activate_tab_%02d", i); action = new QAction(i18n("Activate Tab %1", i), this); coll->addAction(actionname, action); coll->setDefaultShortcut(action, QKeySequence(QStringLiteral("Alt+%1").arg(i))); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotActivateTab); } action = coll->addAction(QStringLiteral("savelinkas")); action->setText(i18n("&Save Link As...")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotSaveLinkAs); action = coll->addAction(QStringLiteral("copylinkaddress")); action->setText(i18n("Copy &Link Address")); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotCopyLinkAddress); action = new QAction(i18n("Copy Image Location"), this); action->setIcon(QIcon::fromTheme(QStringLiteral("view-media-visualization"))); coll->addAction(QStringLiteral("copy_image_location"), action); coll->setShortcutsConfigurable(action, false); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotCopyImageLocation); // save Image On Disk action = new QAction(i18n("Save Image On Disk..."), this); coll->addAction(QStringLiteral("saveas_imageurl"), action); coll->setShortcutsConfigurable(action, false); connect(action, &QAction::triggered, d->tabWidget, &TabWidget::slotSaveImageOnDisk); } QWidget *ActionManagerImpl::container(const QString &name) { if (d->part->factory()) { return d->part->factory()->container(name, d->part); } else { return 0; } } KActionCollection *ActionManagerImpl::actionCollection() const { return d->actionCollection; } QAction *ActionManagerImpl::action(const QString &name) { return d->actionCollection != nullptr ? d->actionCollection->action(name) : nullptr; } void ActionManagerImpl::setArticleActionsEnabled(bool enabled) { #undef setActionEnabled #define setActionEnabled(name) { QAction *const a = action(name); if (a) {a->setEnabled(enabled);}} setActionEnabled(QStringLiteral("article_open")) setActionEnabled(QStringLiteral("article_open_external")) setActionEnabled(QStringLiteral("article_set_status_important")) setActionEnabled(QStringLiteral("article_set_status")) setActionEnabled(QStringLiteral("article_delete")) setActionEnabled(QStringLiteral("file_sendlink")) setActionEnabled(QStringLiteral("file_sendfile")) setActionEnabled(QStringLiteral("article_open_in_background")) setActionEnabled(QStringLiteral("share_serviceurl")) #undef setActionEnabled } WebEngineViewer::ZoomActionMenu *ActionManagerImpl::zoomActionMenu() const { return d->zoomActionMenu; } QString ActionManagerImpl::quickSearchLineText() const { return d->quickSearchLineText(); } QString ActionManagerImpl::ActionManagerImplPrivate::quickSearchLineText() const { return mQuickSearchAction->shortcut().toString(); } diff --git a/src/articlelistview.cpp b/src/articlelistview.cpp index 97b2b0c1..ca236b51 100644 --- a/src/articlelistview.cpp +++ b/src/articlelistview.cpp @@ -1,531 +1,530 @@ /* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny 2005-2008 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "articlelistview.h" #include "actionmanager.h" #include "akregatorconfig.h" #include "article.h" #include "articlemodel.h" #include "kernel.h" #include "types.h" #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include using namespace Akregator; FilterDeletedProxyModel::FilterDeletedProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { setDynamicSortFilter(true); } bool FilterDeletedProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { return !sourceModel()->index(source_row, 0, source_parent).data(ArticleModel::IsDeletedRole).toBool(); } SortColorizeProxyModel::SortColorizeProxyModel(QObject *parent) : QSortFilterProxyModel(parent) , m_keepFlagIcon(QIcon::fromTheme(QStringLiteral("mail-mark-important"))) { m_unreadColor = KColorScheme(QPalette::Normal, KColorScheme::View).foreground(KColorScheme::PositiveText).color(); m_newColor = KColorScheme(QPalette::Normal, KColorScheme::View).foreground(KColorScheme::NegativeText).color(); } bool SortColorizeProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { if (source_parent.isValid()) { return false; } for (uint i = 0; i < m_matchers.size(); ++i) { if (!static_cast(sourceModel())->rowMatches(source_row, m_matchers[i])) { return false; } } return true; } void SortColorizeProxyModel::setFilters(const std::vector > &matchers) { if (m_matchers == matchers) { return; } m_matchers = matchers; invalidateFilter(); } QVariant SortColorizeProxyModel::data(const QModelIndex &idx, int role) const { if (!idx.isValid() || !sourceModel()) { return QVariant(); } const QModelIndex sourceIdx = mapToSource(idx); switch (role) { case Qt::ForegroundRole: switch (static_cast(sourceIdx.data(ArticleModel::StatusRole).toInt())) { case Unread: return Settings::useCustomColors() ? Settings::colorUnreadArticles() : m_unreadColor; case New: return Settings::useCustomColors() ? Settings::colorNewArticles() : m_newColor; case Read: return QApplication::palette().color(QPalette::Text); } break; case Qt::DecorationRole: if (sourceIdx.column() == ArticleModel::ItemTitleColumn) { return sourceIdx.data(ArticleModel::IsImportantRole).toBool() ? m_keepFlagIcon : QVariant(); } break; } return sourceIdx.data(role); } namespace { static bool isRead(const QModelIndex &idx) { if (!idx.isValid()) { return false; } return static_cast(idx.data(ArticleModel::StatusRole).toInt()) == Read; } } void ArticleListView::setArticleModel(ArticleModel *model) { if (!model) { setModel(model); return; } m_proxy = new SortColorizeProxyModel(model); m_proxy->setSourceModel(model); m_proxy->setSortRole(ArticleModel::SortRole); m_proxy->setFilters(m_matchers); FilterDeletedProxyModel *const proxy2 = new FilterDeletedProxyModel(model); proxy2->setSortRole(ArticleModel::SortRole); proxy2->setSourceModel(m_proxy); connect(model, &QAbstractItemModel::rowsInserted, m_proxy.data(), &QSortFilterProxyModel::invalidate); FilterColumnsProxyModel *const columnsProxy = new FilterColumnsProxyModel(model); columnsProxy->setSortRole(ArticleModel::SortRole); columnsProxy->setSourceModel(proxy2); columnsProxy->setColumnEnabled(ArticleModel::ItemTitleColumn); columnsProxy->setColumnEnabled(ArticleModel::FeedTitleColumn); columnsProxy->setColumnEnabled(ArticleModel::DateColumn); columnsProxy->setColumnEnabled(ArticleModel::AuthorColumn); setModel(columnsProxy); header()->setContextMenuPolicy(Qt::CustomContextMenu); header()->setSectionResizeMode(QHeaderView::Interactive); } void ArticleListView::showHeaderMenu(const QPoint &pos) { if (!model()) { return; } QPointer menu = new QMenu(this); menu->setTitle(i18n("Columns")); menu->setAttribute(Qt::WA_DeleteOnClose); const int colCount = model()->columnCount(); int visibleColumns = 0; // number of column currently shown QAction *visibleColumnsAction = nullptr; for (int i = 0; i < colCount; ++i) { QAction *act = menu->addAction(model()->headerData(i, Qt::Horizontal).toString()); act->setCheckable(true); act->setData(i); bool sectionVisible = !header()->isSectionHidden(i); act->setChecked(sectionVisible); if (sectionVisible) { ++visibleColumns; visibleColumnsAction = act; } } // Avoid that the last shown column is also hidden if (visibleColumns == 1) { visibleColumnsAction->setEnabled(false); } QPointer that(this); QAction *const action = menu->exec(header()->mapToGlobal(pos)); if (that && action) { const int col = action->data().toInt(); if (action->isChecked()) { header()->showSection(col); } else { header()->hideSection(col); } } delete menu; } void ArticleListView::saveHeaderSettings() { if (model()) { const QByteArray state = header()->saveState(); if (m_columnMode == FeedMode) { m_feedHeaderState = state; } else { m_groupHeaderState = state; } } KConfigGroup conf(Settings::self()->config(), "General"); conf.writeEntry("ArticleListFeedHeaders", m_feedHeaderState.toBase64()); conf.writeEntry("ArticleListGroupHeaders", m_groupHeaderState.toBase64()); } void ArticleListView::loadHeaderSettings() { KConfigGroup conf(Settings::self()->config(), "General"); m_feedHeaderState = QByteArray::fromBase64(conf.readEntry("ArticleListFeedHeaders").toLatin1()); m_groupHeaderState = QByteArray::fromBase64(conf.readEntry("ArticleListGroupHeaders").toLatin1()); } QItemSelectionModel *ArticleListView::articleSelectionModel() const { return selectionModel(); } const QAbstractItemView *ArticleListView::itemView() const { return this; } QAbstractItemView *ArticleListView::itemView() { return this; } QPoint ArticleListView::scrollBarPositions() const { return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); } void ArticleListView::setScrollBarPositions(const QPoint &p) { horizontalScrollBar()->setValue(p.x()); verticalScrollBar()->setValue(p.y()); } void ArticleListView::setGroupMode() { if (m_columnMode == GroupMode) { return; } if (model()) { m_feedHeaderState = header()->saveState(); } m_columnMode = GroupMode; restoreHeaderState(); } void ArticleListView::setFeedMode() { if (m_columnMode == FeedMode) { return; } if (model()) { m_groupHeaderState = header()->saveState(); } m_columnMode = FeedMode; restoreHeaderState(); } static int maxDateColumnWidth(const QFontMetrics &fm) { int width = 0; QDateTime date(QDate::currentDate(), QTime(23, 59)); for (int x = 0; x < 10; ++x, date = date.addDays(-1)) { QString txt = QLatin1Char(' ') + QLocale().toString(date, QLocale::ShortFormat) + QLatin1Char(' '); width = qMax(width, fm.width(txt)); } return width; } void ArticleListView::restoreHeaderState() { QByteArray state = m_columnMode == GroupMode ? m_groupHeaderState : m_feedHeaderState; header()->restoreState(state); if (state.isEmpty()) { // No state, set a default config: // - hide the feed column in feed mode (no need to see the same feed title over and over) // - set the date column wide enough to fit all possible dates header()->setSectionHidden(ArticleModel::FeedTitleColumn, m_columnMode == FeedMode); header()->setStretchLastSection(false); header()->resizeSection(ArticleModel::DateColumn, maxDateColumnWidth(fontMetrics())); if (model()) { startResizingTitleColumn(); } } if (header()->sectionSize(ArticleModel::DateColumn) == 1) { header()->resizeSection(ArticleModel::DateColumn, maxDateColumnWidth(fontMetrics())); } } void ArticleListView::startResizingTitleColumn() { // set the title column to Stretch resize mode so that it adapts to the // content. finishResizingTitleColumn() will turn the resize mode back to // Interactive so that the user can still resize the column himself if he // wants to header()->setSectionResizeMode(ArticleModel::ItemTitleColumn, QHeaderView::Stretch); QMetaObject::invokeMethod(this, "finishResizingTitleColumn", Qt::QueuedConnection); } void ArticleListView::finishResizingTitleColumn() { if (QApplication::mouseButtons() != Qt::NoButton) { // Come back later: user is still resizing the widget QMetaObject::invokeMethod(this, "finishResizingTitleColumn", Qt::QueuedConnection); return; } header()->setSectionResizeMode(QHeaderView::Interactive); } ArticleListView::~ArticleListView() { saveHeaderSettings(); } void ArticleListView::setIsAggregation(bool aggregation) { if (aggregation) { setGroupMode(); } else { setFeedMode(); } } ArticleListView::ArticleListView(QWidget *parent) : QTreeView(parent) , m_columnMode(FeedMode) { setSortingEnabled(true); setAlternatingRowColors(true); setSelectionMode(QAbstractItemView::ExtendedSelection); setUniformRowHeights(true); setRootIsDecorated(false); setAllColumnsShowFocus(true); setDragDropMode(QAbstractItemView::DragOnly); setMinimumSize(250, 150); setWhatsThis(i18n("

Article list

" "Here you can browse articles from the currently selected feed. " "You can also manage articles, as marking them as persistent (\"Mark as Important\") or delete them, using the right mouse button menu. " "To view the web page of the article, you can open the article internally in a tab or in an external browser window.")); //connect exactly once disconnect(header(), &QWidget::customContextMenuRequested, this, &ArticleListView::showHeaderMenu); connect(header(), &QWidget::customContextMenuRequested, this, &ArticleListView::showHeaderMenu); loadHeaderSettings(); } void ArticleListView::mousePressEvent(QMouseEvent *ev) { // let's push the event, so we can use currentIndex() to get the newly selected article.. QTreeView::mousePressEvent(ev); if (ev->button() == Qt::MidButton) { const QUrl url = currentIndex().data(ArticleModel::LinkRole).toUrl(); Q_EMIT signalMouseButtonPressed(ev->button(), url); } } void ArticleListView::contextMenuEvent(QContextMenuEvent *event) { QWidget *w = ActionManager::getInstance()->container(QStringLiteral("article_popup")); QMenu *popup = qobject_cast(w); if (popup) { popup->exec(event->globalPos()); } } void ArticleListView::setModel(QAbstractItemModel *m) { const bool groupMode = m_columnMode == GroupMode; QAbstractItemModel *const oldModel = model(); if (oldModel) { const QByteArray state = header()->saveState(); if (groupMode) { m_groupHeaderState = state; } else { m_feedHeaderState = state; } } QTreeView::setModel(m); if (m) { sortByColumn(ArticleModel::DateColumn, Qt::DescendingOrder); restoreHeaderState(); // Ensure at least one column is visible if (header()->hiddenSectionCount() == header()->count()) { header()->showSection(ArticleModel::ItemTitleColumn); } } } void ArticleListView::slotClear() { setModel(nullptr); } void ArticleListView::slotPreviousArticle() { if (!model()) { return; } Q_EMIT userActionTakingPlace(); const QModelIndex idx = currentIndex(); const int newRow = qMax(0, (idx.isValid() ? idx.row() : model()->rowCount()) - 1); const QModelIndex newIdx = idx.isValid() ? idx.sibling(newRow, 0) : model()->index(newRow, 0); selectIndex(newIdx); } void ArticleListView::slotNextArticle() { if (!model()) { return; } Q_EMIT userActionTakingPlace(); const QModelIndex idx = currentIndex(); const int newRow = idx.isValid() ? (idx.row() + 1) : 0; const QModelIndex newIdx = model()->index(qMin(newRow, model()->rowCount() - 1), 0); selectIndex(newIdx); } void ArticleListView::slotNextUnreadArticle() { if (!model()) { return; } const int rowCount = model()->rowCount(); const int startRow = qMin(rowCount - 1, (currentIndex().isValid() ? currentIndex().row() + 1 : 0)); int i = startRow; bool foundUnread = false; do { if (!::isRead(model()->index(i, 0))) { foundUnread = true; } else { i = (i + 1) % rowCount; } } while (!foundUnread && i != startRow); if (foundUnread) { selectIndex(model()->index(i, 0)); } } void ArticleListView::selectIndex(const QModelIndex &idx) { if (!idx.isValid()) { return; } setCurrentIndex(idx); clearSelection(); Q_ASSERT(selectionModel()); selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); scrollTo(idx, PositionAtCenter); } void ArticleListView::slotPreviousUnreadArticle() { if (!model()) { return; } const int rowCount = model()->rowCount(); const int startRow = qMax(0, (currentIndex().isValid() ? currentIndex().row() : rowCount) - 1); int i = startRow; bool foundUnread = false; do { if (!::isRead(model()->index(i, 0))) { foundUnread = true; } else { i = i > 0 ? i - 1 : rowCount - 1; } } while (!foundUnread && i != startRow); if (foundUnread) { selectIndex(model()->index(i, 0)); } } void ArticleListView::forceFilterUpdate() { if (m_proxy) { m_proxy->invalidate(); } } void ArticleListView::setFilters(const std::vector > &matchers) { if (m_matchers == matchers) { return; } m_matchers = matchers; if (m_proxy) { m_proxy->setFilters(matchers); } } diff --git a/src/articlematcher.cpp b/src/articlematcher.cpp index f8bbc942..ec476b36 100644 --- a/src/articlematcher.cpp +++ b/src/articlematcher.cpp @@ -1,358 +1,357 @@ /* * articlematcher.cpp * * Copyright (c) 2004, 2005 Frerich Raabe * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "articlematcher.h" #include "article.h" #include "types.h" #include #include #include "akregator_debug.h" #include #include -#include #include namespace Akregator { namespace Filters { AbstractMatcher::AbstractMatcher() { } AbstractMatcher::~AbstractMatcher() { } QString Criterion::subjectToString(Subject subj) { switch (subj) { case Title: return QStringLiteral("Title"); case Link: return QStringLiteral("Link"); case Description: return QStringLiteral("Description"); case Status: return QStringLiteral("Status"); case KeepFlag: return QStringLiteral("KeepFlag"); case Author: return QStringLiteral("Author"); default: // should never happen (TM) return QStringLiteral("Description"); } } Criterion::Subject Criterion::stringToSubject(const QString &subjStr) { if (subjStr == QLatin1String("Title")) { return Title; } else if (subjStr == QLatin1String("Link")) { return Link; } else if (subjStr == QLatin1String("Description")) { return Description; } else if (subjStr == QLatin1String("Status")) { return Status; } else if (subjStr == QLatin1String("KeepFlag")) { return KeepFlag; } else if (subjStr == QLatin1String("Author")) { return Author; } // hopefully never reached return Description; } QString Criterion::predicateToString(Predicate pred) { switch (pred) { case Contains: return QStringLiteral("Contains"); case Equals: return QStringLiteral("Equals"); case Matches: return QStringLiteral("Matches"); case Negation: return QStringLiteral("Negation"); default:// hopefully never reached return QStringLiteral("Contains"); } } Criterion::Predicate Criterion::stringToPredicate(const QString &predStr) { if (predStr == QLatin1String("Contains")) { return Contains; } else if (predStr == QLatin1String("Equals")) { return Equals; } else if (predStr == QLatin1String("Matches")) { return Matches; } else if (predStr == QLatin1String("Negation")) { return Negation; } // hopefully never reached return Contains; } Criterion::Criterion() { } Criterion::Criterion(Subject subject, Predicate predicate, const QVariant &object) : m_subject(subject) , m_predicate(predicate) , m_object(object) { } void Criterion::writeConfig(KConfigGroup *config) const { config->writeEntry(QStringLiteral("subject"), subjectToString(m_subject)); config->writeEntry(QStringLiteral("predicate"), predicateToString(m_predicate)); config->writeEntry(QStringLiteral("objectType"), QString::fromLatin1(m_object.typeName())); config->writeEntry(QStringLiteral("objectValue"), m_object); } void Criterion::readConfig(KConfigGroup *config) { m_subject = stringToSubject(config->readEntry(QStringLiteral("subject"), QString())); m_predicate = stringToPredicate(config->readEntry(QStringLiteral("predicate"), QString())); QVariant::Type type = QVariant::nameToType(config->readEntry(QStringLiteral("objType"), QString()).toLatin1()); if (type != QVariant::Invalid) { m_object = config->readEntry(QStringLiteral("objectValue"), QVariant(type)); } } bool Criterion::satisfiedBy(const Article &article) const { if (article.isNull()) { return false; } QVariant concreteSubject; switch (m_subject) { case Title: concreteSubject = QVariant(article.title()); break; case Description: concreteSubject = QVariant(article.description()); break; case Link: // ### Maybe use prettyUrl here? concreteSubject = QVariant(article.link().url()); break; case Status: concreteSubject = QVariant(article.status()); break; case KeepFlag: concreteSubject = QVariant(article.keep()); break; case Author: concreteSubject = QVariant(article.authorName()); default: break; } bool satisfied = false; const Predicate predicateType = static_cast(m_predicate & ~Negation); QString subjectType = QLatin1String(concreteSubject.typeName()); switch (predicateType) { case Contains: satisfied = concreteSubject.toString().indexOf(m_object.toString(), 0, Qt::CaseInsensitive) != -1; break; case Equals: if (subjectType == QLatin1String("int")) { satisfied = concreteSubject.toInt() == m_object.toInt(); } else { satisfied = concreteSubject.toString() == m_object.toString(); } break; case Matches: satisfied = QRegExp(m_object.toString()).indexIn(concreteSubject.toString()) != -1; break; default: qCDebug(AKREGATOR_LOG) << "Internal inconsistency; predicateType should never be Negation"; break; } if (m_predicate & Negation) { satisfied = !satisfied; } return satisfied; } Criterion::Subject Criterion::subject() const { return m_subject; } Criterion::Predicate Criterion::predicate() const { return m_predicate; } QVariant Criterion::object() const { return m_object; } ArticleMatcher::ArticleMatcher() : m_association(None) { } ArticleMatcher::~ArticleMatcher() { } ArticleMatcher::ArticleMatcher(const QVector &criteria, Association assoc) : m_criteria(criteria) , m_association(assoc) { } bool ArticleMatcher::matches(const Article &a) const { switch (m_association) { case LogicalOr: return anyCriterionMatches(a); case LogicalAnd: return allCriteriaMatch(a); default: break; } return true; } void ArticleMatcher::writeConfig(KConfigGroup *config) const { config->writeEntry(QStringLiteral("matcherAssociation"), associationToString(m_association)); config->writeEntry(QStringLiteral("matcherCriteriaCount"), m_criteria.count()); QString criterionGroupPrefix = config->name() + QLatin1String("_Criterion"); const int criteriaSize(m_criteria.size()); for (int index = 0; index < criteriaSize; ++index) { *config = KConfigGroup(config->config(), criterionGroupPrefix + QString::number(index)); m_criteria.at(index).writeConfig(config); } } void ArticleMatcher::readConfig(KConfigGroup *config) { m_criteria.clear(); m_association = stringToAssociation(config->readEntry(QStringLiteral("matcherAssociation"), QString())); const int count = config->readEntry(QStringLiteral("matcherCriteriaCount"), 0); const QString criterionGroupPrefix = config->name() + QLatin1String("_Criterion"); for (int i = 0; i < count; ++i) { Criterion c; *config = KConfigGroup(config->config(), criterionGroupPrefix + QString::number(i)); c.readConfig(config); m_criteria.append(c); } } bool ArticleMatcher::operator==(const AbstractMatcher &other) const { AbstractMatcher *ptr = const_cast(&other); ArticleMatcher *o = dynamic_cast(ptr); if (!o) { return false; } else { return m_association == o->m_association && m_criteria == o->m_criteria; } } bool ArticleMatcher::operator!=(const AbstractMatcher &other) const { return !(*this == other); } bool ArticleMatcher::anyCriterionMatches(const Article &a) const { if (m_criteria.isEmpty()) { return true; } const int criteriaSize(m_criteria.size()); for (int index = 0; index < criteriaSize; ++index) { if (m_criteria.at(index).satisfiedBy(a)) { return true; } } return false; } bool ArticleMatcher::allCriteriaMatch(const Article &a) const { if (m_criteria.isEmpty()) { return true; } const int criteriaSize(m_criteria.size()); for (int index = 0; index < criteriaSize; ++index) { if (!m_criteria.at(index).satisfiedBy(a)) { return false; } } return true; } ArticleMatcher::Association ArticleMatcher::stringToAssociation(const QString &assocStr) { if (assocStr == QLatin1String("LogicalAnd")) { return LogicalAnd; } else if (assocStr == QLatin1String("LogicalOr")) { return LogicalOr; } else { return None; } } QString ArticleMatcher::associationToString(Association association) { switch (association) { case LogicalAnd: return QStringLiteral("LogicalAnd"); case LogicalOr: return QStringLiteral("LogicalOr"); default: return QStringLiteral("None"); } } } //namespace Filters } //namespace Akregator diff --git a/src/articlemodel.h b/src/articlemodel.h index c46f5d5b..2cdf7e71 100644 --- a/src/articlemodel.h +++ b/src/articlemodel.h @@ -1,107 +1,106 @@ /* This file is part of Akregator. Copyright (C) 2007 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #ifndef AKREGATOR_ARTICLEMODEL_H #define AKREGATOR_ARTICLEMODEL_H #include -#include #include "akregatorpart_export.h" #include #include namespace Akregator { class Article; class TreeNode; namespace Filters { class AbstractMatcher; } class AKREGATORPART_EXPORT ArticleModel : public QAbstractTableModel { Q_OBJECT public: enum Column { ItemTitleColumn = 0, FeedTitleColumn, AuthorColumn, DateColumn, DescriptionColumn, ContentColumn, ColumnCount }; enum Role { SortRole = Qt::UserRole, LinkRole, GuidRole, ItemIdRole, FeedIdRole, StatusRole, IsImportantRole, IsDeletedRole }; explicit ArticleModel(const QVector
&articles, QObject *parent = nullptr); ~ArticleModel(); int columnCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation, int role) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool rowMatches(int row, const QSharedPointer &matcher) const; Article article(int row) const; QStringList mimeTypes() const override; QMimeData *mimeData(const QModelIndexList &indexes) const override; Qt::ItemFlags flags(const QModelIndex &idx) const override; public Q_SLOTS: void articlesAdded(Akregator::TreeNode *, const QVector &); void articlesUpdated(Akregator::TreeNode *, const QVector &); void articlesRemoved(Akregator::TreeNode *, const QVector &); void clear(); private: ArticleModel(const ArticleModel &); ArticleModel &operator=(const ArticleModel &); class Private; Private *const d; }; } //namespace Akregator #endif // AKREGATOR_ARTICLEMODEL_H diff --git a/src/articleviewer-ng/webengine/articleviewerwebengine.cpp b/src/articleviewer-ng/webengine/articleviewerwebengine.cpp index e45510f3..130b1c60 100644 --- a/src/articleviewer-ng/webengine/articleviewerwebengine.cpp +++ b/src/articleviewer-ng/webengine/articleviewerwebengine.cpp @@ -1,492 +1,486 @@ /* Copyright (C) 2015-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "articleviewerwebengine.h" #include "akregator_debug.h" #include "articleviewerwebenginepage.h" #include "webengine/urlhandlerwebenginemanager.h" #include #include "actionmanager.h" #include "akregatorconfig.h" #include "actions/actions.h" #include "urlhandler/webengine/urlhandlerwebengine.h" #include #include #include -#include #include #include #include #include #include #include #include -#include #include #include -#include #include -#include #include #include #include #include #include -#include -#include #include #include #include #include #include using namespace Akregator; ArticleViewerWebEngine::ArticleViewerWebEngine(KActionCollection *ac, QWidget *parent) : WebEngineViewer::WebEngineView(parent) , mActionCollection(ac) , mLastButtonClicked(LeftButton) , mViewerPluginToolManager(nullptr) { mNetworkAccessManager = new WebEngineViewer::InterceptorManager(this, ac, this); QWebEngineProfile *profile = new QWebEngineProfile(this); mPageEngine = new ArticleViewerWebEnginePage(profile, this); profile->setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); setPage(mPageEngine); connect(this, &ArticleViewerWebEngine::showContextMenu, this, &ArticleViewerWebEngine::slotShowContextMenu); setFocusPolicy(Qt::WheelFocus); connect(mPageEngine, &ArticleViewerWebEnginePage::urlClicked, this, &ArticleViewerWebEngine::slotLinkClicked); mWebEngineViewAccessKey = new WebEngineViewer::WebEngineAccessKey(this, this); mWebEngineViewAccessKey->setActionCollection(mActionCollection); connect(mWebEngineViewAccessKey, &WebEngineViewer::WebEngineAccessKey::openUrl, this, &ArticleViewerWebEngine::slotLinkClicked); connect(this, &ArticleViewerWebEngine::loadStarted, this, &ArticleViewerWebEngine::slotLoadStarted); connect(this, &ArticleViewerWebEngine::loadFinished, this, &ArticleViewerWebEngine::slotLoadFinished); connect(page(), &QWebEnginePage::linkHovered, this, &ArticleViewerWebEngine::slotLinkHovered); setContextMenuPolicy(Qt::DefaultContextMenu); mWebShortcutMenuManager = new KIO::KUriFilterSearchProviderActions(this); mShareServiceManager = new PimCommon::ShareServiceUrlManager(this); connect(mShareServiceManager, &PimCommon::ShareServiceUrlManager::serviceUrlSelected, this, &ArticleViewerWebEngine::slotServiceUrlSelected); connect(page(), &QWebEnginePage::audioMutedChanged, this, &ArticleViewerWebEngine::slotWebPageMutedOrAudibleChanged); connect(page(), &QWebEnginePage::recentlyAudibleChanged, this, &ArticleViewerWebEngine::slotWebPageMutedOrAudibleChanged); connect(phishingDatabase(), &WebEngineViewer::LocalDataBaseManager::checkUrlFinished, this, &ArticleViewerWebEngine::slotCheckedUrlFinished); } ArticleViewerWebEngine::~ArticleViewerWebEngine() { } void ArticleViewerWebEngine::execPrintPreviewPage(QPrinter *printer, int timeout) { mPageEngine->execPrintPreviewPage(printer, timeout); } void ArticleViewerWebEngine::slotWebPageMutedOrAudibleChanged() { Q_EMIT webPageMutedOrAudibleChanged(page()->isAudioMuted(), page()->recentlyAudible()); } QVariantHash ArticleViewerWebEngine::introductionData() const { QVariantHash data; data[QStringLiteral("icon")] = QStringLiteral("akregator"); data[QStringLiteral("name")] = i18n("Akregator"); data[QStringLiteral("subtitle")] = i18n("Akregator is a KDE news feed reader."); data[QStringLiteral("version")] = KAboutData::applicationData().version(); return data; } void ArticleViewerWebEngine::showAboutPage() { paintAboutScreen(QStringLiteral(":/about/introduction_akregator.html"), introductionData()); } void ArticleViewerWebEngine::paintAboutScreen(const QString &templateName, const QVariantHash &data) { GrantleeTheme::ThemeManager manager(QStringLiteral("splashPage"), QStringLiteral("splash.theme"), nullptr, QStringLiteral("messageviewer/about/")); GrantleeTheme::Theme theme = manager.theme(QStringLiteral("default")); if (theme.isValid()) { setHtml(theme.render(templateName, data, QByteArrayLiteral("akregator")), QUrl::fromLocalFile(theme.absolutePath() + QLatin1Char('/'))); } else { qCDebug(AKREGATOR_LOG) << "Theme error: failed to find splash theme"; } } void ArticleViewerWebEngine::slotServiceUrlSelected(PimCommon::ShareServiceUrlManager::ServiceType type) { if (mCurrentUrl.isEmpty()) { return; } const QUrl url = mShareServiceManager->generateServiceUrl(mCurrentUrl.url(), QString(), type); mShareServiceManager->openUrl(url); } void ArticleViewerWebEngine::slotSaveLinkAs() { QUrl tmp(mCurrentUrl); if (tmp.fileName().isEmpty()) { tmp = tmp.adjusted(QUrl::RemoveFilename); tmp.setPath(tmp.path() + QLatin1String("index.html")); } KParts::BrowserRun::simpleSave(tmp, tmp.fileName()); } void ArticleViewerWebEngine::slotSaveImageOnDiskInFrame() { slotSaveLinkAs(); } void ArticleViewerWebEngine::slotCopyImageLocationInFrame() { slotCopyLinkAddress(); } void ArticleViewerWebEngine::slotMute(bool mute) { page()->setAudioMuted(mute); } void ArticleViewerWebEngine::slotCopyLinkAddress() { if (mCurrentUrl.isEmpty()) { return; } QClipboard *cb = QApplication::clipboard(); cb->setText(mCurrentUrl.toString(), QClipboard::Clipboard); // don't set url to selection as it's a no-no according to a fd.o spec // which spec? Nobody seems to care (tested Firefox (3.5.10) Konqueror,and KMail (4.2.3)), so I re-enable the following line unless someone gives // a good reason to remove it again (bug 183022) --Frank cb->setText(mCurrentUrl.toString(), QClipboard::Selection); } void ArticleViewerWebEngine::contextMenuEvent(QContextMenuEvent *e) { displayContextMenu(e->pos()); } void ArticleViewerWebEngine::slotShowContextMenu(const QPoint &pos) { displayContextMenu(pos); } void ArticleViewerWebEngine::slotCopy() { triggerPageAction(QWebEnginePage::Copy); } void ArticleViewerWebEngine::slotLoadFinished() { restoreCurrentPosition(); unsetCursor(); clearRelativePosition(); } void ArticleViewerWebEngine::slotLoadStarted() { mWebEngineViewAccessKey->hideAccessKeys(); setCursor(Qt::WaitCursor); } void ArticleViewerWebEngine::slotWebHitFinished(const WebEngineViewer::WebHitTestResult &result) { mCurrentUrl = result.linkUrl(); if (URLHandlerWebEngineManager::instance()->handleContextMenuRequest(mCurrentUrl, mapToGlobal(result.pos()), this)) { return; } QMenu popup(this); const bool contentSelected = !selectedText().isEmpty(); if (!contentSelected) { if (!mCurrentUrl.isEmpty()) { popup.addAction(createOpenLinkInNewTabAction(mCurrentUrl, this, SLOT(slotOpenLinkInForegroundTab()), &popup)); popup.addAction(createOpenLinkInExternalBrowserAction(mCurrentUrl, this, SLOT(slotOpenLinkInBrowser()), &popup)); popup.addSeparator(); popup.addAction(mActionCollection->action(QStringLiteral("savelinkas"))); popup.addAction(mActionCollection->action(QStringLiteral("copylinkaddress"))); } if (!result.imageUrl().isEmpty()) { popup.addSeparator(); popup.addAction(mActionCollection->action(QStringLiteral("copy_image_location"))); popup.addAction(mActionCollection->action(QStringLiteral("saveas_imageurl"))); } popup.addSeparator(); popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl)); popup.addSeparator(); popup.addAction(mShareServiceManager->menu()); } else { popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_copy"))); popup.addSeparator(); mWebShortcutMenuManager->setSelectedText(page()->selectedText()); mWebShortcutMenuManager->addWebShortcutsToMenu(&popup); popup.addSeparator(); popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection)); } popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_print"))); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_printpreview"))); popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_mute"))); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_unmute"))); popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("find_in_messages"))); if (KPIMTextEdit::TextToSpeech::self()->isReady()) { popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("speak_text"))); } popup.exec(mapToGlobal(result.pos())); } void ArticleViewerWebEngine::displayContextMenu(const QPoint &pos) { WebEngineViewer::WebHitTest *webHit = mPageEngine->hitTestContent(pos); connect(webHit, &WebEngineViewer::WebHitTest::finished, this, &ArticleViewerWebEngine::slotWebHitFinished); } void ArticleViewerWebEngine::slotLinkHovered(const QString &link) { QString msg = URLHandlerWebEngineManager::instance()->statusBarMessage(QUrl(link), this); if (msg.isEmpty()) { msg = link; } Q_EMIT showStatusBarMessage(msg); } void ArticleViewerWebEngine::forwardKeyReleaseEvent(QKeyEvent *e) { if (Settings::self()->accessKeyEnabled()) { mWebEngineViewAccessKey->keyReleaseEvent(e); } } void ArticleViewerWebEngine::forwardKeyPressEvent(QKeyEvent *e) { if (e && hasFocus()) { if (Settings::self()->accessKeyEnabled()) { mWebEngineViewAccessKey->keyPressEvent(e); } } } void ArticleViewerWebEngine::forwardWheelEvent(QWheelEvent *e) { if (Settings::self()->accessKeyEnabled()) { mWebEngineViewAccessKey->wheelEvent(e); } if (QApplication::keyboardModifiers() & Qt::ControlModifier) { const int numDegrees = e->delta() / 8; const int numSteps = numDegrees / 15; const qreal factor = ActionManager::getInstance()->zoomActionMenu()->zoomFactor() + numSteps * 10; if (factor >= 10 && factor <= 300) { ActionManager::getInstance()->zoomActionMenu()->setZoomFactor(factor); ActionManager::getInstance()->zoomActionMenu()->setWebViewerZoomFactor(factor / 100.0); } e->accept(); } } void ArticleViewerWebEngine::resizeEvent(QResizeEvent *e) { if (Settings::self()->accessKeyEnabled()) { mWebEngineViewAccessKey->resizeEvent(e); } QWebEngineView::resizeEvent(e); } void ArticleViewerWebEngine::disableIntroduction() { KGuiItem yesButton(KStandardGuiItem::yes()); yesButton.setText(i18n("Disable")); KGuiItem noButton(KStandardGuiItem::no()); noButton.setText(i18n("Keep Enabled")); if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to disable this introduction page?"), i18n("Disable Introduction Page"), yesButton, noButton) == KMessageBox::Yes) { Settings::self()->setDisableIntroduction(true); Settings::self()->save(); } } void ArticleViewerWebEngine::setArticleAction(ArticleViewerWebEngine::ArticleAction type, const QString &articleId, const QString &feed) { Q_EMIT articleAction(type, articleId, feed); } void ArticleViewerWebEngine::restoreCurrentPosition() { mPageEngine->runJavaScript(WebEngineViewer::WebEngineScript::scrollToRelativePosition(relativePosition())); } void ArticleViewerWebEngine::forwardMouseReleaseEvent(QMouseEvent *event) { if (event->button() & Qt::RightButton) { Q_EMIT showContextMenu(event->pos()); mLastButtonClicked = RightButton; } else if (event->button() & Qt::MiddleButton) { mLastButtonClicked = MiddleButton; } else if (event->button() & Qt::LeftButton) { mLastButtonClicked = LeftButton; } } bool ArticleViewerWebEngine::urlIsAMalwareButContinue() { if (KMessageBox::No == KMessageBox::warningYesNo(this, i18n("This web site is a malware, do you want to continue to show it?"), i18n("Malware"))) { return false; } return true; } void ArticleViewerWebEngine::slotCheckedUrlFinished(const QUrl &url, WebEngineViewer::CheckPhishingUrlUtil::UrlStatus status) { switch (status) { case WebEngineViewer::CheckPhishingUrlUtil::BrokenNetwork: KMessageBox::error(this, i18n("The network is broken."), i18n("Check Phishing Url")); break; case WebEngineViewer::CheckPhishingUrlUtil::InvalidUrl: KMessageBox::error(this, i18n("The url %1 is not valid.", url.toString()), i18n("Check Phishing Url")); break; case WebEngineViewer::CheckPhishingUrlUtil::Ok: break; case WebEngineViewer::CheckPhishingUrlUtil::MalWare: if (!urlIsAMalwareButContinue()) { return; } break; case WebEngineViewer::CheckPhishingUrlUtil::Unknown: qCWarning(AKREGATOR_LOG) << "ArticleViewerWebEngine::slotCheckedUrlFinished unknown error "; break; } openSafeUrl(url); } void ArticleViewerWebEngine::slotLinkClicked(const QUrl &url) { if (URLHandlerWebEngineManager::instance()->handleClick(url, this)) { return; } if (Settings::checkPhishingUrl()) { phishingDatabase()->checkUrl(url); } else { openSafeUrl(url); } } void ArticleViewerWebEngine::openSafeUrl(const QUrl &url) { mCurrentUrl = url; OpenUrlRequest req(mCurrentUrl); if (mLastButtonClicked == LeftButton) { switch (Settings::lMBBehaviour()) { case Settings::EnumLMBBehaviour::OpenInExternalBrowser: req.setOptions(OpenUrlRequest::ExternalBrowser); break; case Settings::EnumLMBBehaviour::OpenInBackground: req.setOpenInBackground(true); req.setOptions(OpenUrlRequest::NewTab); break; default: break; } } else if (mLastButtonClicked == MiddleButton) { switch (Settings::mMBBehaviour()) { case Settings::EnumMMBBehaviour::OpenInExternalBrowser: req.setOptions(OpenUrlRequest::ExternalBrowser); break; case Settings::EnumMMBBehaviour::OpenInBackground: req.setOpenInBackground(true); req.setOptions(OpenUrlRequest::NewTab); break; default: break; } } Q_EMIT signalOpenUrlRequest(req); } void ArticleViewerWebEngine::slotOpenLinkInForegroundTab() { OpenUrlRequest req(mCurrentUrl); req.setOptions(OpenUrlRequest::NewTab); Q_EMIT signalOpenUrlRequest(req); } void ArticleViewerWebEngine::slotOpenLinkInBackgroundTab() { OpenUrlRequest req(mCurrentUrl); req.setOptions(OpenUrlRequest::NewTab); req.setOpenInBackground(true); Q_EMIT signalOpenUrlRequest(req); } void ArticleViewerWebEngine::slotOpenLinkInBrowser() { OpenUrlRequest req(mCurrentUrl); req.setOptions(OpenUrlRequest::ExternalBrowser); Q_EMIT signalOpenUrlRequest(req); } void ArticleViewerWebEngine::createViewerPluginToolManager(KActionCollection *ac, QWidget *parent) { mViewerPluginToolManager = new MessageViewer::ViewerPluginToolManager(parent, this); mViewerPluginToolManager->setActionCollection(ac); mViewerPluginToolManager->setPluginName(QStringLiteral("akregator")); mViewerPluginToolManager->setServiceTypeName(QStringLiteral("Akregator/ViewerPlugin")); if (!mViewerPluginToolManager->initializePluginList()) { qCWarning(AKREGATOR_LOG) << " Impossible to initialize plugins"; } mViewerPluginToolManager->createView(); connect(mViewerPluginToolManager, &MessageViewer::ViewerPluginToolManager::activatePlugin, this, &ArticleViewerWebEngine::slotActivatePlugin); } QList ArticleViewerWebEngine::viewerPluginActionList(MessageViewer::ViewerPluginInterface::SpecificFeatureTypes features) { if (mViewerPluginToolManager) { return mViewerPluginToolManager->viewerPluginActionList(features); } return QList(); } void ArticleViewerWebEngine::slotActivatePlugin(MessageViewer::ViewerPluginInterface *interface) { const QString text = selectedText(); if (!text.isEmpty()) { interface->setText(text); } interface->setUrl(mCurrentUrl); interface->execute(); } diff --git a/src/articleviewer-ng/webengine/articleviewerwebenginewidgetng.cpp b/src/articleviewer-ng/webengine/articleviewerwebenginewidgetng.cpp index 7ccbad0c..2b9c2454 100644 --- a/src/articleviewer-ng/webengine/articleviewerwebenginewidgetng.cpp +++ b/src/articleviewer-ng/webengine/articleviewerwebenginewidgetng.cpp @@ -1,205 +1,204 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "articleviewerwebenginewidgetng.h" #include "akregator_debug.h" #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include using namespace Akregator; template struct InvokeWrapper { R *receiver; void (C::*memberFun)(Arg); void operator()(Arg result) { (receiver->*memberFun)(result); } }; template InvokeWrapper invoke(R *receiver, void (C::*memberFun)(Arg)) { InvokeWrapper wrapper = {receiver, memberFun}; return wrapper; } ArticleViewerWebEngineWidgetNg::ArticleViewerWebEngineWidgetNg(ArticleViewerWebEngine *customViewer, KActionCollection *ac, QWidget *parent) : QWidget(parent) , mCurrentPrinter(nullptr) { mArticleViewerNg = customViewer; initializeLayout(ac); } ArticleViewerWebEngineWidgetNg::ArticleViewerWebEngineWidgetNg(KActionCollection *ac, QWidget *parent) : QWidget(parent) , mArticleViewerNg(nullptr) , mCurrentPrinter(nullptr) { initializeLayout(ac); } void ArticleViewerWebEngineWidgetNg::initializeLayout(KActionCollection *ac) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); mTextToSpeechWidget = new KPIMTextEdit::TextToSpeechWidget(this); mTextToSpeechWidget->setObjectName(QStringLiteral("texttospeechwidget")); layout->addWidget(mTextToSpeechWidget); if (!mArticleViewerNg) { mArticleViewerNg = new ArticleViewerWebEngine(ac, this); } mArticleViewerNg->setObjectName(QStringLiteral("articleviewerng")); layout->addWidget(mArticleViewerNg); mArticleViewerNg->createViewerPluginToolManager(ac, this); mSliderContainer = new KPIMTextEdit::SlideContainer(this); mSliderContainer->setObjectName(QStringLiteral("slidercontainer")); mFindBarWebView = new WebEngineViewer::FindBarWebEngineView(mArticleViewerNg, this); mFindBarWebView->setObjectName(QStringLiteral("findbarwebview")); connect(mFindBarWebView, &WebEngineViewer::FindBarWebEngineView::hideFindBar, mSliderContainer, &KPIMTextEdit::SlideContainer::slideOut); mSliderContainer->setContent(mFindBarWebView); layout->addWidget(mSliderContainer); connect(articleViewerNg(), &ArticleViewerWebEngine::textToSpeech, this, &ArticleViewerWebEngineWidgetNg::slotSpeakText); connect(articleViewerNg(), &ArticleViewerWebEngine::findTextInHtml, this, &ArticleViewerWebEngineWidgetNg::slotFind); } ArticleViewerWebEngineWidgetNg::~ArticleViewerWebEngineWidgetNg() { } ArticleViewerWebEngine *ArticleViewerWebEngineWidgetNg::articleViewerNg() const { return mArticleViewerNg; } void ArticleViewerWebEngineWidgetNg::slotFind() { if (mArticleViewerNg->hasSelection()) { mFindBarWebView->setText(mArticleViewerNg->selectedText()); } mSliderContainer->slideIn(); mFindBarWebView->focusAndSetCursor(); } void ArticleViewerWebEngineWidgetNg::slotSpeakText() { const QString text = mArticleViewerNg->selectedText(); mTextToSpeechWidget->say(text); } void ArticleViewerWebEngineWidgetNg::saveCurrentPosition() { mArticleViewerNg->saveRelativePosition(); } void ArticleViewerWebEngineWidgetNg::slotPrint() { printRequested(mArticleViewerNg->page()); } void ArticleViewerWebEngineWidgetNg::printRequested(QWebEnginePage *page) { if (mCurrentPrinter) { return; } mCurrentPrinter = new QPrinter(); QPointer dialog = new QPrintDialog(mCurrentPrinter, this); dialog->setWindowTitle(i18n("Print Document")); if (dialog->exec() != QDialog::Accepted) { slotHandlePagePrinted(false); delete dialog; return; } delete dialog; page->print(mCurrentPrinter, invoke(this, &ArticleViewerWebEngineWidgetNg::slotHandlePagePrinted)); } void ArticleViewerWebEngineWidgetNg::slotHandlePagePrinted(bool result) { Q_UNUSED(result); delete mCurrentPrinter; mCurrentPrinter = nullptr; } void ArticleViewerWebEngineWidgetNg::slotPrintPreview() { QPrintPreviewDialog *dialog = new QPrintPreviewDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->resize(800, 750); connect(dialog, &QPrintPreviewDialog::paintRequested, this, [=](QPrinter *printing) { QApplication::setOverrideCursor(Qt::WaitCursor); mArticleViewerNg->execPrintPreviewPage(printing, 10*1000); QApplication::restoreOverrideCursor(); }); dialog->open(); } void ArticleViewerWebEngineWidgetNg::slotOpenInBrowser() { const QUrl currentUrl(mArticleViewerNg->url()); if (currentUrl.isLocalFile()) { WebEngineViewer::WebEngineExportHtmlPageJob *job = new WebEngineViewer::WebEngineExportHtmlPageJob; job->setEngineView(mArticleViewerNg); connect(job, &WebEngineViewer::WebEngineExportHtmlPageJob::failed, this, &ArticleViewerWebEngineWidgetNg::slotExportHtmlPageFailed); connect(job, &WebEngineViewer::WebEngineExportHtmlPageJob::success, this, &ArticleViewerWebEngineWidgetNg::slotExportHtmlPageSuccess); job->start(); } else { KRun::RunFlags flags; flags |= KRun::RunExecutables; KRun::runUrl(currentUrl, QStringLiteral("text/html"), this, flags); } } void ArticleViewerWebEngineWidgetNg::slotExportHtmlPageFailed() { qCDebug(AKREGATOR_LOG) << " Failed to export as HTML"; } void ArticleViewerWebEngineWidgetNg::slotExportHtmlPageSuccess(const QString &filename) { const QUrl url(QUrl::fromLocalFile(filename)); KRun::RunFlags flags; flags |= KRun::DeleteTemporaryFiles; KRun::runUrl(url, QStringLiteral("text/html"), this, flags); } diff --git a/src/articleviewerwidget.cpp b/src/articleviewerwidget.cpp index 96c00b3d..ff2f7200 100644 --- a/src/articleviewerwidget.cpp +++ b/src/articleviewerwidget.cpp @@ -1,423 +1,418 @@ /* This file is part of Akregator. Copyright (C) 2004 Teemu Rytilahti 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "articleviewerwidget.h" #include "akregatorconfig.h" #include "aboutdata.h" #include "actionmanager.h" #include "actions.h" #include "article.h" #include "articleformatter.h" #include "articlejobs.h" #include "articlematcher.h" #include "feed.h" #include "folder.h" #include "treenode.h" #include "utils.h" #include "openurlrequest.h" #include "akregator_debug.h" #include "akregator-version.h" -#include #include -#include -#include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include -#include #include #include #include using namespace Akregator; using namespace Akregator::Filters; ArticleViewerWidget::ArticleViewerWidget(const QString &grantleeDirectory, KActionCollection *ac, QWidget *parent) : QWidget(parent) , m_imageDir(QUrl::fromLocalFile(QString(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/akregator/Media/")))) , m_node(0) , m_viewMode(NormalView) , m_articleViewerWidgetNg(new Akregator::ArticleViewerWebEngineWidgetNg(ac, this)) , m_grantleeDirectory(grantleeDirectory) { QGridLayout *layout = new QGridLayout(this); layout->setMargin(0); layout->addWidget(m_articleViewerWidgetNg); m_articleHtmlWriter = new Akregator::ArticleHtmlWebEngineWriter(m_articleViewerWidgetNg->articleViewerNg(), this); connect(m_articleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::signalOpenUrlRequest, this, &ArticleViewerWidget::signalOpenUrlRequest); connect(m_articleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::showStatusBarMessage, this, &ArticleViewerWidget::showStatusBarMessage); } ArticleViewerWidget::~ArticleViewerWidget() { } QSharedPointer ArticleViewerWidget::normalViewFormatter() { if (!m_normalViewFormatter.data()) { m_normalViewFormatter = QSharedPointer(new DefaultNormalViewFormatter(m_grantleeDirectory, m_imageDir, m_articleViewerWidgetNg->articleViewerNg())); } return m_normalViewFormatter; } QSharedPointer ArticleViewerWidget::combinedViewFormatter() { if (!m_combinedViewFormatter.data()) { m_combinedViewFormatter = QSharedPointer(new DefaultCombinedViewFormatter(m_grantleeDirectory, m_imageDir, m_articleViewerWidgetNg->articleViewerNg())); } return m_combinedViewFormatter; } void ArticleViewerWidget::slotZoomChangeInFrame(qreal value) { m_articleViewerWidgetNg->articleViewerNg()->setZoomFactor(value); } void ArticleViewerWidget::slotCopy() { m_articleViewerWidgetNg->articleViewerNg()->slotCopy(); } void ArticleViewerWidget::slotSelectionChanged() { ActionManager::getInstance()->action(QStringLiteral("viewer_copy"))->setEnabled(!m_articleViewerWidgetNg->articleViewerNg()->selectedText().isEmpty()); } void ArticleViewerWidget::slotPrint() { m_articleViewerWidgetNg->slotPrint(); } void ArticleViewerWidget::slotPrintPreview() { m_articleViewerWidgetNg->slotPrintPreview(); } void ArticleViewerWidget::connectToNode(TreeNode *node) { if (node) { if (m_viewMode == CombinedView) { connect(node, &TreeNode::signalChanged, this, &ArticleViewerWidget::slotUpdateCombinedView); connect(node, &TreeNode::signalArticlesAdded, this, &ArticleViewerWidget::slotArticlesAdded); connect(node, &TreeNode::signalArticlesRemoved, this, &ArticleViewerWidget::slotArticlesRemoved); connect(node, &TreeNode::signalArticlesUpdated, this, &ArticleViewerWidget::slotArticlesUpdated); } else if (m_viewMode == SummaryView) { connect(node, &TreeNode::signalChanged, this, &ArticleViewerWidget::slotShowSummary); } connect(node, &TreeNode::signalDestroyed, this, &ArticleViewerWidget::slotClear); } } void ArticleViewerWidget::disconnectFromNode(TreeNode *node) { if (node) { node->disconnect(this); } } void ArticleViewerWidget::renderContent(const QString &text) { m_currentText = text; reload(); } void ArticleViewerWidget::beginWriting() { m_articleHtmlWriter->begin(); } void ArticleViewerWidget::endWriting() { m_articleHtmlWriter->end(); } void ArticleViewerWidget::slotShowSummary(TreeNode *node) { m_viewMode = SummaryView; if (!node) { slotClear(); return; } if (node != m_node) { disconnectFromNode(m_node); connectToNode(node); m_node = node; } const QString summary = normalViewFormatter()->formatSummary(node); m_link.clear(); renderContent(summary); setArticleActionsEnabled(false); } void ArticleViewerWidget::showArticle(const Akregator::Article &article) { if (article.isNull() || article.isDeleted()) { slotClear(); return; } m_viewMode = NormalView; disconnectFromNode(m_node); m_article = article; m_node = 0; m_link = article.link(); if (article.feed()->loadLinkedWebsite()) { openUrl(article.link()); } else { renderContent(normalViewFormatter()->formatArticles(QVector() << article, ArticleFormatter::ShowIcon)); } setArticleActionsEnabled(true); } bool ArticleViewerWidget::openUrl(const QUrl &url) { if (!m_article.isNull() && m_article.feed()->loadLinkedWebsite()) { m_articleViewerWidgetNg->articleViewerNg()->load(url); } else { reload(); } return true; } void ArticleViewerWidget::setFilters(const std::vector< QSharedPointer > &filters) { if (filters == m_filters) { return; } m_filters = filters; slotUpdateCombinedView(); } void ArticleViewerWidget::slotUpdateCombinedView() { if (m_viewMode != CombinedView) { return; } if (!m_node) { return slotClear(); } m_articleViewerWidgetNg->saveCurrentPosition(); QString text; int num = 0; QTime spent; spent.start(); const std::vector< QSharedPointer >::const_iterator filterEnd = m_filters.cend(); QVector
articles; for (const Article &i : qAsConst(m_articles)) { if (i.isDeleted()) { continue; } auto func = [i](const QSharedPointer &matcher) -> bool { return !matcher->matches(i); }; if (std::find_if(m_filters.cbegin(), filterEnd, func) != filterEnd) { continue; } articles << i; ++num; } text = combinedViewFormatter()->formatArticles(articles, ArticleFormatter::NoIcon); qCDebug(AKREGATOR_LOG) << "Combined view rendering: (" << num << " articles):" << "generating HTML:" << spent.elapsed() << "ms"; renderContent(text); qCDebug(AKREGATOR_LOG) << "HTML rendering:" << spent.elapsed() << "ms"; } void ArticleViewerWidget::slotArticlesUpdated(TreeNode * /*node*/, const QVector
& /*list*/) { if (m_viewMode == CombinedView) { //TODO slotUpdateCombinedView(); } } void ArticleViewerWidget::slotArticlesAdded(TreeNode * /*node*/, const QVector
&list) { if (m_viewMode == CombinedView) { //TODO sort list, then merge m_articles << list; std::sort(m_articles.begin(), m_articles.end()); slotUpdateCombinedView(); } } void ArticleViewerWidget::slotArticlesRemoved(TreeNode * /*node*/, const QVector
&list) { Q_UNUSED(list) if (m_viewMode == CombinedView) { //TODO slotUpdateCombinedView(); } } void ArticleViewerWidget::slotClear() { disconnectFromNode(m_node); m_node = 0; m_article = Article(); m_articles.clear(); renderContent(QString()); } void ArticleViewerWidget::showNode(TreeNode *node) { m_viewMode = CombinedView; if (node != m_node) { disconnectFromNode(m_node); } connectToNode(node); m_articles.clear(); m_article = Article(); m_node = node; delete m_listJob; m_listJob = node->createListJob(); connect(m_listJob.data(), &ArticleListJob::finished, this, &ArticleViewerWidget::slotArticlesListed); m_listJob->start(); slotUpdateCombinedView(); } qreal ArticleViewerWidget::zoomFactor() const { return m_articleViewerWidgetNg->articleViewerNg()->zoomFactor(); } void ArticleViewerWidget::slotArticlesListed(KJob *job) { Q_ASSERT(job); Q_ASSERT(job == m_listJob); TreeNode *node = m_listJob->node(); if (job->error() || !node) { if (!node) { qCWarning(AKREGATOR_LOG) << "Node to be listed is already deleted"; } else { qCWarning(AKREGATOR_LOG) << job->errorText(); } slotUpdateCombinedView(); return; } m_articles = m_listJob->articles(); std::sort(m_articles.begin(), m_articles.end()); if (node && !m_articles.isEmpty()) { m_link = m_articles.first().link(); } else { m_link = QUrl(); } slotUpdateCombinedView(); } void ArticleViewerWidget::keyPressEvent(QKeyEvent *e) { e->ignore(); } void ArticleViewerWidget::updateAfterConfigChanged() { switch (m_viewMode) { case NormalView: if (!m_article.isNull()) { renderContent(normalViewFormatter()->formatArticles(QVector() << m_article, ArticleFormatter::ShowIcon)); } break; case CombinedView: slotUpdateCombinedView(); break; case SummaryView: slotShowSummary(m_node); break; } } void ArticleViewerWidget::reload() { beginWriting(); m_articleHtmlWriter->queue(m_currentText); endWriting(); } QSize ArticleViewerWidget::sizeHint() const { // Increase height a bit so that we can (roughly) read 25 lines of text QSize sh = QWidget::sizeHint(); sh.setHeight(qMax(sh.height(), 25 * fontMetrics().height())); return sh; } void ArticleViewerWidget::displayAboutPage() { m_articleViewerWidgetNg->articleViewerNg()->showAboutPage(); } void ArticleViewerWidget::setArticleActionsEnabled(bool enabled) { ActionManager::getInstance()->setArticleActionsEnabled(enabled); } Akregator::ArticleViewerWebEngineWidgetNg *ArticleViewerWidget::articleViewerWidgetNg() const { return m_articleViewerWidgetNg; } diff --git a/src/crashwidget/autotests/crashwidgettest.cpp b/src/crashwidget/autotests/crashwidgettest.cpp index 86e8ece3..d61e2264 100644 --- a/src/crashwidget/autotests/crashwidgettest.cpp +++ b/src/crashwidget/autotests/crashwidgettest.cpp @@ -1,32 +1,31 @@ /* Copyright (C) 2016 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "crashwidgettest.h" -#include CrashWidgetTest::CrashWidgetTest(QObject *parent) : QObject(parent) { } CrashWidgetTest::~CrashWidgetTest() { } QTEST_MAIN(CrashWidgetTest) diff --git a/src/feed/feed.h b/src/feed/feed.h index 6c5cfe19..540b3aae 100644 --- a/src/feed/feed.h +++ b/src/feed/feed.h @@ -1,302 +1,301 @@ /* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #ifndef AKREGATOR_FEED_H #define AKREGATOR_FEED_H #include "akregator_export.h" #include "treenode.h" #include #include -#include class QDomElement; class QString; namespace Akregator { class Article; class FetchQueue; class TreeNodeVisitor; class ArticleDeleteJob; namespace Backend { class Storage; } /** represents a feed */ class AKREGATOR_EXPORT Feed : public TreeNode { friend class ::Akregator::Article; friend class ::Akregator::Folder; Q_OBJECT public: /** the archiving modes */ enum ArchiveMode { globalDefault, /**< use default from Settings (default) */ keepAllArticles, /**< Don't delete any articles */ disableArchiving, /**< Don't save any articles except articles with keep flag set (equal to maxArticleNumber() == 0) */ limitArticleNumber, /**< Save maxArticleNumber() articles, plus the ones with keep flag set */ limitArticleAge /**< Save articles not older than maxArticleAge() (or keep flag set) */ }; // class methods /** converts strings to ArchiveMode value if parsing fails, it returns ArchiveMode::globalDefault */ static ArchiveMode stringToArchiveMode(const QString &str); /** converts ArchiveMode values to corresponding strings */ static QString archiveModeToString(ArchiveMode mode); /** creates a Feed object from a description in OPML format */ static Feed *fromOPML(QDomElement e, Akregator::Backend::Storage *storage); /** default constructor */ explicit Feed(Akregator::Backend::Storage *storage); ~Feed(); bool accept(TreeNodeVisitor *visitor) override; /** exports the feed settings to OPML */ QDomElement toOPML(QDomElement parent, QDomDocument document) const override; /** returns whether this feed uses its own fetch interval or the global setting @return @c true iff this feed has a custom fetch interval */ bool useCustomFetchInterval() const; /** set if the feed has its custom fetch interval or uses the global setting @param enabled @c true: use custom interval, @c false: use global default */ void setCustomFetchIntervalEnabled(bool enabled); // FIXME is it -1 or 0 to disable interval fetching? /** Returns custom auto fetch interval of this feed. @return custom fetch interval in minutes, 0 if disabled */ int fetchInterval() const; /** Sets custom auto fetch interval. @param interval interval in minutes, -1 for disabling auto fetching */ void setFetchInterval(int interval); /** returns the archiving mode which is used for this feed */ ArchiveMode archiveMode() const; /** sets the archiving mode for this feed */ void setArchiveMode(ArchiveMode archiveMode); /** returns the maximum age of articles used for expiration by age (used in @c limitArticleAge archive mode) @return expiry age in days */ int maxArticleAge() const; /** sets the maximum age of articles used for expiration by age (used in @c limitArticleAge archive mode) @param maxArticleAge expiry age in days */ void setMaxArticleAge(int maxArticleAge); /** returns the article count limit used in @c limitArticleNumber archive mode **/ int maxArticleNumber() const; /** sets the article count limit used in @c limitArticleNumber archive mode **/ void setMaxArticleNumber(int maxArticleNumber); /** if @c true, new articles are marked immediately as read instead of new/unread. Useful for high-traffic feeds. */ bool markImmediatelyAsRead() const; void setMarkImmediatelyAsRead(bool enabled); void setUseNotification(bool enabled); bool useNotification() const; /** if true, the linked URL is loaded directly in the article viewer instead of showing the description */ void setLoadLinkedWebsite(bool enabled); bool loadLinkedWebsite() const; /** returns the feed image */ QPixmap image() const; /** sets the feed image */ void setImage(const QPixmap &p); /** returns the url of the actual feed source (rss/rdf/atom file) */ QString xmlUrl() const; /** sets the url of the actual feed source (rss/rdf/atom file) */ void setXmlUrl(const QString &s); /** returns the URL of the HTML page of this feed */ QString htmlUrl() const; /** sets the URL of the HTML page of this feed */ void setHtmlUrl(const QString &s); /** returns the description of this feed */ QString description() const; /** sets the description of this feed */ void setDescription(const QString &s); /** returns article by guid * @param guid the guid of the article to be returned * @return the article object with the given guid, or a * null article if non-existent */ Article findArticle(const QString &guid) const; /** returns whether a fetch error has occurred */ bool fetchErrorOccurred() const; Syndication::ErrorCode fetchErrorCode() const; /** returns the unread count for this feed */ int unread() const override; /** returns the number of total articles in this feed @return number of articles */ int totalCount() const override; /** returns if the article archive of this feed is loaded */ bool isArticlesLoaded() const; /** returns if this node is a feed group (@c false here) */ bool isGroup() const override { return false; } //impl bool isAggregation() const override { return false; } /** returns the next node in the tree. Calling next() unless it returns 0 iterates through the tree in pre-order */ const TreeNode *next() const override; TreeNode *next() override; //impl QIcon icon() const override; /** deletes expired articles */ void deleteExpiredArticles(Akregator::ArticleDeleteJob *job); bool isFetching() const; QVector feeds() const override; QVector feeds() override; QVector folders() const override; QVector folders() override; KJob *createMarkAsReadJob() override; public Q_SLOTS: /** starts fetching */ void fetch(bool followDiscovery = false); void slotAbortFetch(); /** add this feed to the fetch queue @c queue */ void slotAddToFetchQueue(Akregator::FetchQueue *queue, bool intervalFetchOnly = false) override; void slotAddFeedIconListener(); Q_SIGNALS: /** emitted when fetching started */ void fetchStarted(Akregator::Feed *); /** emitted when feed finished fetching */ void fetched(Akregator::Feed *); /** emitted when a fetch error occurred */ void fetchError(Akregator::Feed *); /** emitted when a feed URL was found by auto discovery */ void fetchDiscovery(Akregator::Feed *); /** emitted when a fetch is aborted */ void fetchAborted(Akregator::Feed *); private: Akregator::Backend::Storage *storage(); private: void setFavicon(const QIcon &icon); void loadFavicon(const QUrl &url); QVector
articles() override; /** loads articles from archive **/ void loadArticles(); void enforceLimitArticleNumber(); void recalcUnreadCount(); void doArticleNotification() override; /** sets the unread count for this feed */ void setUnread(int unread); /** notifies that article @c mya was set to "deleted". To be called by @ref Article */ void setArticleDeleted(Article &a); /** notifies that article @c mya was changed @param oldStatus if the status was changed, it contains the old status, -1 otherwise To be called by @ref Article */ void setArticleChanged(Article &a, int oldStatus = -1); void appendArticles(const Syndication::FeedPtr &feed); /** appends article @c a to the article list */ void appendArticle(const Article &a); /** checks whether article @c a is expired (considering custom and global archive mode settings) */ bool isExpired(const Article &a) const; /** returns @c true if either this article uses @c limitArticleAge as custom setting or uses the global default, which is @c limitArticleAge */ bool usesExpiryByAge() const; /** executes the actual fetch action */ void tryFetch(); void markAsFetchedNow(); private Q_SLOTS: void fetchCompleted(Syndication::Loader *loader, Syndication::FeedPtr doc, Syndication::ErrorCode errorCode); void slotImageFetched(const QPixmap &image); private: class Private; Private *d; }; } // namespace Akregator #endif // AKREGATOR_FEED_H diff --git a/src/feed/feedpropertiesdialog.cpp b/src/feed/feedpropertiesdialog.cpp index 0d1ac77d..4a6b31a5 100644 --- a/src/feed/feedpropertiesdialog.cpp +++ b/src/feed/feedpropertiesdialog.cpp @@ -1,349 +1,348 @@ /* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "feedpropertiesdialog.h" #include "akregatorconfig.h" #include #include #include -#include #include #include #include using namespace Akregator; FeedPropertiesWidget::FeedPropertiesWidget(QWidget *parent, const QString &name) : QWidget(parent) { setObjectName(name); setupUi(this); connect(cb_updateInterval, &QCheckBox::toggled, updateSpinBox, &QSpinBox::setEnabled); connect(cb_updateInterval, &QCheckBox::toggled, updateComboBox, &KComboBox::setEnabled); connect(cb_updateInterval, &QCheckBox::toggled, updateLabel, &QLabel::setEnabled); connect(cb_updateInterval, &QCheckBox::toggled, this, &FeedPropertiesWidget::slotUpdateCheckBoxToggled); connect(updateComboBox, QOverload::of(&KComboBox::activated), this, &FeedPropertiesWidget::slotUpdateComboBoxActivated); connect(updateSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &FeedPropertiesWidget::slotUpdateComboBoxLabels); connect(rb_limitArticleAge, &QRadioButton::toggled, sb_maxArticleAge, &KPluralHandlingSpinBox::setEnabled); connect(rb_limitArticleNumber, &QRadioButton::toggled, sb_maxArticleNumber, &KPluralHandlingSpinBox::setEnabled); } FeedPropertiesWidget::~FeedPropertiesWidget() { } void FeedPropertiesWidget::slotUpdateComboBoxActivated(int index) { updateSpinBox->setEnabled(index != Never); } void FeedPropertiesWidget::slotUpdateComboBoxLabels(int value) { updateComboBox->setItemText(FeedPropertiesWidget::Minutes, i18np("Minute", "Minutes", value)); updateComboBox->setItemText(FeedPropertiesWidget::Hours, i18np("Hour", "Hours", value)); updateComboBox->setItemText(FeedPropertiesWidget::Days, i18np("Day", "Days", value)); } void FeedPropertiesWidget::slotUpdateCheckBoxToggled(bool enabled) { updateSpinBox->setEnabled(enabled && updateComboBox->currentIndex() != Never); } FeedPropertiesDialog::FeedPropertiesDialog(QWidget *parent, const QString &name) : QDialog(parent) , m_feed(nullptr) { setObjectName(name); setWindowTitle(i18n("Feed Properties")); widget = new FeedPropertiesWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &FeedPropertiesDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &FeedPropertiesDialog::reject); buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); setModal(true); mainLayout->addWidget(widget); mainLayout->addWidget(buttonBox); widget->feedNameEdit->setFocus(); widget->updateComboBox->insertItem(FeedPropertiesWidget::Minutes, i18np("Minute", "Minutes", 0)); widget->updateComboBox->insertItem(FeedPropertiesWidget::Hours, i18np("Hour", "Hours", 0)); widget->updateComboBox->insertItem(FeedPropertiesWidget::Days, i18np("Day", "Days", 0)); widget->updateComboBox->insertItem(FeedPropertiesWidget::Never, i18nc("never fetch new articles", "Never")); widget->sb_maxArticleAge->setSuffix(ki18np(" day", " days")); widget->sb_maxArticleNumber->setSuffix(ki18np(" article", " articles")); connect(widget->feedNameEdit, &QLineEdit::textChanged, this, &FeedPropertiesDialog::slotSetWindowTitle); } FeedPropertiesDialog::~FeedPropertiesDialog() { } void FeedPropertiesDialog::accept() { m_feed->setNotificationMode(false); m_feed->setTitle(feedName()); m_feed->setXmlUrl(url()); m_feed->setCustomFetchIntervalEnabled(autoFetch()); if (autoFetch()) { m_feed->setFetchInterval(fetchInterval()); } m_feed->setArchiveMode(archiveMode()); m_feed->setMaxArticleAge(maxArticleAge()); m_feed->setMaxArticleNumber(maxArticleNumber()); m_feed->setMarkImmediatelyAsRead(markImmediatelyAsRead()); m_feed->setUseNotification(useNotification()); m_feed->setLoadLinkedWebsite(loadLinkedWebsite()); m_feed->setNotificationMode(true); QDialog::accept(); } void FeedPropertiesDialog::slotSetWindowTitle(const QString &title) { setWindowTitle(title.isEmpty() ? i18n("Feed Properties") : i18n("Properties of %1", title)); } void FeedPropertiesDialog::setFeed(Feed *feed) { m_feed = feed; if (!feed) { return; } setFeedName(feed->title()); setUrl(feed->xmlUrl()); setAutoFetch(feed->useCustomFetchInterval()); if (feed->useCustomFetchInterval()) { setFetchInterval(feed->fetchInterval()); } else { setFetchInterval(Settings::autoFetchInterval()); } setArchiveMode(feed->archiveMode()); setMaxArticleAge(feed->maxArticleAge()); setMaxArticleNumber(feed->maxArticleNumber()); setMarkImmediatelyAsRead(feed->markImmediatelyAsRead()); setUseNotification(feed->useNotification()); setLoadLinkedWebsite(feed->loadLinkedWebsite()); slotSetWindowTitle(feedName()); } QString FeedPropertiesDialog::feedName() const { return widget->feedNameEdit->text(); } QString FeedPropertiesDialog::url() const { return widget->urlEdit->text(); } bool FeedPropertiesDialog::autoFetch() const { return widget->cb_updateInterval->isChecked(); } int FeedPropertiesDialog::fetchInterval() const { switch (widget->updateComboBox->currentIndex()) { case FeedPropertiesWidget::Minutes: return widget->updateSpinBox->value(); case FeedPropertiesWidget::Hours: return widget->updateSpinBox->value() * 60; case FeedPropertiesWidget::Days: return widget->updateSpinBox->value() * 60 * 24; case FeedPropertiesWidget::Never: default: return -1; // never } } Feed::ArchiveMode FeedPropertiesDialog::archiveMode() const { // i could check the button group's int, but order could change... if (widget->rb_globalDefault->isChecked()) { return Feed::globalDefault; } if (widget->rb_keepAllArticles->isChecked()) { return Feed::keepAllArticles; } if (widget->rb_limitArticleAge->isChecked()) { return Feed::limitArticleAge; } if (widget->rb_limitArticleNumber->isChecked()) { return Feed::limitArticleNumber; } if (widget->rb_disableArchiving->isChecked()) { return Feed::disableArchiving; } // in a perfect world, this is never reached return Feed::globalDefault; } int FeedPropertiesDialog::maxArticleAge() const { return widget->sb_maxArticleAge->value(); } int FeedPropertiesDialog::maxArticleNumber() const { return widget->sb_maxArticleNumber->value(); } void FeedPropertiesDialog::setArchiveMode(Feed::ArchiveMode mode) { switch (mode) { case Feed::globalDefault: widget->rb_globalDefault->setChecked(true); break; case Feed::keepAllArticles: widget->rb_keepAllArticles->setChecked(true); break; case Feed::disableArchiving: widget->rb_disableArchiving->setChecked(true); break; case Feed::limitArticleAge: widget->rb_limitArticleAge->setChecked(true); break; case Feed::limitArticleNumber: widget->rb_limitArticleNumber->setChecked(true); } } void FeedPropertiesDialog::setFeedName(const QString &title) { widget->feedNameEdit->setText(title); } void FeedPropertiesDialog::setUrl(const QString &url) { widget->urlEdit->setText(url); } void FeedPropertiesDialog::setAutoFetch(bool customFetchEnabled) { widget->cb_updateInterval->setChecked(customFetchEnabled); widget->updateComboBox->setEnabled(customFetchEnabled); if (widget->updateSpinBox->value() > -1) { widget->updateSpinBox->setEnabled(customFetchEnabled); } else { widget->updateSpinBox->setEnabled(false); } } void FeedPropertiesDialog::setFetchInterval(int interval) { if (interval == -1) { // never update widget->updateSpinBox->setValue(0); widget->updateSpinBox->setDisabled(true); widget->updateComboBox->setCurrentIndex(FeedPropertiesWidget::Never); return; } if (interval == 0) { widget->updateSpinBox->setValue(0); widget->updateSpinBox->setEnabled(widget->cb_updateInterval->isChecked()); widget->updateComboBox->setCurrentIndex(FeedPropertiesWidget::Minutes); return; } if (interval % (60 * 24) == 0) { widget->updateSpinBox->setValue(interval / (60 * 24)); widget->updateSpinBox->setEnabled(widget->cb_updateInterval->isChecked()); widget->updateComboBox->setCurrentIndex(FeedPropertiesWidget::Days); return; } if (interval % 60 == 0) { widget->updateSpinBox->setValue(interval / 60); widget->updateSpinBox->setEnabled(widget->cb_updateInterval->isChecked()); widget->updateComboBox->setCurrentIndex(FeedPropertiesWidget::Hours); return; } widget->updateSpinBox->setValue(interval); widget->updateSpinBox->setEnabled(widget->cb_updateInterval->isChecked()); widget->updateComboBox->setCurrentIndex(FeedPropertiesWidget::Minutes); } void FeedPropertiesDialog::setMaxArticleAge(int age) { widget->sb_maxArticleAge->setValue(age); } void FeedPropertiesDialog::setMaxArticleNumber(int number) { widget->sb_maxArticleNumber->setValue(number); } void FeedPropertiesDialog::setMarkImmediatelyAsRead(bool enabled) { widget->checkBox_markRead->setChecked(enabled); } bool FeedPropertiesDialog::markImmediatelyAsRead() const { return widget->checkBox_markRead->isChecked(); } void FeedPropertiesDialog::setUseNotification(bool enabled) { widget->checkBox_useNotification->setChecked(enabled); } bool FeedPropertiesDialog::useNotification() const { return widget->checkBox_useNotification->isChecked(); } bool FeedPropertiesDialog::loadLinkedWebsite() const { return widget->checkBox_loadWebsite->isChecked(); } void FeedPropertiesDialog::setLoadLinkedWebsite(bool enabled) { widget->checkBox_loadWebsite->setChecked(enabled); } void FeedPropertiesDialog::selectFeedName() { widget->feedNameEdit->selectAll(); } diff --git a/src/formatter/articleformatter.cpp b/src/formatter/articleformatter.cpp index 79fb2dc1..e09b4946 100644 --- a/src/formatter/articleformatter.cpp +++ b/src/formatter/articleformatter.cpp @@ -1,83 +1,81 @@ /* This file is part of Akregator. Copyright (C) 2006 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "articleformatter.h" #include "akregatorconfig.h" #include "article.h" #include "feed.h" #include "folder.h" #include "treenode.h" #include "treenodevisitor.h" #include "utils.h" #include -#include -#include #include using namespace Syndication; using namespace Akregator; class Q_DECL_HIDDEN ArticleFormatter::Private { public: explicit Private(); class SummaryVisitor; }; ArticleFormatter::Private::Private() { } ArticleFormatter::ArticleFormatter() : d(new Private()) { } ArticleFormatter::~ArticleFormatter() { delete d; } QString ArticleFormatter::formatEnclosure(const Enclosure &enclosure) { if (enclosure.isNull()) { return QString(); } const QString title = !enclosure.title().isEmpty() ? enclosure.title() : enclosure.url(); const uint length = enclosure.length(); const QString type = enclosure.type(); QString inf; if (!type.isEmpty() && length > 0) { inf = i18n("(%1, %2)", type, KFormat().formatByteSize(length)); } else if (!type.isNull()) { inf = type; } else if (length > 0) { inf = KFormat().formatByteSize(length); } const QString str = QStringLiteral("%2 %3").arg(enclosure.url(), title, inf); return str; } diff --git a/src/formatter/articleformatter.h b/src/formatter/articleformatter.h index 8e335026..a345dcef 100644 --- a/src/formatter/articleformatter.h +++ b/src/formatter/articleformatter.h @@ -1,62 +1,61 @@ /* This file is part of Akregator. Copyright (C) 2006 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #ifndef AKREGATOR_ARTICLEFORMATTER_H #define AKREGATOR_ARTICLEFORMATTER_H -#include #include #include namespace Akregator { class Article; class TreeNode; class ArticleFormatter { public: enum IconOption { NoIcon, ShowIcon }; explicit ArticleFormatter(); virtual ~ArticleFormatter(); virtual QString formatArticles(const QVector
&article, IconOption icon) const = 0; virtual QString formatSummary(TreeNode *node) const = 0; static QString formatEnclosure(const Syndication::Enclosure &enclosure); private: class Private; Private *const d; Q_DISABLE_COPY(ArticleFormatter) }; } // namespace Akregator #endif // AKREGATOR_ARTICLEFORMATTER_H diff --git a/src/formatter/articlegrantleeobject.h b/src/formatter/articlegrantleeobject.h index 78ba2eaf..7eb38b3d 100644 --- a/src/formatter/articlegrantleeobject.h +++ b/src/formatter/articlegrantleeobject.h @@ -1,99 +1,99 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ARTICLEGRANTLEEOBJECT_H #define ARTICLEGRANTLEEOBJECT_H #include #include #include "articleformatter.h" - +#include namespace Akregator { class ArticleGrantleeObject : public QObject { Q_OBJECT Q_PROPERTY(QString strippedTitle READ strippedTitle) Q_PROPERTY(QString author READ author) Q_PROPERTY(QString content READ content) Q_PROPERTY(QString articleLinkUrl READ articleLinkUrl) Q_PROPERTY(QString articlePubDate READ articlePubDate) Q_PROPERTY(QString enclosure READ enclosure) Q_PROPERTY(QString articleCompleteStoryLink READ articleCompleteStoryLink) Q_PROPERTY(QString imageFeed READ imageFeed) Q_PROPERTY(QString deleteAction READ deleteAction) Q_PROPERTY(QString markAsReadAction READ markAsReadAction) Q_PROPERTY(QString markAsUnreadAction READ markAsUnreadAction) Q_PROPERTY(QString markAsImportantAction READ markAsImportantAction) Q_PROPERTY(QString sendUrlAction READ sendUrlAction) Q_PROPERTY(QString sendFileAction READ sendFileAction) Q_PROPERTY(QString openInExternalBrowser READ openInExternalBrowser) Q_PROPERTY(QString openInBackgroundTab READ openInBackgroundTab) Q_PROPERTY(QString share READ share) Q_PROPERTY(QString commentLink READ commentLink) Q_PROPERTY(QString commentNumber READ commentNumber) Q_PROPERTY(Akregator::ArticleGrantleeObject::ArticleStatus articleStatus READ articleStatus) Q_PROPERTY(bool important READ important) public: explicit ArticleGrantleeObject(const QUrl &imageDir, const Article &article, ArticleFormatter::IconOption icon, QObject *parent = nullptr); ~ArticleGrantleeObject(); enum ArticleStatus { Unread = 0, Read, New }; Q_ENUMS(ArticleStatus) Akregator::ArticleGrantleeObject::ArticleStatus articleStatus() const; QString strippedTitle() const; QString author() const; QString content() const; QString articleLinkUrl() const; QString articlePubDate() const; QString enclosure() const; QString articleCompleteStoryLink() const; QString imageFeed() const; //Action QString deleteAction() const; QString markAsReadAction() const; QString markAsUnreadAction() const; QString markAsImportantAction() const; QString sendUrlAction() const; QString sendFileAction() const; QString openInExternalBrowser() const; QString share() const; QString commentLink() const; QString commentNumber() const; QString openInBackgroundTab() const; bool important() const; private: QString createActionUrl(const QString &actionName) const; Article mArticle; ArticleFormatter::IconOption mArticleFormatOption; QUrl mImageDir; int mIconSize; }; } #endif // ARTICLEGRANTLEEOBJECT_H diff --git a/src/formatter/defaultcombinedviewformatter.cpp b/src/formatter/defaultcombinedviewformatter.cpp index ee2c29c5..bc4f485d 100644 --- a/src/formatter/defaultcombinedviewformatter.cpp +++ b/src/formatter/defaultcombinedviewformatter.cpp @@ -1,62 +1,58 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "defaultcombinedviewformatter.h" #include "grantleeviewformatter.h" #include "akregatorconfig.h" #include "akregator_debug.h" #include "article.h" #include "feed.h" #include "folder.h" #include "treenode.h" #include "treenodevisitor.h" #include "utils.h" -#include -#include #include -#include #include -#include using namespace Akregator; DefaultCombinedViewFormatter::DefaultCombinedViewFormatter(const QString &grantleeDirectory, const QUrl &imageDir, QPaintDevice *device) : ArticleFormatter() { const QString combinedPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("akregator/grantleetheme/%1/").arg(grantleeDirectory), QStandardPaths::LocateDirectory); mGrantleeViewFormatter = new GrantleeViewFormatter(QStringLiteral("combinedview.html"), combinedPath, imageDir, device->logicalDpiY()); } DefaultCombinedViewFormatter::~DefaultCombinedViewFormatter() { delete mGrantleeViewFormatter; } QString DefaultCombinedViewFormatter::formatArticles(const QVector
&articles, IconOption icon) const { return mGrantleeViewFormatter->formatArticles(articles, icon); } QString DefaultCombinedViewFormatter::formatSummary(TreeNode *) const { return QString(); } diff --git a/src/formatter/defaultnormalviewformatter.cpp b/src/formatter/defaultnormalviewformatter.cpp index fe3cd987..95bca3b4 100644 --- a/src/formatter/defaultnormalviewformatter.cpp +++ b/src/formatter/defaultnormalviewformatter.cpp @@ -1,106 +1,102 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "defaultnormalviewformatter.h" #include "akregatorconfig.h" #include "grantleeviewformatter.h" #include "article.h" #include "feed.h" #include "folder.h" #include "treenode.h" #include "treenodevisitor.h" #include "utils.h" -#include -#include #include -#include #include -#include using namespace Syndication; using namespace Akregator; class DefaultNormalViewFormatter::SummaryVisitor : public TreeNodeVisitor { public: SummaryVisitor(DefaultNormalViewFormatter *p) : parent(p) { } ~SummaryVisitor() { } bool visitFeed(Feed *node) override { text = parent->mGrantleeViewFormatter->formatFeed(node); return true; } bool visitFolder(Folder *node) override { text = parent->mGrantleeViewFormatter->formatFolder(node); return true; } QString formatSummary(TreeNode *node) { text.clear(); visit(node); return text; } QString text; private: DefaultNormalViewFormatter *parent = nullptr; }; DefaultNormalViewFormatter::DefaultNormalViewFormatter(const QString &grantleeDirectory, const QUrl &imageDir, QPaintDevice *device) : ArticleFormatter() , m_imageDir(imageDir) , m_summaryVisitor(new SummaryVisitor(this)) { m_DefaultThemePath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("akregator/grantleetheme/%1/").arg(grantleeDirectory), QStandardPaths::LocateDirectory); mGrantleeViewFormatter = new GrantleeViewFormatter(QStringLiteral("normalview.html"), m_DefaultThemePath, m_imageDir, device->logicalDpiY()); } DefaultNormalViewFormatter::~DefaultNormalViewFormatter() { delete mGrantleeViewFormatter; delete m_summaryVisitor; } QString DefaultNormalViewFormatter::formatSummary(TreeNode *node) const { return m_summaryVisitor->formatSummary(node); } QString DefaultNormalViewFormatter::formatArticles(const QVector
&articles, IconOption icon) const { if (articles.count() != 1) { return {}; } return mGrantleeViewFormatter->formatArticles(articles, icon); } diff --git a/src/formatter/defaultnormalviewformatter.h b/src/formatter/defaultnormalviewformatter.h index f22b6783..6209eee3 100644 --- a/src/formatter/defaultnormalviewformatter.h +++ b/src/formatter/defaultnormalviewformatter.h @@ -1,49 +1,50 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DEFAULTNORMALVIEWFORMATTER_H #define DEFAULTNORMALVIEWFORMATTER_H #include "articleformatter.h" #include "akregator_export.h" +#include class QPaintDevice; namespace Akregator { class GrantleeViewFormatter; class AKREGATOR_EXPORT DefaultNormalViewFormatter : public ArticleFormatter { public: explicit DefaultNormalViewFormatter(const QString &grantleeDirectory, const QUrl &imageDir, QPaintDevice *device = nullptr); ~DefaultNormalViewFormatter(); QString formatArticles(const QVector
&article, IconOption option) const override; QString formatSummary(TreeNode *node) const override; private: DefaultNormalViewFormatter(); QUrl m_imageDir; QString m_DefaultThemePath; class SummaryVisitor; SummaryVisitor *m_summaryVisitor = nullptr; GrantleeViewFormatter *mGrantleeViewFormatter = nullptr; }; } #endif // DEFAULTNORMALVIEWFORMATTER_H diff --git a/src/formatter/grantleeviewformatter.cpp b/src/formatter/grantleeviewformatter.cpp index d5c1f522..298b3695 100644 --- a/src/formatter/grantleeviewformatter.cpp +++ b/src/formatter/grantleeviewformatter.cpp @@ -1,167 +1,166 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "grantleeviewformatter.h" #include "articlegrantleeobject.h" #include "utils.h" #include "akregatorconfig.h" #include #include #include #include -#include #include #include #include using namespace Akregator; GrantleeViewFormatter::GrantleeViewFormatter(const QString &htmlFileName, const QString &themePath, const QUrl &imageDir, int deviceDpiY, QObject *parent) : PimCommon::GenericGrantleeFormatter(htmlFileName, themePath, parent) , mImageDir(imageDir) , mHtmlArticleFileName(htmlFileName) , mGrantleeThemePath(QStringLiteral("file://") + themePath + QLatin1Char('/')) , mDeviceDpiY(deviceDpiY) { mDirectionString = QApplication::isRightToLeft() ? QStringLiteral("rtl") : QStringLiteral("ltr"); } GrantleeViewFormatter::~GrantleeViewFormatter() { } int GrantleeViewFormatter::pointsToPixel(int pointSize) const { return (pointSize * mDeviceDpiY + 36) / 72; } void GrantleeViewFormatter::addStandardObject(QVariantHash &grantleeObject) { grantleeObject.insert(QStringLiteral("absoluteThemePath"), mGrantleeThemePath); grantleeObject.insert(QStringLiteral("applicationDir"), mDirectionString); grantleeObject.insert(QStringLiteral("standardFamilyFont"), Settings::standardFont()); grantleeObject.insert(QStringLiteral("mediumFontSize"), pointsToPixel(Settings::mediumFontSize())); } QString GrantleeViewFormatter::formatFeed(Akregator::Feed *feed) { setDefaultHtmlMainFile(QStringLiteral("defaultnormalvisitfeed.html")); if (!errorMessage().isEmpty()) { return errorMessage(); } QVariantHash feedObject; addStandardObject(feedObject); feedObject.insert(QStringLiteral("strippedTitle"), Utils::stripTags(feed->title())); QString numberOfArticle; if (feed->unread() == 0) { numberOfArticle = i18n(" (no unread articles)"); } else { numberOfArticle = i18np(" (1 unread article)", " (%1 unread articles)", feed->unread()); } feedObject.insert(QStringLiteral("feedCount"), numberOfArticle); QString feedImage; if (!feed->image().isNull()) { // image feedImage = QLatin1String("
"); QString file = Utils::fileNameForUrl(feed->xmlUrl()); QUrl u(mImageDir); u = u.adjusted(QUrl::RemoveFilename); u.setPath(u.path() + file); feedImage = QStringLiteral("\n").arg(feed->htmlUrl(), u.url()); } else { feedImage = QStringLiteral("
"); } feedObject.insert(QStringLiteral("feedImage"), feedImage); if (!feed->description().isEmpty()) { QString feedDescription; feedDescription = QStringLiteral("
").arg(mDirectionString); feedDescription += i18n("Description: %1
", feed->description()); feedDescription += QStringLiteral("
"); // /description feedObject.insert(QStringLiteral("feedDescription"), feedDescription); } if (!feed->htmlUrl().isEmpty()) { QString feedHomePage; feedHomePage = QStringLiteral("
").arg(mDirectionString); feedHomePage += i18n("Homepage: %2", feed->htmlUrl(), feed->htmlUrl()); feedHomePage += QStringLiteral("
"); // / link feedObject.insert(QStringLiteral("feedHomePage"), feedHomePage); } return render(feedObject); } QString GrantleeViewFormatter::formatFolder(Akregator::Folder *node) { setDefaultHtmlMainFile(QStringLiteral("defaultnormalvisitfolder.html")); if (!errorMessage().isEmpty()) { return errorMessage(); } QVariantHash folderObject; addStandardObject(folderObject); folderObject.insert(QStringLiteral("nodeTitle"), node->title()); QString numberOfArticle; if (node->unread() == 0) { numberOfArticle = i18n(" (no unread articles)"); } else { numberOfArticle = i18np(" (1 unread article)", " (%1 unread articles)", node->unread()); } folderObject.insert(QStringLiteral("nodeCount"), numberOfArticle); return render(folderObject); } QString GrantleeViewFormatter::formatArticles(const QVector
&article, ArticleFormatter::IconOption icon) { setDefaultHtmlMainFile(mHtmlArticleFileName); if (!errorMessage().isEmpty()) { return errorMessage(); } QVariantHash articleObject; QVariantList articlesList; const int nbArticles(article.count()); articlesList.reserve(nbArticles); QList lstObj; lstObj.reserve(nbArticles); for (int i = 0; i < nbArticles; ++i) { ArticleGrantleeObject *articleObj = new ArticleGrantleeObject(mImageDir, article.at(i), icon); articlesList << QVariant::fromValue(static_cast(articleObj)); lstObj.append(articleObj); } articleObject.insert(QStringLiteral("articles"), articlesList); addStandardObject(articleObject); articleObject.insert(QStringLiteral("dateI18n"), i18n("Date")); articleObject.insert(QStringLiteral("commentI18n"), i18n("Comment")); articleObject.insert(QStringLiteral("completeStoryI18n"), i18n("Complete Story")); articleObject.insert(QStringLiteral("authorI18n"), i18n("Author")); articleObject.insert(QStringLiteral("enclosureI18n"), i18n("Enclosure")); const QString str = render(articleObject); qDeleteAll(lstObj); return str; } diff --git a/src/formatter/grantleeviewformatter.h b/src/formatter/grantleeviewformatter.h index aa2619e2..1d9573a3 100644 --- a/src/formatter/grantleeviewformatter.h +++ b/src/formatter/grantleeviewformatter.h @@ -1,50 +1,50 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef GRANTLEEVIEWFORMATTER_H #define GRANTLEEVIEWFORMATTER_H #include "PimCommon/GenericGrantleeFormatter" #include "article.h" #include "articleformatter.h" - +#include namespace Akregator { class Folder; class GrantleeViewFormatter : public PimCommon::GenericGrantleeFormatter { Q_OBJECT public: explicit GrantleeViewFormatter(const QString &htmlFileName, const QString &themePath, const QUrl &imageDir, int deviceDpiY, QObject *parent = nullptr); ~GrantleeViewFormatter(); QString formatArticles(const QVector
&article, ArticleFormatter::IconOption icon); QString formatFolder(Akregator::Folder *node); QString formatFeed(Akregator::Feed *feed); private: void addStandardObject(QVariantHash &grantleeObject); int pointsToPixel(int pointSize) const; QUrl mImageDir; QString mHtmlArticleFileName; QString mDirectionString; QString mGrantleeThemePath; int mDeviceDpiY; }; } #endif // GRANTLEEVIEWFORMATTER_H diff --git a/src/frame/frame.cpp b/src/frame/frame.cpp index afe0806e..7f9392ba 100644 --- a/src/frame/frame.cpp +++ b/src/frame/frame.cpp @@ -1,206 +1,204 @@ /* This file is part of Akregator. Copyright (C) 2004 Sashmit Bhaduri This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "frame.h" #include "utils.h" -#include #include #include "akregator_debug.h" #include -#include #include using namespace Akregator; Frame::Frame(QWidget *parent) : QWidget(parent) { m_title = i18n("Untitled"); m_state = Idle; m_progress = -1; m_progressItem = 0; m_isRemovable = true; m_loading = false; m_id = m_idCounter++; } void Frame::slotSetTitle(const QString &s) { if (m_title != s) { m_title = s; Q_EMIT signalTitleChanged(this, s); } } bool Frame::isLoading() const { return m_loading; } void Frame::slotSetCaption(const QString &s) { if (m_progressItem) { m_progressItem->setLabel(s); } m_caption = s; Q_EMIT signalCaptionChanged(this, s); } void Frame::slotSetStatusText(const QString &s) { m_statusText = s; m_statusText = Akregator::Utils::stripTags(m_statusText); Q_EMIT signalStatusText(this, m_statusText); } void Frame::slotSetProgress(int a) { if (m_progressItem) { m_progressItem->setProgress((int)a); } m_progress = a; Q_EMIT signalLoadingProgress(this, a); } void Frame::slotSetState(State state) { m_state = state; switch (m_state) { case Frame::Started: Q_EMIT signalStarted(this); break; case Frame::Canceled: Q_EMIT signalCanceled(this, QString()); break; case Frame::Idle: case Frame::Completed: default: Q_EMIT signalCompleted(this); } } int Frame::m_idCounter = 0; int Frame::id() const { return m_id; } void Frame::setRemovable(bool removable) { m_isRemovable = removable; } bool Frame::isRemovable() const { return m_isRemovable; } Frame::~Frame() { if (m_progressItem) { m_progressItem->setComplete(); } } Frame::State Frame::state() const { return m_state; } QString Frame::title() const { return m_title; } QString Frame::caption() const { return m_caption; } QString Frame::statusText() const { return m_statusText; } QIcon Frame::icon() const { return m_icon; } void Frame::setIcon(const QIcon &icon) { m_icon = icon; } void Frame::slotSetStarted() { m_loading = true; if (m_progressId.isNull() || m_progressId.isEmpty()) { m_progressId = KPIM::ProgressManager::getUniqueID(); } m_progressItem = KPIM::ProgressManager::createProgressItem(m_progressId, title(), QString(), false); m_progressItem->setStatus(i18n("Loading...")); m_state = Started; Q_EMIT signalStarted(this); } void Frame::slotStop() { if (m_loading) { m_loading = false; } } void Frame::slotSetCanceled(const QString &s) { m_loading = false; if (m_progressItem) { m_progressItem->setStatus(i18n("Loading canceled")); m_progressItem->setComplete(); m_progressItem = 0; } m_state = Canceled; Q_EMIT signalCanceled(this, s); } void Frame::slotSetCompleted() { m_loading = false; if (m_progressItem) { m_progressItem->setStatus(i18n("Loading completed")); m_progressItem->setComplete(); m_progressItem = 0; } m_state = Completed; Q_EMIT signalCompleted(this); } int Frame::progress() const { return m_progress; } diff --git a/src/frame/framemanager.cpp b/src/frame/framemanager.cpp index d07c90f3..0782abaa 100644 --- a/src/frame/framemanager.cpp +++ b/src/frame/framemanager.cpp @@ -1,289 +1,287 @@ /* This file is part of Akregator. Copyright (C) 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "framemanager.h" #include "config-akregator.h" #include "akregatorconfig.h" #include "frame.h" #include "actionmanager.h" #include "openurlrequest.h" #include -#include #include #include #include #include #include "akregator_debug.h" #include #include -#include #include using namespace Akregator; FrameManager::FrameManager(QObject *parent) : QObject(parent) , m_currentFrame(nullptr) { } FrameManager::~FrameManager() { } Frame *FrameManager::currentFrame() const { return m_currentFrame; } void FrameManager::slotAddFrame(Frame *frame) { m_frames.insert(frame->id(), frame); connect(frame, &Frame::signalCanceled, this, &FrameManager::slotSetCanceled); connect(frame, &Frame::signalStarted, this, &FrameManager::slotSetStarted); connect(frame, &Frame::signalCaptionChanged, this, &FrameManager::slotSetCaption); connect(frame, &Frame::signalLoadingProgress, this, &FrameManager::slotSetProgress); connect(frame, &Frame::signalCompleted, this, &FrameManager::slotSetCompleted); connect(frame, &Frame::signalTitleChanged, this, &FrameManager::slotSetTitle); connect(frame, &Frame::signalStatusText, this, &FrameManager::slotSetStatusText); connect(frame, SIGNAL(signalOpenUrlRequest(Akregator::OpenUrlRequest&)), this, SLOT(slotOpenUrlRequest(Akregator::OpenUrlRequest&))); //setPartGuiActive(frame->part(), false); Q_EMIT signalFrameAdded(frame); if (m_frames.count() == 1) { slotChangeFrame(frame->id()); } } void FrameManager::slotRemoveFrame(int id) { Frame *frame = m_frames.value(id); if (!frame || !frame->isRemovable()) { return; } frame->disconnect(this); if (m_currentFrame == frame) { slotChangeFrame(-1); } m_frames.insert(id, 0); m_frames.remove(id); Q_EMIT signalFrameRemoved(id); delete frame; } Frame *FrameManager::findFrameById(int id) const { return m_frames.value(id); } void FrameManager::slotChangeFrame(int frameId) { Frame *frame = m_frames.value(frameId); if (frame == m_currentFrame) { return; } Frame *oldFrame = m_currentFrame; m_currentFrame = frame; //if (oldFrame) { // setPartGuiActive(oldFrame->part(), false); //} if (frame) { //setPartGuiActive(frame->part(), true); // TODO: handle removable flag switch (frame->state()) { case Frame::Started: Q_EMIT signalStarted(); break; case Frame::Canceled: Q_EMIT signalCanceled(QString()); break; case Frame::Idle: case Frame::Completed: default: Q_EMIT signalCompleted(); } Q_EMIT signalCaptionChanged(frame->caption()); Q_EMIT signalTitleChanged(frame->title()); Q_EMIT signalLoadingProgress(frame->progress()); Q_EMIT signalStatusText(frame->statusText()); } else { Q_EMIT signalCompleted(); Q_EMIT signalCaptionChanged(QString()); Q_EMIT signalTitleChanged(QString()); Q_EMIT signalLoadingProgress(100); Q_EMIT signalStatusText(QString()); } Q_EMIT signalCurrentFrameChanged(oldFrame, frame); } void FrameManager::slotSetStarted(Frame *frame) { if (frame == m_currentFrame) { Q_EMIT signalStarted(); } } void FrameManager::slotSetCanceled(Frame *frame, const QString &reason) { if (frame == m_currentFrame) { Q_EMIT signalCanceled(reason); } } void FrameManager::slotSetCompleted(Frame *frame) { if (frame == m_currentFrame) { Q_EMIT signalCompleted(); } } void FrameManager::slotSetProgress(Frame *frame, int progress) { if (frame == m_currentFrame) { Q_EMIT signalLoadingProgress(progress); } } void FrameManager::slotSetCaption(Frame *frame, const QString &caption) { if (frame == m_currentFrame) { Q_EMIT signalCaptionChanged(caption); } } void FrameManager::slotSetTitle(Frame *frame, const QString &title) { if (frame == m_currentFrame) { Q_EMIT signalTitleChanged(title); } } void FrameManager::slotSetStatusText(Frame *frame, const QString &statusText) { if (frame == m_currentFrame) { Q_EMIT signalStatusText(statusText); } } void FrameManager::openUrl(OpenUrlRequest &request) { if (request.browserArgs().newTab() || request.browserArgs().forcesNewWindow() || request.options() == OpenUrlRequest::NewTab || (m_currentFrame->id() == 0)) { int newFrameId = -1; Q_EMIT signalRequestNewFrame(newFrameId); request.setFrameId(newFrameId); } else { request.setFrameId(m_currentFrame->id()); } if (m_frames.contains(request.frameId())) { Frame *frame = m_frames.value(request.frameId()); if (frame->openUrl(request)) { request.setWasHandled(true); } } if (!request.openInBackground()) { Q_EMIT signalSelectFrame(request.frameId()); } } void FrameManager::openInExternalBrowser(const OpenUrlRequest &request) { QUrl url = request.url(); if (!url.isValid()) { return; } if (!Settings::externalBrowserUseKdeDefault()) { QHash map; map.insert(QLatin1Char('u'), url.url()); const QString cmd = KMacroExpander::expandMacrosShellQuote(Settings::externalBrowserCustomCommand(), map); const QStringList args = KShell::splitArgs(cmd); if (!args.isEmpty()) { KProcess::startDetached(args); return; } } if (request.args().mimeType().isEmpty()) { QDesktopServices::openUrl(url); } else { KRun::runUrl(url, request.args().mimeType(), 0 /*window*/, false, false); } } void FrameManager::slotOpenUrlRequest(OpenUrlRequest &request, bool useOpenInBackgroundSetting) { qCDebug(AKREGATOR_LOG) << "FrameManager::slotOpenUrlRequest():" << request.debugInfo(); if (request.options() == OpenUrlRequest::ExternalBrowser) { openInExternalBrowser(request); return; } if (useOpenInBackgroundSetting) { // Honour user's preference for foreground/background tabs if (request.options() == OpenUrlRequest::NewTab || request.browserArgs().newTab()) { request.setOpenInBackground(Settings::lMBBehaviour() == Settings::EnumLMBBehaviour::OpenInBackground); } } openUrl(request); } void FrameManager::saveProperties(KConfigGroup &config) { //write children QStringList strlst; QHash::const_iterator i; QHash::const_iterator end(m_frames.constEnd()); for (i = m_frames.constBegin(); i != end; ++i) { // No need to save the main frame Frame *currentFrame = i.value(); if (currentFrame && qobject_cast(currentFrame)) { QString newPrefix = QLatin1Char('T') + QString::number(i.key()); if (currentFrame->saveConfig(config, newPrefix + QLatin1Char('_'))) { strlst.append(newPrefix); if (currentFrame == m_currentFrame) { config.writeEntry(QStringLiteral("CurrentTab"), newPrefix); } } } } config.writeEntry(QStringLiteral("Children"), strlst); } diff --git a/src/frame/webengine/akrwebengineviewer.cpp b/src/frame/webengine/akrwebengineviewer.cpp index dbf0c7e8..e06fa76c 100644 --- a/src/frame/webengine/akrwebengineviewer.cpp +++ b/src/frame/webengine/akrwebengineviewer.cpp @@ -1,154 +1,152 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "akrwebengineviewer.h" #include "articleviewer-ng/webengine/articleviewerwebenginepage.h" #include "akregatorconfig.h" #include "actionmanager.h" #include "actions.h" #include "webengine/urlhandlerwebenginemanager.h" #include - +#include #include #include -#include -#include #include #include #include #include #include #include #include using namespace Akregator; AkrWebEngineViewer::AkrWebEngineViewer(KActionCollection *ac, QWidget *parent) : ArticleViewerWebEngine(ac, parent) { settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); settings()->setAttribute(QWebEngineSettings::PluginsEnabled, false); settings()->setAttribute(QWebEngineSettings::AutoLoadImages, true); settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false); settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, false); settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, false); settings()->setAttribute(QWebEngineSettings::XSSAuditingEnabled, false); settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, false); settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, false); settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, false); } AkrWebEngineViewer::~AkrWebEngineViewer() { } void AkrWebEngineViewer::contextMenuEvent(QContextMenuEvent *e) { displayContextMenu(e->pos()); } void AkrWebEngineViewer::slotWebHitFinished(const WebEngineViewer::WebHitTestResult &result) { mCurrentUrl = result.linkUrl(); if (URLHandlerWebEngineManager::instance()->handleContextMenuRequest(mCurrentUrl, mapToGlobal(result.pos()), this)) { return; } QMenu popup(this); QWebEngineHistory *history = page()->history(); bool needSeparator = false; if (history->canGoBack()) { popup.addAction(pageAction(QWebEnginePage::Back)); needSeparator = true; } if (history->canGoForward()) { popup.addAction(pageAction(QWebEnginePage::Forward)); needSeparator = true; } if (needSeparator) { popup.addSeparator(); } popup.addAction(pageAction(QWebEnginePage::Stop)); popup.addSeparator(); popup.addAction(pageAction(QWebEnginePage::Reload)); popup.addSeparator(); const bool contentSelected = !selectedText().isEmpty(); if (!contentSelected) { if (!mCurrentUrl.isEmpty()) { popup.addAction(createOpenLinkInNewTabAction(mCurrentUrl, this, SLOT(slotOpenLinkInForegroundTab()), &popup)); popup.addAction(createOpenLinkInExternalBrowserAction(mCurrentUrl, this, SLOT(slotOpenLinkInBrowser()), &popup)); popup.addSeparator(); popup.addAction(mActionCollection->action(QStringLiteral("savelinkas"))); popup.addAction(mActionCollection->action(QStringLiteral("copylinkaddress"))); } if (!result.imageUrl().isEmpty()) { popup.addSeparator(); popup.addAction(mActionCollection->action(QStringLiteral("copy_image_location"))); popup.addAction(mActionCollection->action(QStringLiteral("saveas_imageurl"))); } popup.addSeparator(); popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedUrl)); popup.addSeparator(); popup.addAction(mShareServiceManager->menu()); } else { popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_copy"))); popup.addSeparator(); mWebShortcutMenuManager->setSelectedText(page()->selectedText()); mWebShortcutMenuManager->addWebShortcutsToMenu(&popup); popup.addSeparator(); popup.addActions(viewerPluginActionList(MessageViewer::ViewerPluginInterface::NeedSelection)); } popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_print"))); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("viewer_printpreview"))); popup.addSeparator(); popup.addAction(pageAction(QWebEnginePage::SavePage)); popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_mute"))); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("tab_unmute"))); const QList interceptorUrlActions = mNetworkAccessManager->interceptorUrlActions(result); if (!interceptorUrlActions.isEmpty()) { popup.addSeparator(); popup.addActions(interceptorUrlActions); } popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("find_in_messages"))); if (KPIMTextEdit::TextToSpeech::self()->isReady()) { popup.addSeparator(); popup.addAction(ActionManager::getInstance()->action(QStringLiteral("speak_text"))); } popup.exec(mapToGlobal(result.pos())); } void AkrWebEngineViewer::displayContextMenu(const QPoint &pos) { WebEngineViewer::WebHitTest *webHit = mPageEngine->hitTestContent(pos); connect(webHit, &WebEngineViewer::WebHitTest::finished, this, &AkrWebEngineViewer::slotWebHitFinished); } QWebEngineView *AkrWebEngineViewer::createWindow(QWebEnginePage::WebWindowType type) { Q_UNUSED(type); return this; } diff --git a/src/frame/webengine/webengineframe.cpp b/src/frame/webengine/webengineframe.cpp index ede7847f..f85ddf8c 100644 --- a/src/frame/webengine/webengineframe.cpp +++ b/src/frame/webengine/webengineframe.cpp @@ -1,222 +1,221 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "openurlrequest.h" #include "webengineframe.h" #include "akrwebengineviewer.h" #include -#include #include #include using namespace Akregator; WebEngineFrame::WebEngineFrame(KActionCollection *ac, QWidget *parent) : Frame(parent) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); setRemovable(true); Akregator::AkrWebEngineViewer *viewer = new Akregator::AkrWebEngineViewer(ac, this); mArticleViewerWidgetNg = new Akregator::ArticleViewerWebEngineWidgetNg(viewer, ac, this); connect(mArticleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::titleChanged, this, &WebEngineFrame::slotTitleChanged); connect(mArticleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::loadProgress, this, &WebEngineFrame::slotProgressChanged); connect(mArticleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::signalOpenUrlRequest, this, &WebEngineFrame::signalOpenUrlRequest); connect(mArticleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::loadStarted, this, &WebEngineFrame::slotLoadStarted); connect(mArticleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::loadFinished, this, &WebEngineFrame::slotLoadFinished); connect(mArticleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::showStatusBarMessage, this, &WebEngineFrame::showStatusBarMessage); connect(mArticleViewerWidgetNg->articleViewerNg(), &ArticleViewerWebEngine::webPageMutedOrAudibleChanged, this, &WebEngineFrame::slotWebPageMutedOrAudibleChanged); layout->addWidget(mArticleViewerWidgetNg); } WebEngineFrame::~WebEngineFrame() { } void WebEngineFrame::slotWebPageMutedOrAudibleChanged(bool isAudioMuted, bool wasRecentlyAudible) { Q_EMIT webPageMutedOrAudibleChanged(this, isAudioMuted, wasRecentlyAudible); } void WebEngineFrame::slotLoadFinished() { Q_EMIT signalCompleted(this); } void WebEngineFrame::slotLoadStarted() { Q_EMIT signalStarted(this); } void WebEngineFrame::slotProgressChanged(int progress) { Q_EMIT signalLoadingProgress(this, progress); } void WebEngineFrame::slotTitleChanged(const QString &title) { slotSetTitle(title); } QUrl WebEngineFrame::url() const { return mArticleViewerWidgetNg->articleViewerNg()->url(); } void WebEngineFrame::loadUrl(const QUrl &url) { KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); connect(job, &KIO::FavIconRequestJob::result, this, [job, this](KJob *) { if (!job->error()) { Q_EMIT signalIconChanged(this, QIcon(job->iconFile())); } }); mArticleViewerWidgetNg->articleViewerNg()->load(url); } bool WebEngineFrame::openUrl(const OpenUrlRequest &request) { const QUrl url = request.url(); loadUrl(url); return true; } void WebEngineFrame::loadConfig(const KConfigGroup &config, const QString &prefix) { const QString url = config.readEntry(QStringLiteral("url").prepend(prefix), QString()); const qreal zf = config.readEntry(QStringLiteral("zoom").prepend(prefix), 1.0); mArticleViewerWidgetNg->articleViewerNg()->setZoomFactor(zf); loadUrl(QUrl(url)); } bool WebEngineFrame::saveConfig(KConfigGroup &config, const QString &prefix) { const QString urlPath(url().url()); if (urlPath != QLatin1String("about:blank")) { config.writeEntry(QStringLiteral("url").prepend(prefix), urlPath); config.writeEntry(QStringLiteral("zoom").prepend(prefix), zoomFactor()); return true; } else { return false; } } void WebEngineFrame::slotCopyInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->articleViewerNg()->slotCopy(); } void WebEngineFrame::slotPrintInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->slotPrint(); } void WebEngineFrame::slotPrintPreviewInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->slotPrintPreview(); } void WebEngineFrame::slotFindTextInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->slotFind(); } void WebEngineFrame::slotTextToSpeechInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->slotSpeakText(); } void WebEngineFrame::slotZoomChangeInFrame(int frameId, qreal value) { if (frameId != id()) { return; } mArticleViewerWidgetNg->articleViewerNg()->setZoomFactor(value); } void WebEngineFrame::slotReload() { mArticleViewerWidgetNg->articleViewerNg()->reload(); } void WebEngineFrame::slotStop() { mArticleViewerWidgetNg->articleViewerNg()->stop(); } qreal WebEngineFrame::zoomFactor() const { return mArticleViewerWidgetNg->articleViewerNg()->zoomFactor(); } void WebEngineFrame::slotSaveLinkAsInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->articleViewerNg()->slotSaveLinkAs(); } void WebEngineFrame::slotCopyLinkAsInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->articleViewerNg()->slotCopyLinkAddress(); } void WebEngineFrame::slotSaveImageOnDiskInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->articleViewerNg()->slotSaveImageOnDiskInFrame(); } void WebEngineFrame::slotCopyImageLocationInFrame(int frameId) { if (frameId != id()) { return; } mArticleViewerWidgetNg->articleViewerNg()->slotCopyImageLocationInFrame(); } void WebEngineFrame::slotMute(int frameId, bool mute) { if (frameId != id()) { return; } mArticleViewerWidgetNg->articleViewerNg()->slotMute(mute); } diff --git a/src/frame/webengine/webengineframe.h b/src/frame/webengine/webengineframe.h index 6dceef6f..c3753dcd 100644 --- a/src/frame/webengine/webengineframe.h +++ b/src/frame/webengine/webengineframe.h @@ -1,75 +1,74 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef WEBENGINEFRAME_H #define WEBENGINEFRAME_H #include "frame.h" #include "akregatorpart_export.h" -#include namespace Akregator { class ArticleViewerWebEngineWidgetNg; class AKREGATORPART_EXPORT WebEngineFrame : public Frame { Q_OBJECT public: explicit WebEngineFrame(KActionCollection *ac, QWidget *parent = nullptr); ~WebEngineFrame(); QUrl url() const override; bool openUrl(const OpenUrlRequest &request) override; void loadConfig(const KConfigGroup &, const QString &) override; bool saveConfig(KConfigGroup &, const QString &) override; qreal zoomFactor() const override; public Q_SLOTS: void slotReload() override; void slotStop() override; void slotZoomChangeInFrame(int frameId, qreal value); void slotCopyInFrame(int frameId); void slotPrintInFrame(int frameId); void slotPrintPreviewInFrame(int frameId); void slotFindTextInFrame(int frameId); void slotTextToSpeechInFrame(int frameId); void slotSaveLinkAsInFrame(int frameId); void slotCopyLinkAsInFrame(int frameId); void slotSaveImageOnDiskInFrame(int frameId); void slotCopyImageLocationInFrame(int frameId); void slotMute(int frameId, bool mute); Q_SIGNALS: void signalIconChanged(Akregator::Frame *, const QIcon &icon); void webPageMutedOrAudibleChanged(Akregator::Frame *, bool isAudioMuted, bool wasRecentlyAudible); private Q_SLOTS: void slotTitleChanged(const QString &title); void slotProgressChanged(int progress); void slotLoadStarted(); void slotLoadFinished(); void slotWebPageMutedOrAudibleChanged(bool isAudioMuted, bool wasRecentlyAudible); private: void loadUrl(const QUrl &url); Akregator::ArticleViewerWebEngineWidgetNg *mArticleViewerWidgetNg = nullptr; }; } #endif diff --git a/src/job/downloadarticlejob.cpp b/src/job/downloadarticlejob.cpp index b48fd88a..0fa8dd1c 100644 --- a/src/job/downloadarticlejob.cpp +++ b/src/job/downloadarticlejob.cpp @@ -1,111 +1,110 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "downloadarticlejob.h" #include "akregator_debug.h" #include #include #include #include -#include #include #include using namespace Akregator; DownloadArticleJob::DownloadArticleJob(QObject *parent) : QObject(parent) , mTemporaryFile(nullptr) , mAttachmentTemporaryFile(nullptr) { } DownloadArticleJob::~DownloadArticleJob() { if (mAttachmentTemporaryFile) { mAttachmentTemporaryFile->removeTempFiles(); mAttachmentTemporaryFile = 0; } } void DownloadArticleJob::forceCleanupTemporaryFile() { if (mAttachmentTemporaryFile) { mAttachmentTemporaryFile->forceCleanTempFiles(); delete mAttachmentTemporaryFile; mAttachmentTemporaryFile = 0; } } void DownloadArticleJob::start() { if (mArticleUrl.isEmpty()) { deleteLater(); return; } if (mTemporaryFile) { qCDebug(AKREGATOR_LOG) << " There is a problem as we call start twice"; return; } mTemporaryFile = new QTemporaryFile(this); mTemporaryFile->open(); mTemporaryFile->setAutoRemove(false); mAttachmentTemporaryFile = new MimeTreeParser::AttachmentTemporaryFilesDirs; KIO::Job *job = KIO::file_copy(mArticleUrl, QUrl::fromLocalFile(mTemporaryFile->fileName()), -1, KIO::Overwrite); mAttachmentTemporaryFile->addTempFile(mTemporaryFile->fileName()); connect(job, &KIO::Job::result, this, &DownloadArticleJob::slotUrlSaveResult); } void DownloadArticleJob::setArticleUrl(const QUrl &articleUrl) { mArticleUrl = articleUrl; } void DownloadArticleJob::slotUrlSaveResult(KJob *job) { if (job->error()) { KIO::Job *kiojob = dynamic_cast(job); if (kiojob && kiojob->uiDelegate()) { kiojob->uiDelegate()->showErrorMessage(); } else { qCWarning(AKREGATOR_LOG) << "There is no GUI delegate set for a kjob, and it failed with error:" << job->errorString(); } } else { sendAttachment(); deleteLater(); } } void DownloadArticleJob::setTitle(const QString &title) { mTitle = title; } void DownloadArticleJob::sendAttachment() { QUrlQuery query; query.addQueryItem(QStringLiteral("subject"), mTitle); query.addQueryItem(QStringLiteral("body"), mText); query.addQueryItem(QStringLiteral("attach"), mTemporaryFile->fileName()); QUrl url; url.setScheme(QStringLiteral("mailto")); url.setQuery(query); QDesktopServices::openUrl(url); deleteLater(); } void DownloadArticleJob::setText(const QString &text) { mText = text; } diff --git a/src/mainwidget.cpp b/src/mainwidget.cpp index 46ab9421..d4b943f2 100644 --- a/src/mainwidget.cpp +++ b/src/mainwidget.cpp @@ -1,1341 +1,1339 @@ /* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny 2004 Sashmit Bhaduri 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "mainwidget.h" #include "utils.h" #include "actionmanagerimpl.h" #include "addfeeddialog.h" #include "articlelistview.h" #include "articleviewerwidget.h" #include "abstractselectioncontroller.h" #include "articlejobs.h" #include "articlematcher.h" #include "akregatorconfig.h" #include "akregator_part.h" #include "Libkdepim/BroadcastStatus" #include "createfeedcommand.h" #include "createfoldercommand.h" #include "deletesubscriptioncommand.h" #include "editsubscriptioncommand.h" #include "expireitemscommand.h" #include "importfeedlistcommand.h" #include "feed.h" #include "feedlist.h" #include "feedpropertiesdialog.h" #include "fetchqueue.h" #include "folder.h" #include "framemanager.h" #include "kernel.h" #include "notificationmanager.h" #include "openurlrequest.h" #include "progressmanager.h" #include "widgets/searchbar.h" #include "selectioncontroller.h" #include "subscriptionlistjobs.h" #include "subscriptionlistmodel.h" #include "subscriptionlistview.h" #include "tabwidget.h" #include "treenode.h" #include "treenodevisitor.h" #include "types.h" #include "mainframe.h" #include #include "job/downloadarticlejob.h" -#include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include "articleviewer-ng/webengine/articleviewerwebenginewidgetng.h" using namespace Akregator; MainWidget::~MainWidget() { // if m_shuttingDown is false, slotOnShutdown was not called. That // means that not the whole app is shutdown, only the part. So it // should be no risk to do the cleanups now if (!m_shuttingDown) { slotOnShutdown(); } } MainWidget::MainWidget(Part *part, QWidget *parent, ActionManagerImpl *actionManager, const QString &name) : QWidget(parent) , m_feedList() , m_viewMode(NormalView) , m_actionManager(actionManager) , m_feedListManagementInterface(new FeedListManagementImpl) { setObjectName(name); FeedListManagementInterface::setInstance(m_feedListManagementInterface); m_actionManager->initMainWidget(this); m_part = part; m_shuttingDown = false; m_displayingAboutPage = false; setFocusPolicy(Qt::StrongFocus); QVBoxLayout *lt = new QVBoxLayout(this); lt->setMargin(0); m_horizontalSplitter = new QSplitter(Qt::Horizontal, this); m_horizontalSplitter->setOpaqueResize(true); m_horizontalSplitter->setChildrenCollapsible(false); lt->addWidget(m_horizontalSplitter); connect(Kernel::self()->fetchQueue(), &FetchQueue::signalStarted, this, &MainWidget::slotFetchingStarted); connect(Kernel::self()->fetchQueue(), &FetchQueue::signalStopped, this, &MainWidget::slotFetchingStopped); m_feedListView = new SubscriptionListView(m_horizontalSplitter); m_feedListView->setObjectName(QStringLiteral("feedtree")); m_actionManager->initSubscriptionListView(m_feedListView); connect(m_feedListView, &SubscriptionListView::userActionTakingPlace, this, &MainWidget::ensureArticleTabVisible); m_tabWidget = new TabWidget(m_horizontalSplitter); m_actionManager->initTabWidget(m_tabWidget); connect(m_part, &Part::signalSettingsChanged, this, &MainWidget::slotSettingsChanged); connect(m_tabWidget, &TabWidget::signalCurrentFrameChanged, this, &MainWidget::slotCurrentFrameChanged); connect(m_tabWidget, &TabWidget::signalRemoveFrameRequest, Kernel::self()->frameManager(), &FrameManager::slotRemoveFrame); connect(m_tabWidget, SIGNAL(signalOpenUrlRequest(Akregator::OpenUrlRequest&)), Kernel::self()->frameManager(), SLOT(slotOpenUrlRequest(Akregator::OpenUrlRequest&))); connect(Kernel::self()->frameManager(), &FrameManager::signalFrameAdded, m_tabWidget, &TabWidget::slotAddFrame); connect(Kernel::self()->frameManager(), &FrameManager::signalSelectFrame, m_tabWidget, &TabWidget::slotSelectFrame); connect(Kernel::self()->frameManager(), &FrameManager::signalFrameRemoved, m_tabWidget, &TabWidget::slotRemoveFrame); connect(Kernel::self()->frameManager(), &FrameManager::signalRequestNewFrame, this, &MainWidget::slotRequestNewFrame); connect(Kernel::self()->frameManager(), &FrameManager::signalFrameRemoved, this, &MainWidget::slotFramesChanged); connect(Kernel::self()->frameManager(), &FrameManager::signalCompleted, this, &MainWidget::slotFramesChanged); connect(PimCommon::NetworkManager::self()->networkConfigureManager(), &QNetworkConfigurationManager::onlineStateChanged, this, &MainWidget::slotNetworkStatusChanged); m_tabWidget->setWhatsThis(i18n("You can view multiple articles in several open tabs.")); m_mainTab = new QWidget(this); m_mainTab->setObjectName(QStringLiteral("Article Tab")); m_mainTab->setWhatsThis(i18n("Articles list.")); QVBoxLayout *mainTabLayout = new QVBoxLayout(m_mainTab); mainTabLayout->setMargin(0); m_searchBar = new SearchBar(m_mainTab); if (!Settings::showQuickFilter()) { m_searchBar->hide(); } m_articleSplitter = new QSplitter(Qt::Vertical, m_mainTab); m_articleSplitter->setObjectName(QStringLiteral("panner2")); m_articleSplitter->setChildrenCollapsible(false); m_articleWidget = new QWidget(m_articleSplitter); QVBoxLayout *articleWidgetLayout = new QVBoxLayout; m_articleWidget->setLayout(articleWidgetLayout); articleWidgetLayout->setMargin(0); articleWidgetLayout->setSpacing(0); m_articleListView = new ArticleListView; articleWidgetLayout->addWidget(m_searchBar); articleWidgetLayout->addWidget(m_articleListView); connect(m_articleListView, &ArticleListView::userActionTakingPlace, this, &MainWidget::ensureArticleTabVisible); m_selectionController = new SelectionController(this); m_selectionController->setArticleLister(m_articleListView); m_selectionController->setFeedSelector(m_feedListView); connect(m_searchBar, &SearchBar::signalSearch, m_selectionController, &AbstractSelectionController::setFilters); FolderExpansionHandler *expansionHandler = new FolderExpansionHandler(this); connect(m_feedListView, &QTreeView::expanded, expansionHandler, &FolderExpansionHandler::itemExpanded); connect(m_feedListView, &QTreeView::collapsed, expansionHandler, &FolderExpansionHandler::itemCollapsed); m_selectionController->setFolderExpansionHandler(expansionHandler); connect(m_selectionController, &AbstractSelectionController::currentSubscriptionChanged, this, &MainWidget::slotNodeSelected); connect(m_selectionController, &AbstractSelectionController::currentArticleChanged, this, &MainWidget::slotArticleSelected); connect(m_selectionController, &AbstractSelectionController::articleDoubleClicked, this, &MainWidget::slotOpenArticleInBrowser); m_actionManager->initArticleListView(m_articleListView); connect(m_articleListView, &ArticleListView::signalMouseButtonPressed, this, &MainWidget::slotMouseButtonPressed); m_articleViewer = new ArticleViewerWidget(Settings::grantleeDirectory(), m_actionManager->actionCollection(), m_articleSplitter); m_articleListView->setFocusProxy(m_articleViewer); setFocusProxy(m_articleViewer); connect(m_articleViewer, &ArticleViewerWidget::showStatusBarMessage, this, &MainWidget::slotShowStatusBarMessage); connect(m_articleViewer, SIGNAL(signalOpenUrlRequest(Akregator::OpenUrlRequest&)), Kernel::self()->frameManager(), SLOT(slotOpenUrlRequest(Akregator::OpenUrlRequest&))); connect(m_searchBar, &SearchBar::signalSearch, m_articleViewer, &ArticleViewerWidget::setFilters); mainTabLayout->addWidget(m_articleSplitter); m_mainFrame = new MainFrame(this, m_mainTab); m_mainFrame->slotSetTitle(i18n("Articles")); m_mainFrame->setArticleViewer(m_articleViewer); connect(m_articleViewer->articleViewerWidgetNg()->articleViewerNg(), &ArticleViewerWebEngine::articleAction, this, &MainWidget::slotArticleAction); connect(m_tabWidget, &TabWidget::signalCopyInFrame, m_mainFrame, &MainFrame::slotCopyInFrame); connect(m_tabWidget, &TabWidget::signalPrintInFrame, m_mainFrame, &MainFrame::slotPrintInFrame); connect(m_tabWidget, &TabWidget::signalZoomChangedInFrame, m_mainFrame, &MainFrame::slotZoomChangeInFrame); connect(m_tabWidget, &TabWidget::signalPrintPreviewInFrame, m_mainFrame, &MainFrame::slotPrintPreviewInFrame); connect(m_tabWidget, &TabWidget::signalFindTextInFrame, m_mainFrame, &MainFrame::slotFindTextInFrame); connect(m_tabWidget, &TabWidget::signalTextToSpeechInFrame, m_mainFrame, &MainFrame::slotTextToSpeechInFrame); connect(m_tabWidget, &TabWidget::signalSaveLinkAsInFrame, m_mainFrame, &MainFrame::slotSaveLinkAsInFrame); connect(m_tabWidget, &TabWidget::signalCopyLinkAsInFrame, m_mainFrame, &MainFrame::slotCopyLinkAsInFrame); connect(m_tabWidget, &TabWidget::signalCopyImageLocation, m_mainFrame, &MainFrame::slotCopyImageLocationInFrame); connect(m_tabWidget, &TabWidget::signalSaveImageOnDisk, m_mainFrame, &MainFrame::slotSaveImageOnDiskInFrame); connect(m_tabWidget, &TabWidget::signalMute, m_mainFrame, &MainFrame::slotMute); Kernel::self()->frameManager()->slotAddFrame(m_mainFrame); const QList sp1sizes = Settings::splitter1Sizes(); if (sp1sizes.count() >= m_horizontalSplitter->count()) { m_horizontalSplitter->setSizes(sp1sizes); } const QList sp2sizes = Settings::splitter2Sizes(); if (sp2sizes.count() >= m_articleSplitter->count()) { m_articleSplitter->setSizes(sp2sizes); } if (!Settings::self()->disableIntroduction()) { m_articleWidget->hide(); m_articleViewer->displayAboutPage(); m_mainFrame->slotSetTitle(i18n("About")); m_displayingAboutPage = true; } m_fetchTimer = new QTimer(this); connect(m_fetchTimer, &QTimer::timeout, this, &MainWidget::slotDoIntervalFetches); m_fetchTimer->start(1000 * 60); // delete expired articles once per hour m_expiryTimer = new QTimer(this); connect(m_expiryTimer, &QTimer::timeout, this, &MainWidget::slotDeleteExpiredArticles); m_expiryTimer->start(3600 * 1000); m_markReadTimer = new QTimer(this); m_markReadTimer->setSingleShot(true); connect(m_markReadTimer, &QTimer::timeout, this, &MainWidget::slotSetCurrentArticleReadDelayed); setFeedList(QSharedPointer(new FeedList(Kernel::self()->storage()))); switch (Settings::viewMode()) { case CombinedView: slotCombinedView(); break; case WidescreenView: slotWidescreenView(); break; default: slotNormalView(); } if (!Settings::resetQuickFilterOnNodeChange()) { m_searchBar->slotSetStatus(Settings::statusFilter()); m_searchBar->slotSetText(Settings::textFilter()); } } void MainWidget::slotSettingsChanged() { m_tabWidget->slotSettingsChanged(); m_articleViewer->updateAfterConfigChanged(); } void MainWidget::slotOnShutdown() { disconnect(m_tabWidget, &TabWidget::signalCurrentFrameChanged, this, &MainWidget::slotCurrentFrameChanged); m_shuttingDown = true; // close all pageviewers in a controlled way // fixes bug 91660, at least when no part loading data while (m_tabWidget->count() > 1) { // remove frames until only the main frame remains m_tabWidget->setCurrentIndex(m_tabWidget->count() - 1); // select last page m_tabWidget->slotRemoveCurrentFrame(); } Kernel::self()->fetchQueue()->slotAbort(); setFeedList(QSharedPointer()); delete m_feedListManagementInterface; delete m_feedListView; // call delete here, so that the header settings will get saved delete m_articleListView; // same for this one delete m_mainTab; delete m_mainFrame; m_mainFrame = 0; Settings::self()->save(); } void MainWidget::saveSettings() { const QList spl1 = m_horizontalSplitter->sizes(); if (std::count(spl1.begin(), spl1.end(), 0) == 0) { Settings::setSplitter1Sizes(spl1); } const QList spl2 = m_articleSplitter->sizes(); if (std::count(spl2.begin(), spl2.end(), 0) == 0) { Settings::setSplitter2Sizes(spl2); } Settings::setViewMode(m_viewMode); Settings::self()->save(); } void MainWidget::connectFrame(Akregator::WebEngineFrame *frame) { connect(m_tabWidget, &TabWidget::signalCopyInFrame, frame, &WebEngineFrame::slotCopyInFrame); connect(m_tabWidget, &TabWidget::signalPrintInFrame, frame, &WebEngineFrame::slotPrintInFrame); connect(m_tabWidget, &TabWidget::signalZoomChangedInFrame, frame, &WebEngineFrame::slotZoomChangeInFrame); connect(m_tabWidget, &TabWidget::signalPrintPreviewInFrame, frame, &WebEngineFrame::slotPrintPreviewInFrame); connect(m_tabWidget, &TabWidget::signalFindTextInFrame, frame, &WebEngineFrame::slotFindTextInFrame); connect(m_tabWidget, &TabWidget::signalTextToSpeechInFrame, frame, &WebEngineFrame::slotTextToSpeechInFrame); connect(m_tabWidget, &TabWidget::signalSaveLinkAsInFrame, frame, &WebEngineFrame::slotSaveLinkAsInFrame); connect(m_tabWidget, &TabWidget::signalCopyLinkAsInFrame, frame, &WebEngineFrame::slotCopyLinkAsInFrame); connect(m_tabWidget, &TabWidget::signalCopyImageLocation, frame, &WebEngineFrame::slotCopyImageLocationInFrame); connect(m_tabWidget, &TabWidget::signalSaveImageOnDisk, frame, &WebEngineFrame::slotSaveImageOnDiskInFrame); connect(m_tabWidget, &TabWidget::signalMute, frame, &WebEngineFrame::slotMute); connect(frame, &WebEngineFrame::showStatusBarMessage, this, &MainWidget::slotShowStatusBarMessage); connect(frame, &WebEngineFrame::signalIconChanged, m_tabWidget, &TabWidget::slotSetIcon); connect(frame, &WebEngineFrame::webPageMutedOrAudibleChanged, m_tabWidget, &TabWidget::slotWebPageMutedOrAudibleChanged); } void MainWidget::slotRequestNewFrame(int &frameId) { WebEngineFrame *frame = new WebEngineFrame(m_actionManager->actionCollection(), m_tabWidget); connectFrame(frame); Kernel::self()->frameManager()->slotAddFrame(frame); frameId = frame->id(); } void MainWidget::sendArticle(bool attach) { QByteArray text; QString title; Frame *frame = Kernel::self()->frameManager()->currentFrame(); if (frame && frame->id() > 0) { // are we in some other tab than the articlelist? text = frame->url().toString().toLatin1(); title = frame->title(); } else { // nah, we're in articlelist.. const Article article = m_selectionController->currentArticle(); if (!article.isNull()) { text = article.link().toDisplayString().toLatin1(); title = Akregator::Utils::convertHtmlTags(article.title()); } } if (text.isEmpty()) { return; } sendArticle(text, title, attach); } void MainWidget::cleanUpDownloadFile() { for (QPointer job : qAsConst(mListDownloadArticleJobs)) { if (job) { job->forceCleanupTemporaryFile(); } } } void MainWidget::sendArticle(const QByteArray &text, const QString &title, bool attach) { if (attach) { QPointer download = new Akregator::DownloadArticleJob(this); download->setArticleUrl(QUrl(QString::fromUtf8(text))); download->setText(QString::fromUtf8(text)); download->setTitle(title); mListDownloadArticleJobs.append(download); download->start(); } else { QUrlQuery query; query.addQueryItem(QStringLiteral("subject"), title); query.addQueryItem(QStringLiteral("body"), QString::fromUtf8(text)); QUrl url; url.setScheme(QStringLiteral("mailto")); url.setQuery(query); QDesktopServices::openUrl(url); } } void MainWidget::importFeedList(const QDomDocument &doc) { ImportFeedListCommand *cmd = new ImportFeedListCommand; cmd->setParentWidget(this); cmd->setFeedListDocument(doc); cmd->setTargetList(m_feedList); cmd->start(); } void MainWidget::setFeedList(const QSharedPointer &list) { if (list == m_feedList) { return; } const QSharedPointer oldList = m_feedList; m_feedList = list; if (m_feedList) { connect(m_feedList.data(), &FeedList::unreadCountChanged, this, &MainWidget::slotSetTotalUnread); } slotSetTotalUnread(); m_feedListManagementInterface->setFeedList(m_feedList); Kernel::self()->setFeedList(m_feedList); ProgressManager::self()->setFeedList(m_feedList); m_selectionController->setFeedList(m_feedList); if (oldList) { oldList->disconnect(this); } slotDeleteExpiredArticles(); } void MainWidget::deleteExpiredArticles(const QSharedPointer &list) { if (!list) { return; } ExpireItemsCommand *cmd = new ExpireItemsCommand(this); cmd->setParentWidget(this); cmd->setFeedList(list); cmd->setFeeds(list->feedIds()); cmd->start(); } void MainWidget::slotDeleteExpiredArticles() { deleteExpiredArticles(m_feedList); } QDomDocument MainWidget::feedListToOPML() { QDomDocument dom; if (m_feedList) { dom = m_feedList->toOpml(); } return dom; } void MainWidget::addFeedToGroup(const QString &url, const QString &groupName) { // Locate the group. const QList namedGroups = m_feedList->findByTitle(groupName); Folder *group = nullptr; for (TreeNode *const candidate : namedGroups) { if (candidate->isGroup()) { group = static_cast(candidate); break; } } if (!group) { Folder *g = new Folder(groupName); m_feedList->allFeedsFolder()->appendChild(g); group = g; } // Invoke the Add Feed dialog with url filled in. addFeed(url, 0, group, true); } void MainWidget::slotNormalView() { if (m_viewMode == NormalView) { return; } if (m_viewMode == CombinedView) { m_articleWidget->show(); const Article article = m_selectionController->currentArticle(); if (!article.isNull()) { m_articleViewer->showArticle(article); } else { m_articleViewer->slotShowSummary(m_selectionController->selectedSubscription()); } } m_articleSplitter->setOrientation(Qt::Vertical); m_viewMode = NormalView; Settings::setViewMode(m_viewMode); } void MainWidget::slotWidescreenView() { if (m_viewMode == WidescreenView) { return; } if (m_viewMode == CombinedView) { m_articleWidget->show(); Article article = m_selectionController->currentArticle(); if (!article.isNull()) { m_articleViewer->showArticle(article); } else { m_articleViewer->slotShowSummary(m_selectionController->selectedSubscription()); } } m_articleSplitter->setOrientation(Qt::Horizontal); m_viewMode = WidescreenView; Settings::setViewMode(m_viewMode); } void MainWidget::slotCombinedView() { if (m_viewMode == CombinedView) { return; } m_articleListView->slotClear(); m_articleWidget->hide(); m_viewMode = CombinedView; Settings::setViewMode(m_viewMode); } void MainWidget::slotMoveCurrentNodeUp() { TreeNode *current = m_selectionController->selectedSubscription(); if (!current) { return; } TreeNode *prev = current->prevSibling(); Folder *parent = current->parent(); if (!prev || !parent) { return; } parent->removeChild(prev); parent->insertChild(prev, current); m_feedListView->ensureNodeVisible(current); } void MainWidget::slotMoveCurrentNodeDown() { TreeNode *current = m_selectionController->selectedSubscription(); if (!current) { return; } TreeNode *next = current->nextSibling(); Folder *parent = current->parent(); if (!next || !parent) { return; } parent->removeChild(current); parent->insertChild(current, next); m_feedListView->ensureNodeVisible(current); } void MainWidget::slotMoveCurrentNodeLeft() { TreeNode *current = m_selectionController->selectedSubscription(); if (!current || !current->parent() || !current->parent()->parent()) { return; } Folder *parent = current->parent(); Folder *grandparent = current->parent()->parent(); parent->removeChild(current); grandparent->insertChild(current, parent); m_feedListView->ensureNodeVisible(current); } void MainWidget::slotMoveCurrentNodeRight() { TreeNode *current = m_selectionController->selectedSubscription(); if (!current || !current->parent()) { return; } TreeNode *prev = current->prevSibling(); if (prev && prev->isGroup()) { Folder *fg = static_cast(prev); current->parent()->removeChild(current); fg->appendChild(current); m_feedListView->ensureNodeVisible(current); } } void MainWidget::slotSendLink() { sendArticle(); } void MainWidget::slotSendFile() { sendArticle(true); } void MainWidget::slotNodeSelected(TreeNode *node) { m_markReadTimer->stop(); if (m_displayingAboutPage) { m_mainFrame->slotSetTitle(i18n("Articles")); if (m_viewMode != CombinedView) { m_articleWidget->show(); } if (Settings::showQuickFilter()) { m_searchBar->show(); } m_displayingAboutPage = false; } m_tabWidget->setCurrentWidget(m_mainFrame); if (Settings::resetQuickFilterOnNodeChange()) { m_searchBar->slotClearSearch(); } if (m_viewMode == CombinedView) { m_articleViewer->showNode(node); } else { m_articleViewer->slotShowSummary(node); } if (node) { m_mainFrame->setWindowTitle(node->title()); } m_actionManager->slotNodeSelected(node); } void MainWidget::slotFeedAdd() { Folder *group = nullptr; if (!m_selectionController->selectedSubscription()) { group = m_feedList->allFeedsFolder(); } else { if (m_selectionController->selectedSubscription()->isGroup()) { group = static_cast(m_selectionController->selectedSubscription()); } else { group = m_selectionController->selectedSubscription()->parent(); } } TreeNode *const lastChild = !group->children().isEmpty() ? group->children().last() : nullptr; addFeed(QString(), lastChild, group, false); } void MainWidget::addFeed(const QString &url, TreeNode *after, Folder *parent, bool autoExec) { CreateFeedCommand *cmd(new CreateFeedCommand(this)); cmd->setParentWidget(this); cmd->setPosition(parent, after); cmd->setRootFolder(m_feedList->allFeedsFolder()); cmd->setAutoExecute(autoExec); cmd->setUrl(url); cmd->setSubscriptionListView(m_feedListView); cmd->start(); } void MainWidget::slotFeedAddGroup() { CreateFolderCommand *cmd = new CreateFolderCommand(this); cmd->setParentWidget(this); cmd->setSelectedSubscription(m_selectionController->selectedSubscription()); cmd->setRootFolder(m_feedList->allFeedsFolder()); cmd->setSubscriptionListView(m_feedListView); cmd->start(); } void MainWidget::slotFeedRemove() { TreeNode *selectedNode = m_selectionController->selectedSubscription(); // don't delete root element! (safety valve) if (!selectedNode || selectedNode == m_feedList->allFeedsFolder()) { return; } DeleteSubscriptionCommand *cmd = new DeleteSubscriptionCommand(this); cmd->setParentWidget(this); cmd->setSubscription(m_feedList, selectedNode->id()); cmd->start(); } void MainWidget::slotFeedModify() { TreeNode *const node = m_selectionController->selectedSubscription(); if (!node) { return; } EditSubscriptionCommand *cmd = new EditSubscriptionCommand(this); cmd->setParentWidget(this); cmd->setSubscription(m_feedList, node->id()); cmd->setSubscriptionListView(m_feedListView); cmd->start(); } void MainWidget::slotNextUnreadArticle() { ensureArticleTabVisible(); if (m_viewMode == CombinedView) { m_feedListView->slotNextUnreadFeed(); return; } TreeNode *sel = m_selectionController->selectedSubscription(); if (sel && sel->unread() > 0) { m_articleListView->slotNextUnreadArticle(); } else { m_feedListView->slotNextUnreadFeed(); } } void MainWidget::slotPrevUnreadArticle() { ensureArticleTabVisible(); if (m_viewMode == CombinedView) { m_feedListView->slotPrevUnreadFeed(); return; } TreeNode *sel = m_selectionController->selectedSubscription(); if (sel && sel->unread() > 0) { m_articleListView->slotPreviousUnreadArticle(); } else { m_feedListView->slotPrevUnreadFeed(); } } void MainWidget::slotMarkAllFeedsRead() { KJob *job = m_feedList->createMarkAsReadJob(); connect(job, &KJob::finished, m_selectionController, &AbstractSelectionController::forceFilterUpdate); job->start(); } void MainWidget::slotMarkAllRead() { if (!m_selectionController->selectedSubscription()) { return; } KJob *job = m_selectionController->selectedSubscription()->createMarkAsReadJob(); connect(job, &KJob::finished, m_selectionController, &AbstractSelectionController::forceFilterUpdate); job->start(); } void MainWidget::slotSetTotalUnread() { Q_EMIT signalUnreadCountChanged(m_feedList ? m_feedList->unread() : 0); } void MainWidget::slotDoIntervalFetches() { if (!m_feedList) { return; } #if 0 // the following solid check apparently doesn't work reliably and causes // interval fetching not working although the user is actually online (but solid reports he's not const Networking::Status status = Solid::Networking::status(); if (status != Networking::Connected && status != Networking::Unknown) { return; } #endif m_feedList->addToFetchQueue(Kernel::self()->fetchQueue(), true); } void MainWidget::slotFetchCurrentFeed() { if (!m_selectionController->selectedSubscription()) { return; } if (isNetworkAvailable()) { m_selectionController->selectedSubscription()->slotAddToFetchQueue(Kernel::self()->fetchQueue()); } else { m_mainFrame->slotSetStatusText(i18n("Networking is not available.")); } } void MainWidget::slotFetchAllFeeds() { if (m_feedList && isNetworkAvailable()) { m_feedList->addToFetchQueue(Kernel::self()->fetchQueue()); } else if (m_feedList) { m_mainFrame->slotSetStatusText(i18n("Networking is not available.")); } } void MainWidget::slotFetchingStarted() { m_mainFrame->slotSetState(Frame::Started); m_actionManager->action(QStringLiteral("feed_stop"))->setEnabled(true); m_mainFrame->slotSetStatusText(i18n("Fetching Feeds...")); } void MainWidget::slotFetchingStopped() { m_mainFrame->slotSetState(Frame::Completed); m_actionManager->action(QStringLiteral("feed_stop"))->setEnabled(false); m_mainFrame->slotSetStatusText(QString()); } void MainWidget::slotArticleSelected(const Akregator::Article &article) { if (m_viewMode == CombinedView) { return; } m_markReadTimer->stop(); Q_ASSERT(article.isNull() || article.feed()); QVector
articles = m_selectionController->selectedArticles(); Q_EMIT signalArticlesSelected(articles); KToggleAction *const maai = qobject_cast(m_actionManager->action(QStringLiteral("article_set_status_important"))); Q_ASSERT(maai); maai->setChecked(article.keep()); m_articleViewer->showArticle(article); if (m_selectionController->selectedArticles().isEmpty()) { m_articleListView->setCurrentIndex(m_selectionController->currentArticleIndex()); } if (article.isNull() || article.status() == Akregator::Read) { return; } if (!Settings::useMarkReadDelay()) { return; } const int delay = Settings::markReadDelay(); if (delay > 0) { m_markReadTimer->start(delay * 1000); } else { Akregator::ArticleModifyJob *job = new Akregator::ArticleModifyJob; const Akregator::ArticleId aid = { article.feed()->xmlUrl(), article.guid() }; job->setStatus(aid, Akregator::Read); job->start(); } } void MainWidget::slotMouseButtonPressed(int button, const QUrl &url) { if (button != Qt::MidButton) { return; } if (!url.isValid()) { return; } OpenUrlRequest req(url); switch (Settings::mMBBehaviour()) { case Settings::EnumMMBBehaviour::OpenInExternalBrowser: req.setOptions(OpenUrlRequest::ExternalBrowser); break; case Settings::EnumMMBBehaviour::OpenInBackground: req.setOptions(OpenUrlRequest::NewTab); req.setOpenInBackground(true); break; default: req.setOptions(OpenUrlRequest::NewTab); req.setOpenInBackground(false); } Kernel::self()->frameManager()->slotOpenUrlRequest(req); } void MainWidget::slotOpenHomepage() { Feed *feed = dynamic_cast(m_selectionController->selectedSubscription()); if (!feed) { return; } QUrl url(feed->htmlUrl()); if (url.isValid()) { OpenUrlRequest req(url); req.setOptions(OpenUrlRequest::ExternalBrowser); Kernel::self()->frameManager()->slotOpenUrlRequest(req); } } void MainWidget::slotOpenSelectedArticlesInBrowser() { const QVector
articles = m_selectionController->selectedArticles(); for (const Akregator::Article &article : articles) { slotOpenArticleInBrowser(article); } } void MainWidget::slotOpenArticleInBrowser(const Akregator::Article &article) { if (!article.isNull() && article.link().isValid()) { OpenUrlRequest req(article.link()); req.setOptions(OpenUrlRequest::ExternalBrowser); Kernel::self()->frameManager()->slotOpenUrlRequest(req); } } void MainWidget::openSelectedArticles(bool openInBackground) { const QVector
articles = m_selectionController->selectedArticles(); for (const Akregator::Article &article : articles) { const QUrl url = article.link(); if (!url.isValid()) { continue; } OpenUrlRequest req(url); req.setOptions(OpenUrlRequest::NewTab); if (openInBackground) { req.setOpenInBackground(true); Kernel::self()->frameManager()->slotOpenUrlRequest(req, false /*don't use settings for open in background*/); } else { Kernel::self()->frameManager()->slotOpenUrlRequest(req); } } } void MainWidget::currentArticleInfo(QString &link, QString &title) { const Article article = m_selectionController->currentArticle(); if (article.isNull()) { return; } if (article.link().isValid()) { link = article.link().url(); title = Utils::convertHtmlTags(article.title()); } } void MainWidget::updateQuickSearchLineText() { m_searchBar->updateQuickSearchLineText(m_actionManager->quickSearchLineText()); } void MainWidget::slotCopyLinkAddress() { const Article article = m_selectionController->currentArticle(); if (article.isNull()) { return; } QString link; if (article.link().isValid()) { link = article.link().url(); QClipboard *cb = QApplication::clipboard(); cb->setText(link, QClipboard::Clipboard); // don't set url to selection as it's a no-no according to a fd.o spec //cb->setText(link, QClipboard::Selection); } } void MainWidget::slotToggleShowQuickFilter() { if (Settings::showQuickFilter()) { Settings::setShowQuickFilter(false); m_searchBar->slotClearSearch(); m_searchBar->hide(); } else { Settings::setShowQuickFilter(true); if (!m_displayingAboutPage) { m_searchBar->show(); } } } void MainWidget::slotArticleDelete() { if (m_viewMode == CombinedView) { return; } const QVector
articles = m_selectionController->selectedArticles(); QString msg; switch (articles.count()) { case 0: return; case 1: msg = i18n("Are you sure you want to delete article %1?", articles.first().title()); break; default: msg = i18np("Are you sure you want to delete the selected article?", "Are you sure you want to delete the %1 selected articles?", articles.count()); } if (KMessageBox::warningContinueCancel(this, msg, i18n("Delete Article"), KStandardGuiItem::del(), KStandardGuiItem::cancel(), QStringLiteral("Disable delete article confirmation")) != KMessageBox::Continue) { return; } TreeNode *const selected = m_selectionController->selectedSubscription(); if (selected) { selected->setNotificationMode(false); } Akregator::ArticleDeleteJob *job = new Akregator::ArticleDeleteJob; for (const Akregator::Article &i : articles) { Feed *const feed = i.feed(); if (!feed) { continue; } const Akregator::ArticleId aid = { feed->xmlUrl(), i.guid() }; job->appendArticleId(aid); } job->start(); if (selected) { selected->setNotificationMode(true); } } void MainWidget::slotFramesChanged() { // We need to wait till the frame is fully loaded QMetaObject::invokeMethod(m_part, "slotAutoSave", Qt::QueuedConnection); } void MainWidget::slotArticleToggleKeepFlag(bool) { const QVector
articles = m_selectionController->selectedArticles(); if (articles.isEmpty()) { return; } bool allFlagsSet = true; for (const Akregator::Article &i : articles) { allFlagsSet = allFlagsSet && i.keep(); if (!allFlagsSet) { break; } } Akregator::ArticleModifyJob *job = new Akregator::ArticleModifyJob; for (const Akregator::Article &i : articles) { const Akregator::ArticleId aid = { i.feed()->xmlUrl(), i.guid() }; job->setKeep(aid, !allFlagsSet); } job->start(); } namespace { void setArticleStatus(const QString &feedUrl, const QString &articleId, int status) { if (!feedUrl.isEmpty() && !articleId.isEmpty()) { Akregator::ArticleModifyJob *job = new Akregator::ArticleModifyJob; const Akregator::ArticleId aid = { feedUrl, articleId }; job->setStatus(aid, status); job->start(); } } void setSelectedArticleStatus(const Akregator::AbstractSelectionController *controller, int status) { const QVector articles = controller->selectedArticles(); if (articles.isEmpty()) { return; } Akregator::ArticleModifyJob *job = new Akregator::ArticleModifyJob; for (const Akregator::Article &i : articles) { const Akregator::ArticleId aid = { i.feed()->xmlUrl(), i.guid() }; job->setStatus(aid, status); } job->start(); } } void MainWidget::slotSetSelectedArticleRead() { ::setSelectedArticleStatus(m_selectionController, Akregator::Read); } void MainWidget::slotSetSelectedArticleUnread() { ::setSelectedArticleStatus(m_selectionController, Akregator::Unread); } void MainWidget::slotSetSelectedArticleNew() { ::setSelectedArticleStatus(m_selectionController, Akregator::New); } void MainWidget::slotSetCurrentArticleReadDelayed() { const Article article = m_selectionController->currentArticle(); if (article.isNull()) { return; } Akregator::ArticleModifyJob *const job = new Akregator::ArticleModifyJob; const Akregator::ArticleId aid = { article.feed()->xmlUrl(), article.guid() }; job->setStatus(aid, Akregator::Read); job->start(); } void MainWidget::slotShowStatusBarMessage(const QString &msg) { KPIM::BroadcastStatus::instance()->setStatusMsg(msg); } void MainWidget::readProperties(const KConfigGroup &config) { if (!Settings::resetQuickFilterOnNodeChange()) { // read filter settings m_searchBar->slotSetText(config.readEntry("searchLine")); m_searchBar->slotSetStatus(config.readEntry("searchCombo").toInt()); } const QString currentTabName = config.readEntry("CurrentTab"); // Reopen tabs const QStringList childList = config.readEntry(QStringLiteral("Children"), QStringList()); int currentFrameId = -1; for (const QString &framePrefix : childList) { WebEngineFrame *const frame = new WebEngineFrame(m_actionManager->actionCollection(), m_tabWidget); frame->loadConfig(config, framePrefix + QLatin1Char('_')); connectFrame(frame); Kernel::self()->frameManager()->slotAddFrame(frame); if (currentTabName == framePrefix) { currentFrameId = frame->id(); } } if (currentFrameId != -1) { m_tabWidget->slotSelectFrame(currentFrameId); } } void MainWidget::saveProperties(KConfigGroup &config) { // save filter settings const QString searchStr(m_searchBar->text()); if (searchStr.isEmpty()) { config.deleteEntry("searchLine"); } else { config.writeEntry("searchLine", m_searchBar->text()); } config.writeEntry("searchCombo", m_searchBar->status()); Kernel::self()->frameManager()->saveProperties(config); } void MainWidget::ensureArticleTabVisible() { m_tabWidget->setCurrentWidget(m_mainFrame); } void MainWidget::slotReloadAllTabs() { m_tabWidget->slotReloadAllTabs(); } bool MainWidget::isNetworkAvailable() const { return PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline(); } void MainWidget::slotNetworkStatusChanged(bool status) { if (status) { m_mainFrame->slotSetStatusText(i18n("Networking is available now.")); this->slotFetchAllFeeds(); } else { m_mainFrame->slotSetStatusText(i18n("Networking is not available.")); } } void MainWidget::slotOpenSelectedArticles() { openSelectedArticles(false); } void MainWidget::slotOpenSelectedArticlesInBackground() { openSelectedArticles(true); } void MainWidget::slotCurrentFrameChanged(int frameId) { Kernel::self()->frameManager()->slotChangeFrame(frameId); m_actionManager->zoomActionMenu()->setZoomFactor(Kernel::self()->frameManager()->currentFrame()->zoomFactor() * 100); } void MainWidget::slotFocusQuickSearch() { m_searchBar->setFocusSearchLine(); } void MainWidget::slotArticleAction(Akregator::ArticleViewerWebEngine::ArticleAction type, const QString &articleId, const QString &feed) { switch (type) { case ArticleViewerWebEngine::DeleteAction: { Akregator::ArticleDeleteJob *job = new Akregator::ArticleDeleteJob; const Akregator::ArticleId aid = { feed, articleId }; job->appendArticleId(aid); job->start(); break; } case ArticleViewerWebEngine::MarkAsRead: ::setArticleStatus(feed, articleId, Akregator::Read); break; case ArticleViewerWebEngine::MarkAsUnRead: ::setArticleStatus(feed, articleId, Akregator::Unread); break; case ArticleViewerWebEngine::MarkAsImportant: { Akregator::ArticleModifyJob *job = new Akregator::ArticleModifyJob; const Akregator::Article article = m_feedList->findArticle(feed, articleId); const Akregator::ArticleId aid = { feed, articleId }; job->setKeep(aid, !article.keep()); job->start(); break; } case ArticleViewerWebEngine::SendUrlArticle: { case ArticleViewerWebEngine::SendFileArticle: const Article article = m_feedList->findArticle(feed, articleId); const QByteArray text = article.link().toDisplayString().toLatin1(); const QString title = Akregator::Utils::convertHtmlTags(article.title()); if (text.isEmpty()) { return; } sendArticle(text, title, (type == ArticleViewerWebEngine::SendFileArticle)); break; } case ArticleViewerWebEngine::OpenInBackgroundTab: { const Akregator::Article article = m_feedList->findArticle(feed, articleId); const QUrl url = article.link(); if (url.isValid()) { OpenUrlRequest req(url); req.setOptions(OpenUrlRequest::NewTab); req.setOpenInBackground(true); Kernel::self()->frameManager()->slotOpenUrlRequest(req, false /*don't use settings for open in background*/); } break; } case ArticleViewerWebEngine::OpenInExternalBrowser: { const Akregator::Article article = m_feedList->findArticle(feed, articleId); slotOpenArticleInBrowser(article); break; } case ArticleViewerWebEngine::Share: const Akregator::Article article = m_feedList->findArticle(feed, articleId); const QUrl url = article.link(); if (url.isValid()) { //TODO } break; } } diff --git a/src/openurlrequest.h b/src/openurlrequest.h index 83d08238..0162b3f5 100644 --- a/src/openurlrequest.h +++ b/src/openurlrequest.h @@ -1,90 +1,89 @@ /* This file is part of Akregator. Copyright (C) 2006 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #ifndef AKREGATOR_OPENURLREQUEST_H #define AKREGATOR_OPENURLREQUEST_H #include #include -#include #include "akregator_export.h" namespace Akregator { class AKREGATOR_EXPORT OpenUrlRequest { public: /** * Akregator-specific options specifying how a link should be handled. * TODO: check what can be done by overriding KURLArgs flags. */ enum Options { None = 0, /**< no explicit options, use default */ NewTab, /**< open in new tab */ ExternalBrowser /**< open in external browser */ }; explicit OpenUrlRequest(const QUrl &url = QUrl()); /** * the Id of the frame that sent the request */ int frameId() const; void setFrameId(int frameId); QUrl url() const; void setUrl(const QUrl &url); KParts::OpenUrlArguments args() const; void setArgs(const KParts::OpenUrlArguments &args); KParts::BrowserArguments browserArgs() const; void setBrowserArgs(const KParts::BrowserArguments &args); Options options() const; void setOptions(Options options); bool openInBackground() const; void setOpenInBackground(bool background); QString debugInfo() const; bool wasHandled() const; void setWasHandled(bool handled); private: int m_frameId; QUrl m_url; KParts::OpenUrlArguments m_args; KParts::BrowserArguments m_browserArgs; Options m_options; bool m_inBackground; bool m_wasHandled; }; } // namespace Akregator #endif // AKREGATOR_OPENURLREQUEST_H diff --git a/src/tabwidget.cpp b/src/tabwidget.cpp index 18871c5a..ab98d4e6 100644 --- a/src/tabwidget.cpp +++ b/src/tabwidget.cpp @@ -1,530 +1,526 @@ /* This file is part of Akregator. Copyright (C) 2004 Sashmit Bhaduri This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "tabwidget.h" #include #include #include -#include #include #include #include #include #include -#include -#include #include "akregator_debug.h" #include #include #include #include #include #include -#include #include #include #include "actionmanager.h" #include "akregatorconfig.h" #include "frame.h" #include "framemanager.h" #include "kernel.h" #include "openurlrequest.h" #include "utils/temporaryvalue.h" #include using namespace Akregator; class Q_DECL_HIDDEN TabWidget::Private { private: TabWidget *const q; public: explicit Private(TabWidget *qq) : q(qq) , currentMaxLength(30) , currentItem(0) , tabsClose(0) { } QHash frames; QHash framesById; int currentMaxLength; QWidget *currentItem = nullptr; QToolButton *tabsClose = nullptr; QWidget *selectedWidget() const { return (currentItem && q->indexOf(currentItem) != -1) ? currentItem : q->currentWidget(); } uint tabBarWidthForMaxChars(int maxLength); void setTitle(const QString &title, QWidget *sender); void updateTabBarVisibility(); Frame *currentFrame(); }; void TabWidget::Private::updateTabBarVisibility() { const bool tabBarIsHidden = ((q->count() <= 1) && !Settings::alwaysShowTabBar()); if (tabBarIsHidden) { q->tabBar()->hide(); } else { q->tabBar()->show(); } if (q->count() >= 1 && Settings::closeButtonOnTabs()) { q->tabBar()->tabButton(0, QTabBar::RightSide)->hide(); } } TabWidget::TabWidget(QWidget *parent) : QTabWidget(parent) , d(new Private(this)) { setMinimumSize(250, 150); setMovable(false); setDocumentMode(true); setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &TabWidget::customContextMenuRequested, this, &TabWidget::slotTabContextMenuRequest); connect(this, &TabWidget::currentChanged, this, &TabWidget::slotTabChanged); connect(this, &QTabWidget::tabCloseRequested, this, &TabWidget::slotCloseRequest); setTabsClosable(Settings::closeButtonOnTabs()); d->tabsClose = new QToolButton(this); connect(d->tabsClose, &QToolButton::clicked, this, &TabWidget::slotRemoveCurrentFrame); d->tabsClose->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); d->tabsClose->setEnabled(false); d->tabsClose->adjustSize(); d->tabsClose->setToolTip(i18n("Close the current tab")); #ifndef QT_NO_ACCESSIBILITY d->tabsClose->setAccessibleName(i18n("Close tab")); #endif setCornerWidget(d->tabsClose, Qt::TopRightCorner); d->updateTabBarVisibility(); } TabWidget::~TabWidget() { delete d; } void TabWidget::slotTabContextMenuRequest(const QPoint &pos) { QTabBar *bar = tabBar(); if (count() <= 1) { return; } const int indexBar = bar->tabAt(bar->mapFrom(this, pos)); if (indexBar == -1) { return; } QMenu menu(this); const int countTab = (count() > 1); QAction *detachTab = menu.addAction(i18nc("@action:inmenu", "Detach Tab")); detachTab->setEnabled((indexBar != 0) && countTab); detachTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-detach"))); menu.addSeparator(); QAction *closeTab = menu.addAction(i18nc("@action:inmenu", "Close Tab")); closeTab->setEnabled((indexBar != 0) && countTab); closeTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); QAction *allOther = menu.addAction(i18nc("@action:inmenu", "Close All Other Tabs")); allOther->setEnabled(countTab); allOther->setIcon(QIcon::fromTheme(QStringLiteral("tab-close-other"))); QAction *allTab = menu.addAction(i18nc("@action:inmenu", "Close All Tabs")); allTab->setEnabled(countTab); allTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); QAction *action = menu.exec(mapToGlobal(pos)); if (action == allOther) { // Close all other tabs slotCloseAllTabExcept(indexBar); } else if (action == closeTab) { slotCloseRequest(indexBar); } else if (action == allTab) { slotCloseAllTab(); } else if (action == detachTab) { slotDetachTab(indexBar); } } void TabWidget::closeAllTabExcept(int index) { //Don't close first tab for (int i = count() - 1; i > 0; --i) { if (i == index) { continue; } slotCloseRequest(i); } } void TabWidget::slotCloseAllTabExcept(int index) { closeAllTabExcept(index); } void TabWidget::slotCloseAllTab() { closeAllTabExcept(); } void TabWidget::slotSettingsChanged() { if (tabsClosable() != Settings::closeButtonOnTabs()) { setTabsClosable(Settings::closeButtonOnTabs()); } d->updateTabBarVisibility(); } void TabWidget::slotNextTab() { setCurrentIndex((currentIndex() + 1) % count()); } void TabWidget::slotPreviousTab() { if (currentIndex() == 0) { setCurrentIndex(count() - 1); } else { setCurrentIndex(currentIndex() - 1); } } void TabWidget::slotSelectFrame(int frameId) { Frame *frame = d->framesById.value(frameId); if (frame && frame != d->currentFrame()) { setCurrentWidget(frame); frame->setFocus(); } } void TabWidget::slotAddFrame(Frame *frame) { if (!frame) { return; } d->frames.insert(frame, frame); d->framesById.insert(frame->id(), frame); addTab(frame, frame->title()); connect(frame, &Frame::signalTitleChanged, this, &TabWidget::slotSetTitle); slotSetTitle(frame, frame->title()); } Frame *TabWidget::Private::currentFrame() { QWidget *w = q->currentWidget(); Q_ASSERT(frames.value(w)); return w ? frames.value(w) : 0; } void TabWidget::slotZoomChanged(qreal value) { if (!d->currentFrame()) { return; } Q_EMIT signalZoomChangedInFrame(d->currentFrame()->id(), value); } void TabWidget::slotTabChanged(int index) { Frame *frame = d->frames.value(widget(index)); d->tabsClose->setEnabled(frame && frame->isRemovable()); Q_EMIT signalCurrentFrameChanged(frame ? frame->id() : -1); } void TabWidget::tabInserted(int) { d->updateTabBarVisibility(); } void TabWidget::tabRemoved(int) { d->updateTabBarVisibility(); } void TabWidget::slotRemoveCurrentFrame() { Frame *const frame = d->currentFrame(); if (frame) { Q_EMIT signalRemoveFrameRequest(frame->id()); } } void TabWidget::slotRemoveFrame(int frameId) { if (!d->framesById.contains(frameId)) { return; } Frame *f = d->framesById.value(frameId); d->frames.remove(f); d->framesById.remove(frameId); f->disconnect(this); removeTab(indexOf(f)); Q_EMIT signalRemoveFrameRequest(f->id()); if (d->currentFrame()) { d->setTitle(d->currentFrame()->title(), currentWidget()); } } // copied wholesale from KonqFrameTabs uint TabWidget::Private::tabBarWidthForMaxChars(int maxLength) { int hframe; QStyleOption o; hframe = q->tabBar()->style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &o, q); QFontMetrics fm = q->tabBar()->fontMetrics(); int x = 0; for (int i = 0; i < q->count(); ++i) { Frame *f = frames.value(q->widget(i)); if (!f) { continue; // frames is out of sync, e.g. because tabInserted wasn't called yet - #185597 } QString newTitle = f->title(); if (newTitle.length() > maxLength) { newTitle = newTitle.left(maxLength - 3) + QLatin1String("..."); } int lw = fm.width(newTitle); int iw = q->tabBar()->tabIcon(i).pixmap(q->tabBar()->style()->pixelMetric( QStyle::PM_SmallIconSize), QIcon::Normal ).width() + 4; x += (q->tabBar()->style()->sizeFromContents(QStyle::CT_TabBarTab, &o, QSize(qMax(lw + hframe + iw, QApplication::globalStrut().width()), 0), q)).width(); } return x; } void TabWidget::slotSetTitle(Frame *frame, const QString &title) { d->setTitle(title, frame); } void TabWidget::slotWebPageMutedOrAudibleChanged(Akregator::Frame *frame, bool isAudioMuted, bool wasRecentlyAudible) { Q_UNUSED(wasRecentlyAudible); const int idx = indexOf(frame); if (idx < 0) { return; } if (isAudioMuted) { setTabIcon(idx, QIcon::fromTheme(QStringLiteral("audio-volume-muted"))); } else { setTabIcon(idx, frame->icon()); } } void TabWidget::slotSetIcon(Akregator::Frame *frame, const QIcon &icon) { const int idx = indexOf(frame); if (idx < 0) { return; } frame->setIcon(icon); setTabIcon(idx, icon); } void TabWidget::Private::setTitle(const QString &title, QWidget *sender) { int senderIndex = q->indexOf(sender); q->setTabToolTip(senderIndex, QString()); uint lcw = 0, rcw = 0; int tabBarHeight = q->tabBar()->sizeHint().height(); QWidget *leftCorner = q->cornerWidget(Qt::TopLeftCorner); if (leftCorner && leftCorner->isVisible()) { lcw = qMax(leftCorner->width(), tabBarHeight); } QWidget *rightCorner = q->cornerWidget(Qt::TopRightCorner); if (rightCorner && rightCorner->isVisible()) { rcw = qMax(rightCorner->width(), tabBarHeight); } uint maxTabBarWidth = q->width() - lcw - rcw; int newMaxLength = 30; for (; newMaxLength > 3; newMaxLength--) { if (tabBarWidthForMaxChars(newMaxLength) < maxTabBarWidth) { break; } } QString newTitle = title; if (newTitle.length() > newMaxLength) { q->setTabToolTip(senderIndex, newTitle); newTitle = newTitle.left(newMaxLength - 3) + QLatin1String("..."); } newTitle.replace(QLatin1Char('&'), QStringLiteral("&&")); if (q->tabText(senderIndex) != newTitle) { q->setTabText(senderIndex, newTitle); } if (newMaxLength != currentMaxLength) { for (int i = 0; i < q->count(); ++i) { Frame *f = frames.value(q->widget(i)); if (!f) { continue; // frames is out of sync, e.g. because tabInserted wasn't called yet - #185597 } newTitle = f->title(); int index = q->indexOf(q->widget(i)); q->setTabToolTip(index, QString()); if (newTitle.length() > newMaxLength) { q->setTabToolTip(index, newTitle); newTitle = newTitle.left(newMaxLength - 3) + QLatin1String("..."); } newTitle.replace(QLatin1Char('&'), QStringLiteral("&&")); if (newTitle != q->tabText(index)) { q->setTabText(index, newTitle); } } currentMaxLength = newMaxLength; } } void TabWidget::slotDetachTab(int index) { QWidget *w = widget(index); Frame *frame = d->frames.value(w); if (frame && frame->url().isValid() && frame->isRemovable()) { OpenUrlRequest request; request.setUrl(frame->url()); request.setOptions(OpenUrlRequest::ExternalBrowser); Q_EMIT signalOpenUrlRequest(request); slotCloseRequest(index); } } void TabWidget::slotTextToSpeech() { Q_EMIT signalTextToSpeechInFrame(d->currentFrame()->id()); } void TabWidget::slotFindTextInHtml() { Q_EMIT signalFindTextInFrame(d->currentFrame()->id()); } void TabWidget::slotCopyLinkAddress() { Q_EMIT signalCopyLinkAsInFrame(d->currentFrame()->id()); } void TabWidget::slotSaveLinkAs() { Q_EMIT signalSaveLinkAsInFrame(d->currentFrame()->id()); } void TabWidget::slotPrintPreview() { Q_EMIT signalPrintPreviewInFrame(d->currentFrame()->id()); } void TabWidget::slotPrint() { Q_EMIT signalPrintInFrame(d->currentFrame()->id()); } void TabWidget::slotCopy() { Q_EMIT signalCopyInFrame(d->currentFrame()->id()); } void TabWidget::slotSaveImageOnDisk() { Q_EMIT signalSaveImageOnDisk(d->currentFrame()->id()); } void TabWidget::slotUnMute() { Q_EMIT signalMute(d->currentFrame()->id(), false); } void TabWidget::slotMute() { Q_EMIT signalMute(d->currentFrame()->id(), true); } void TabWidget::slotCopyImageLocation() { Q_EMIT signalCopyImageLocation(d->currentFrame()->id()); } void TabWidget::slotCloseTab() { QWidget *widget = d->selectedWidget(); Frame *frame = d->frames.value(widget); if (frame == 0 || !frame->isRemovable()) { return; } Q_EMIT signalRemoveFrameRequest(frame->id()); } void TabWidget::slotReloadAllTabs() { for (Frame *frame : qAsConst(d->frames)) { frame->slotReload(); } } void TabWidget::slotCloseRequest(int index) { QWidget *w = widget(index); if (d->frames.value(w)) { Q_EMIT signalRemoveFrameRequest(d->frames.value(w)->id()); } } void TabWidget::slotActivateTab() { setCurrentIndex(sender()->objectName().rightRef(2).toInt() - 1); } diff --git a/src/trayicon.cpp b/src/trayicon.cpp index 0ffdc2e9..12510930 100644 --- a/src/trayicon.cpp +++ b/src/trayicon.cpp @@ -1,75 +1,72 @@ /* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "trayicon.h" #include "akregatorconfig.h" #include "akregator_debug.h" #include -#include #include #include -#include -#include using namespace Akregator; TrayIcon *TrayIcon::m_instance = 0; TrayIcon *TrayIcon::getInstance() { return m_instance; } void TrayIcon::setInstance(TrayIcon *trayIcon) { m_instance = trayIcon; } TrayIcon::TrayIcon(QObject *parent) : KStatusNotifierItem(parent) , m_unread(0) { setToolTipTitle(i18n("Akregator")); setToolTipIconByName(i18n("Akregator")); setIconByName(QStringLiteral("akregator")); } TrayIcon::~TrayIcon() { } void TrayIcon::slotSetUnread(int unread) { m_unread = unread; this->setToolTip(QStringLiteral("akregator"), i18n("Akregator"), unread == 0 ? i18n("There are no unread articles") : i18np("1 unread article", "%1 unread articles", unread)); setStatus(unread > 0 ? KStatusNotifierItem::Active : KStatusNotifierItem::Passive); } void TrayIcon::settingsChanged() { slotSetUnread(m_unread); } diff --git a/src/trayicon.h b/src/trayicon.h index 11d18f70..e29ee43a 100644 --- a/src/trayicon.h +++ b/src/trayicon.h @@ -1,54 +1,53 @@ /* This file is part of Akregator. Copyright (C) 2004 Stanislav Karchebny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #ifndef AKREGATOR_TRAYICON_H #define AKREGATOR_TRAYICON_H #include "akregator_export.h" -#include #include namespace Akregator { class AKREGATOR_EXPORT TrayIcon : public KStatusNotifierItem { Q_OBJECT public: static TrayIcon *getInstance(); static void setInstance(TrayIcon *trayIcon); explicit TrayIcon(QObject *parent = nullptr); ~TrayIcon(); public Q_SLOTS: void settingsChanged(); void slotSetUnread(int unread); private: static TrayIcon *m_instance; int m_unread; }; } // namespace Akregator #endif // AKREGATOR_TRAYICON_H diff --git a/src/widgets/searchbar.cpp b/src/widgets/searchbar.cpp index 85cfdd27..b27252f6 100644 --- a/src/widgets/searchbar.cpp +++ b/src/widgets/searchbar.cpp @@ -1,208 +1,205 @@ /* This file is part of Akregator. Copyright (C) 2005 Frank Osterfeld This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include "searchbar.h" #include "akregatorconfig.h" #include "articlematcher.h" #include "article.h" #include "widgets/statussearchline.h" #include #include #include -#include -#include -#include #include #include #include #include using namespace Akregator; using namespace Akregator::Filters; class SearchBar::SearchBarPrivate { public: QString searchText; QTimer timer; StatusSearchLine *searchLine = nullptr; int delay; std::vector > matchers; void triggerTimer() { if (timer.isActive()) { timer.stop(); } timer.start(200); } }; SearchBar::SearchBar(QWidget *parent) : QWidget(parent) , d(new SearchBar::SearchBarPrivate) { d->delay = 400; QHBoxLayout *layout = new QHBoxLayout(this); layout->setMargin(2); layout->setSpacing(5); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); d->searchLine = new StatusSearchLine(this); d->searchLine->setClearButtonShown(true); d->searchLine->setPlaceholderText(i18n("Search articles...")); layout->addWidget(d->searchLine); connect(d->searchLine, &KLineEdit::textChanged, this, &SearchBar::slotSearchStringChanged); connect(d->searchLine, &StatusSearchLine::statusChanged, this, &SearchBar::slotStatusChanged); connect(&(d->timer), &QTimer::timeout, this, &SearchBar::slotActivateSearch); d->timer.setSingleShot(true); } SearchBar::~SearchBar() { delete d; d = 0; } QString SearchBar::text() const { return d->searchText; } int SearchBar::status() const { return static_cast(d->searchLine->status()); } void SearchBar::setDelay(int ms) { d->delay = ms; } int SearchBar::delay() const { return d->delay; } void SearchBar::setFocusSearchLine() { d->searchLine->setFocus(); } void SearchBar::slotClearSearch() { if (status() != 0 || !d->searchLine->text().trimmed().isEmpty()) { d->searchLine->clear(); d->searchLine->setStatus(Akregator::StatusSearchLine::AllArticles); d->timer.stop(); slotActivateSearch(); } } void SearchBar::slotSetStatus(int status) { d->searchLine->setStatus(static_cast(status)); d->triggerTimer(); } void SearchBar::slotSetText(const QString &text) { d->searchLine->setText(text); d->triggerTimer(); } void SearchBar::slotStatusChanged(Akregator::StatusSearchLine::Status /*status*/) { d->triggerTimer(); } std::vector > SearchBar::matchers() const { return d->matchers; } void SearchBar::updateQuickSearchLineText(const QString &searchLineShortcut) { d->searchLine->setPlaceholderText(i18n("Search articles...<%1>", searchLineShortcut)); } void SearchBar::slotSearchStringChanged(const QString &search) { d->searchText = search; d->triggerTimer(); } void SearchBar::slotActivateSearch() { QVector textCriteria; QVector statusCriteria; if (!d->searchText.isEmpty()) { Criterion subjCrit(Criterion::Title, Criterion::Contains, d->searchText); textCriteria << subjCrit; Criterion crit1(Criterion::Description, Criterion::Contains, d->searchText); textCriteria << crit1; Criterion authCrit(Criterion::Author, Criterion::Contains, d->searchText); textCriteria << authCrit; } switch (d->searchLine->status()) { case StatusSearchLine::AllArticles: break; case StatusSearchLine::NewArticles: { Criterion crit(Criterion::Status, Criterion::Equals, New); statusCriteria << crit; break; } case StatusSearchLine::UnreadArticles: { Criterion crit1(Criterion::Status, Criterion::Equals, New); Criterion crit2(Criterion::Status, Criterion::Equals, Unread); statusCriteria << crit1; statusCriteria << crit2; break; } case StatusSearchLine::ImportantArticles: { Criterion crit(Criterion::KeepFlag, Criterion::Equals, true); statusCriteria << crit; break; } } std::vector > matchers; matchers.push_back(QSharedPointer(new ArticleMatcher(textCriteria, ArticleMatcher::LogicalOr))); matchers.push_back(QSharedPointer(new ArticleMatcher(statusCriteria, ArticleMatcher::LogicalOr))); Settings::setStatusFilter(d->searchLine->status()); Settings::setTextFilter(d->searchText); d->matchers = matchers; Q_EMIT signalSearch(matchers); } diff --git a/src/widgets/statussearchline.cpp b/src/widgets/statussearchline.cpp index 3c4e123f..4e43a94d 100644 --- a/src/widgets/statussearchline.cpp +++ b/src/widgets/statussearchline.cpp @@ -1,109 +1,108 @@ /* Copyright (C) 2016-2017 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "statussearchline.h" #include #include #include #include -#include using namespace Akregator; Q_DECLARE_METATYPE(Akregator::StatusSearchLine::Status) StatusSearchLine::StatusSearchLine(QWidget *parent) : KLineEdit(parent) , mDefaultStatus(AllArticles) , mSearchLineStatusAction(nullptr) { initializeHash(); initializeActions(); } StatusSearchLine::~StatusSearchLine() { } void StatusSearchLine::initializeHash() { const QIcon iconAll = QIcon::fromTheme(QStringLiteral("system-run")); const QIcon iconNew = QIcon::fromTheme(QStringLiteral("mail-mark-unread-new")); const QIcon iconUnread = QIcon::fromTheme(QStringLiteral("mail-mark-unread")); const QIcon iconKeep = QIcon::fromTheme(QStringLiteral("mail-mark-important")); StatusInfo statusAll(i18n("All Articles"), iconAll); StatusInfo statusUnread(i18nc("Unread articles filter", "Unread"), iconUnread); StatusInfo statusNew(i18nc("New articles filter", "New"), iconNew); StatusInfo statusImportant(i18nc("Important articles filter", "Important"), iconKeep); mHashStatus.insert(AllArticles, statusAll); mHashStatus.insert(NewArticles, statusNew); mHashStatus.insert(UnreadArticles, statusUnread); mHashStatus.insert(ImportantArticles, statusImportant); } void StatusSearchLine::setStatus(StatusSearchLine::Status status) { updateStatusIcon(status); } void StatusSearchLine::initializeActions() { mSearchLineStatusAction = addAction(mHashStatus.value(AllArticles).mIcon, QLineEdit::LeadingPosition); mSearchLineStatusAction->setToolTip(mHashStatus.value(AllArticles).mText); connect(mSearchLineStatusAction, &QAction::triggered, this, &StatusSearchLine::showMenu); } void StatusSearchLine::showMenu() { QMenu p(this); QActionGroup *grp = new QActionGroup(this); grp->setExclusive(true); for (int i = AllArticles; i <= ImportantArticles; ++i) { StatusSearchLine::Status status = static_cast(i); QAction *act = new QAction(mHashStatus.value(status).mIcon, mHashStatus.value(status).mText, this); act->setCheckable(true); act->setChecked(mDefaultStatus == status); act->setData(QVariant::fromValue(status)); grp->addAction(act); p.addAction(act); if (i == AllArticles) { p.addSeparator(); } } QAction *a = p.exec(mapToGlobal(QPoint(0, height()))); if (a) { const StatusSearchLine::Status newStatus = a->data().value(); updateStatusIcon(newStatus); } } void StatusSearchLine::updateStatusIcon(StatusSearchLine::Status status) { if (mDefaultStatus != status) { mDefaultStatus = status; mSearchLineStatusAction->setIcon(mHashStatus[status].mIcon); mSearchLineStatusAction->setToolTip(mHashStatus[status].mText); Q_EMIT statusChanged(mDefaultStatus); } } StatusSearchLine::Status StatusSearchLine::status() const { return mDefaultStatus; }