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