1 /* ============================================================
2 * QuiteRSS is a open-source cross-platform RSS/Atom news feeds reader
3 * Copyright (C) 2011-2020 QuiteRSS Team <quiterssteam@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 * ============================================================ */
18 #include "newsmodel.h"
19
20 #include "mainapplication.h"
21
NewsModel(QObject * parent,QTreeView * view)22 NewsModel::NewsModel(QObject *parent, QTreeView *view)
23 : QSqlTableModel(parent)
24 , simplifiedDateTime_(true)
25 , view_(view)
26 {
27 setEditStrategy(QSqlTableModel::OnManualSubmit);
28 }
29
data(const QModelIndex & index,int role) const30 QVariant NewsModel::data(const QModelIndex &index, int role) const
31 {
32 if (index.row() > (view_->verticalScrollBar()->value() + view_->verticalScrollBar()->pageStep()))
33 return QSqlTableModel::data(index, role);
34
35 MainWindow *mainWindow = mainApp->mainWindow();
36
37 if (role == Qt::DecorationRole) {
38 if (QSqlTableModel::fieldIndex("read") == index.column()) {
39 QPixmap icon;
40 if (1 == QSqlTableModel::index(index.row(), fieldIndex("new")).data(Qt::EditRole).toInt())
41 icon.load(":/images/bulletNew");
42 else if (0 == index.data(Qt::EditRole).toInt())
43 icon.load(":/images/bulletUnread");
44 else icon.load(":/images/bulletRead");
45 return icon;
46 } else if (QSqlTableModel::fieldIndex("starred") == index.column()) {
47 QPixmap icon;
48 if (0 == index.data(Qt::EditRole).toInt())
49 icon.load(":/images/starOff");
50 else icon.load(":/images/starOn");
51 return icon;
52 } else if (QSqlTableModel::fieldIndex("feedId") == index.column()) {
53 QPixmap icon;
54 int feedId = QSqlTableModel::index(index.row(), fieldIndex("feedId")).data(Qt::EditRole).toInt();
55 QModelIndex feedIndex = mainWindow->feedsModel_->indexById(feedId);
56 bool isFeed = (feedIndex.isValid() && mainWindow->feedsModel_->isFolder(feedIndex)) ? false : true;
57
58 if (feedIndex.isValid()) {
59 QByteArray byteArray = mainWindow->feedsModel_->dataField(feedIndex, "image").toByteArray();
60 if (!byteArray.isNull()) {
61 icon.loadFromData(QByteArray::fromBase64(byteArray));
62 } else if (isFeed) {
63 icon.load(":/images/feed");
64 } else {
65 icon.load(":/images/folder");
66 }
67 }
68
69 return icon;
70 } else if (QSqlTableModel::fieldIndex("label") == index.column()) {
71 QIcon icon;
72 QString strIdLabels = index.data(Qt::EditRole).toString();
73 QList<QTreeWidgetItem *> labelListItems = mainApp->mainWindow()->
74 categoriesTree_->getLabelListItems();
75 foreach (QTreeWidgetItem *item, labelListItems) {
76 if (strIdLabels.contains(QString(",%1,").arg(item->text(2)))) {
77 icon = item->icon(0);
78 break;
79 }
80 }
81 return icon;
82 }
83 } else if (role == Qt::ToolTipRole) {
84 if (QSqlTableModel::fieldIndex("feedId") == index.column()) {
85 int feedId = QSqlTableModel::index(index.row(), fieldIndex("feedId")).data(Qt::EditRole).toInt();
86 QModelIndex feedIndex = mainWindow->feedsModel_->indexById(feedId);
87 return mainWindow->feedsModel_->dataField(feedIndex, "text").toString();
88 } else if (QSqlTableModel::fieldIndex("title") == index.column()) {
89 QString title = index.data(Qt::EditRole).toString();
90 if ((view_->header()->sectionSize(index.column()) - 14) < view_->header()->fontMetrics().width(title))
91 return title;
92 }
93 return QString("");
94 } else if (role == Qt::DisplayRole) {
95 if (QSqlTableModel::fieldIndex("read") == index.column()) {
96 return QVariant();
97 } else if (QSqlTableModel::fieldIndex("starred") == index.column()) {
98 return QVariant();
99 } else if (QSqlTableModel::fieldIndex("feedId") == index.column()) {
100 return QVariant();
101 } else if (QSqlTableModel::fieldIndex("rights") == index.column()) {
102 int feedId = QSqlTableModel::index(index.row(), fieldIndex("feedId")).data(Qt::EditRole).toInt();
103 QModelIndex feedIndex = mainWindow->feedsModel_->indexById(feedId);
104 return mainWindow->feedsModel_->dataField(feedIndex, "text").toString();
105 } else if (QSqlTableModel::fieldIndex("published") == index.column()) {
106 QDateTime dtLocal;
107 QString strDate = index.data(Qt::EditRole).toString();
108
109 if (!strDate.isNull()) {
110 QDateTime dtLocalTime = QDateTime::currentDateTime();
111 QDateTime dtUTC = QDateTime(dtLocalTime.date(), dtLocalTime.time(), Qt::UTC);
112 int nTimeShift = dtLocalTime.secsTo(dtUTC);
113
114 QDateTime dt = QDateTime::fromString(strDate, Qt::ISODate);
115 dtLocal = dt.addSecs(nTimeShift);
116 } else {
117 dtLocal = QDateTime::fromString(
118 QSqlTableModel::index(index.row(), fieldIndex("received")).data(Qt::EditRole).toString(),
119 Qt::ISODate);
120 }
121 if (simplifiedDateTime_) {
122 if (QDateTime::currentDateTime().date() <= dtLocal.date())
123 return dtLocal.toString(formatTime_);
124 else
125 return dtLocal.toString(formatDate_);
126 } else {
127 return dtLocal.toString(formatDate_ + " " + formatTime_);
128 }
129 } else if (QSqlTableModel::fieldIndex("received") == index.column()) {
130 QDateTime dateTime = QDateTime::fromString(
131 index.data(Qt::EditRole).toString(),
132 Qt::ISODate);
133 if (simplifiedDateTime_) {
134 if (QDateTime::currentDateTime().date() == dateTime.date()) {
135 return dateTime.toString(formatTime_);
136 } else return dateTime.toString(formatDate_);
137 } else {
138 return dateTime.toString(formatDate_ + " " + formatTime_);
139 }
140 } else if (QSqlTableModel::fieldIndex("label") == index.column()) {
141 QStringList nameLabelList;
142 QString strIdLabels = index.data(Qt::EditRole).toString();
143 QList<QTreeWidgetItem *> labelListItems = mainApp->mainWindow()->
144 categoriesTree_->getLabelListItems();
145 foreach (QTreeWidgetItem *item, labelListItems) {
146 if (strIdLabels.contains(QString(",%1,").arg(item->text(2)))) {
147 nameLabelList << item->text(0);
148 }
149 }
150 return nameLabelList.join(", ");
151 } else if (QSqlTableModel::fieldIndex("link_href") == index.column()) {
152 QString linkStr = index.data(Qt::EditRole).toString();
153 if (linkStr.isEmpty()) {
154 linkStr = QSqlTableModel::index(index.row(), fieldIndex("link_alternate")).
155 data(Qt::EditRole).toString();
156 }
157 linkStr = linkStr.simplified();
158 linkStr = linkStr.remove("http://");
159 linkStr = linkStr.remove("https://");
160 return linkStr;
161 } else if (QSqlTableModel::fieldIndex("title") == index.column()) {
162 if (index.data(Qt::EditRole).toString().isEmpty())
163 return tr("(no title)");
164 }
165 } else if (role == Qt::FontRole) {
166 QFont font = view_->font();
167 if (0 == QSqlTableModel::index(index.row(), fieldIndex("read")).data(Qt::EditRole).toInt())
168 font.setBold(true);
169 return font;
170 } else if (role == Qt::BackgroundRole) {
171 if (index.row() == view_->currentIndex().row()) {
172 if (!focusedNewsBGColor_.isEmpty())
173 return QColor(focusedNewsBGColor_);
174 }
175
176 if (QSqlTableModel::index(index.row(), fieldIndex("label")).data(Qt::EditRole).isValid()) {
177 QString strIdLabels = QSqlTableModel::index(index.row(), fieldIndex("label")).data(Qt::EditRole).toString();
178 QList<QTreeWidgetItem *> labelListItems = mainApp->mainWindow()->
179 categoriesTree_->getLabelListItems();
180 foreach (QTreeWidgetItem *item, labelListItems) {
181 if (strIdLabels.contains(QString(",%1,").arg(item->text(2)))) {
182 QString strColor = item->data(0, CategoriesTreeWidget::colorBgRole).toString();
183 if (!strColor.isEmpty())
184 return QColor(strColor);
185 break;
186 }
187 }
188 }
189 } else if (role == Qt::TextColorRole) {
190 if (index.row() == view_->currentIndex().row()) {
191 return QColor(focusedNewsTextColor_);
192 }
193
194 if (QSqlTableModel::index(index.row(), fieldIndex("label")).data(Qt::EditRole).isValid()) {
195 QString strIdLabels = QSqlTableModel::index(index.row(), fieldIndex("label")).data(Qt::EditRole).toString();
196 QList<QTreeWidgetItem *> labelListItems = mainApp->mainWindow()->
197 categoriesTree_->getLabelListItems();
198 foreach (QTreeWidgetItem *item, labelListItems) {
199 if (strIdLabels.contains(QString(",%1,").arg(item->text(2)))) {
200 QString strColor = item->data(0, CategoriesTreeWidget::colorTextRole).toString();
201 if (!strColor.isEmpty())
202 return QColor(strColor);
203 break;
204 }
205 }
206 }
207
208 if (1 == QSqlTableModel::index(index.row(), fieldIndex("new")).data(Qt::EditRole).toInt())
209 return QColor(newNewsTextColor_);
210
211 if (0 == QSqlTableModel::index(index.row(), fieldIndex("read")).data(Qt::EditRole).toInt())
212 return QColor(unreadNewsTextColor_);
213
214 return QColor(textColor_);
215 }
216 return QSqlTableModel::data(index, role);
217 }
218
headerData(int section,Qt::Orientation orientation,int role) const219 /*virtual*/ QVariant NewsModel::headerData(int section,
220 Qt::Orientation orientation,
221 int role) const
222 {
223 if (role == Qt::DisplayRole) {
224 QString text = QSqlTableModel::headerData(section, orientation, role).toString();
225 if (text.isEmpty()) return QVariant();
226
227 int stopColFix = 0;
228 for (int i = view_->header()->count()-1; i >= 0; i--) {
229 int lIdx = view_->header()->logicalIndex(i);
230 if (!view_->header()->isSectionHidden(lIdx)) {
231 stopColFix = lIdx;
232 break;
233 }
234 }
235 int padding = 8;
236 if (stopColFix == section) padding = padding + 20;
237 text = view_->header()->fontMetrics().elidedText(
238 text, Qt::ElideRight, view_->header()->sectionSize(section)-padding);
239 return text;
240 }
241 return QSqlTableModel::headerData(section, orientation, role);
242 }
243
setData(const QModelIndex & index,const QVariant & value,int role)244 /*virtual*/ bool NewsModel::setData(const QModelIndex &index, const QVariant &value, int role)
245 {
246 return QSqlTableModel::setData(index, value, role);
247 }
248
sort(int column,Qt::SortOrder order)249 /*virtual*/ void NewsModel::sort(int column, Qt::SortOrder order)
250 {
251 int newsId = index(view_->currentIndex().row(), fieldIndex("id")).data().toInt();
252
253 if ((column == fieldIndex("read")) || (column == fieldIndex("starred")) ||
254 (column == fieldIndex("rights"))) {
255 emit signalSort(column, order);
256 column = fieldIndex("rights");
257 }
258 QSqlTableModel::sort(column, order);
259
260 while (canFetchMore())
261 fetchMore();
262
263 if (newsId > 0) {
264 QModelIndex startIndex = index(0, fieldIndex("id"));
265 QModelIndexList indexList = match(startIndex, Qt::EditRole, newsId);
266 if (indexList.count()) {
267 int newsRow = indexList.first().row();
268 view_->setCurrentIndex(index(newsRow, fieldIndex("title")));
269 }
270 }
271 }
272
match(const QModelIndex & start,int role,const QVariant & value,int hits,Qt::MatchFlags flags) const273 /*virtual*/ QModelIndexList NewsModel::match(
274 const QModelIndex &start, int role, const QVariant &value, int hits,
275 Qt::MatchFlags flags) const
276 {
277 return QSqlTableModel::match(start, role, value, hits, flags);
278 }
279
280 // ----------------------------------------------------------------------------
dataField(int row,const QString & fieldName) const281 QVariant NewsModel::dataField(int row, const QString &fieldName) const
282 {
283 return index(row, fieldIndex(fieldName)).data(Qt::EditRole);
284 }
285
setFilter(const QString & filter)286 void NewsModel::setFilter(const QString &filter)
287 {
288 QPalette palette = view_->palette();
289 palette.setColor(QPalette::AlternateBase, mainApp->mainWindow()->alternatingRowColors_);
290 view_->setPalette(palette);
291
292 QSqlTableModel::setFilter(filter);
293 }
294
select()295 bool NewsModel::select()
296 {
297 QPalette palette = view_->palette();
298 palette.setColor(QPalette::AlternateBase, mainApp->mainWindow()->alternatingRowColors_);
299 view_->setPalette(palette);
300
301 return QSqlTableModel::select();
302 }
303