]> git.sur5r.net Git - minitube/blob - src/channelview.cpp
New upstream version 3.8
[minitube] / src / channelview.cpp
1 /* $BEGIN_LICENSE
2
3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
5
6 Minitube is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
18
19 $END_LICENSE */
20
21 #include "channelview.h"
22 #include "aggregatevideosource.h"
23 #include "channelaggregator.h"
24 #include "channelitemdelegate.h"
25 #include "channelmodel.h"
26 #include "database.h"
27 #include "iconutils.h"
28 #include "mainwindow.h"
29 #include "searchparams.h"
30 #include "ytchannel.h"
31 #include "ytsearch.h"
32 #ifdef APP_EXTRA
33 #include "extra.h"
34 #endif
35 #include "channellistview.h"
36
37 #include "ivchannelsource.h"
38 #include "videoapi.h"
39 #include "ytjschannelsource.h"
40
41 namespace {
42 const QString sortByKey = "subscriptionsSortBy";
43 const QString showUpdatedKey = "subscriptionsShowUpdated";
44 } // namespace
45
46 ChannelView::ChannelView(QWidget *parent) : View(parent), showUpdated(false), sortBy(SortByName) {
47     QBoxLayout *layout = new QVBoxLayout(this);
48     layout->setMargin(0);
49     layout->setSpacing(0);
50
51     listView = new ChannelListView();
52     listView->setItemDelegate(new ChannelItemDelegate(this));
53
54     channelsModel = new ChannelModel(this);
55     listView->setModel(channelsModel);
56
57     connect(listView, SIGNAL(clicked(const QModelIndex &)),
58             SLOT(itemActivated(const QModelIndex &)));
59     connect(listView, SIGNAL(contextMenu(QPoint)), SLOT(showContextMenu(QPoint)));
60     connect(listView, SIGNAL(viewportEntered()), channelsModel, SLOT(clearHover()));
61
62     layout->addWidget(listView);
63
64     setupActions();
65
66     connect(ChannelAggregator::instance(), SIGNAL(channelChanged(YTChannel *)), channelsModel,
67             SLOT(updateChannel(YTChannel *)));
68     connect(ChannelAggregator::instance(), SIGNAL(unwatchedCountChanged(int)),
69             SLOT(unwatchedCountChanged(int)));
70
71     unwatchedCountChanged(ChannelAggregator::instance()->getUnwatchedCount());
72 }
73
74 void ChannelView::setupActions() {
75     QSettings settings;
76
77     statusActions << MainWindow::instance()->getAction("importSubscriptions");
78
79     sortBy = static_cast<SortBy>(settings.value(sortByKey, SortByName).toInt());
80
81     QMenu *sortMenu = new QMenu(this);
82     QActionGroup *sortGroup = new QActionGroup(this);
83
84     QAction *sortByNameAction = new QAction(tr("Name"), this);
85     sortByNameAction->setActionGroup(sortGroup);
86     sortByNameAction->setCheckable(true);
87     if (sortBy == SortByName) sortByNameAction->setChecked(true);
88     connect(sortByNameAction, SIGNAL(triggered()), SLOT(setSortByName()));
89     sortMenu->addAction(sortByNameAction);
90
91     QAction *sortByUpdatedAction = new QAction(tr("Last Updated"), this);
92     sortByUpdatedAction->setActionGroup(sortGroup);
93     sortByUpdatedAction->setCheckable(true);
94     if (sortBy == SortByUpdated) sortByUpdatedAction->setChecked(true);
95     connect(sortByUpdatedAction, SIGNAL(triggered()), SLOT(setSortByUpdated()));
96     sortMenu->addAction(sortByUpdatedAction);
97
98     QAction *sortByAddedAction = new QAction(tr("Last Added"), this);
99     sortByAddedAction->setActionGroup(sortGroup);
100     sortByAddedAction->setCheckable(true);
101     if (sortBy == SortByAdded) sortByAddedAction->setChecked(true);
102     connect(sortByAddedAction, SIGNAL(triggered()), SLOT(setSortByAdded()));
103     sortMenu->addAction(sortByAddedAction);
104
105     QAction *sortByLastWatched = new QAction(tr("Last Watched"), this);
106     sortByLastWatched->setActionGroup(sortGroup);
107     sortByLastWatched->setCheckable(true);
108     if (sortBy == SortByLastWatched) sortByLastWatched->setChecked(true);
109     connect(sortByLastWatched, SIGNAL(triggered()), SLOT(setSortByLastWatched()));
110     sortMenu->addAction(sortByLastWatched);
111
112     QAction *sortByMostWatched = new QAction(tr("Most Watched"), this);
113     sortByMostWatched->setActionGroup(sortGroup);
114     sortByMostWatched->setCheckable(true);
115     if (sortBy == SortByMostWatched) sortByMostWatched->setChecked(true);
116     connect(sortByMostWatched, SIGNAL(triggered()), SLOT(setSortByMostWatched()));
117     sortMenu->addAction(sortByMostWatched);
118
119     QToolButton *sortButton = new QToolButton(this);
120     sortButton->setText(tr("Sort by"));
121     IconUtils::setIcon(sortButton, "sort");
122     sortButton->setIconSize(QSize(16, 16));
123     sortButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
124     sortButton->setPopupMode(QToolButton::InstantPopup);
125     sortButton->setMenu(sortMenu);
126     QWidgetAction *widgetAction = new QWidgetAction(this);
127     widgetAction->setDefaultWidget(sortButton);
128     widgetAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
129     statusActions << widgetAction;
130
131     markAsWatchedAction = new QAction(tr("Mark all as watched"), this);
132     IconUtils::setIcon(markAsWatchedAction, "mark-watched");
133     markAsWatchedAction->setEnabled(false);
134     markAsWatchedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W));
135     connect(markAsWatchedAction, SIGNAL(triggered()), SLOT(markAllAsWatched()));
136     statusActions << markAsWatchedAction;
137
138     showUpdated = settings.value(showUpdatedKey, false).toBool();
139     QAction *showUpdatedAction = new QAction(tr("Show Updated"), this);
140     IconUtils::setIcon(showUpdatedAction, "show-updated");
141     showUpdatedAction->setCheckable(true);
142     showUpdatedAction->setChecked(showUpdated);
143     showUpdatedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U));
144     connect(showUpdatedAction, SIGNAL(toggled(bool)), SLOT(toggleShowUpdated(bool)));
145     statusActions << showUpdatedAction;
146
147     for (QAction *action : statusActions) {
148         window()->addAction(action);
149         MainWindow::instance()->setupAction(action);
150     }
151 }
152
153 QString ChannelView::noSubscriptionsMessage() {
154     return tr("You have no subscriptions. "
155               "Use the star symbol to subscribe to channels.");
156 }
157
158 void ChannelView::appear() {
159     updateQuery(true);
160     MainWindow::instance()->showActionsInStatusBar(statusActions, true);
161     setFocus();
162     ChannelAggregator::instance()->start();
163 }
164
165 void ChannelView::disappear() {
166     MainWindow::instance()->showActionsInStatusBar(statusActions, false);
167 }
168
169 void ChannelView::itemActivated(const QModelIndex &index) {
170     ChannelModel::ItemTypes itemType = channelsModel->typeForIndex(index);
171     if (itemType == ChannelModel::ItemChannel) {
172         YTChannel *channel = channelsModel->channelForIndex(index);
173         SearchParams *params = new SearchParams();
174         params->setChannelId(channel->getChannelId());
175         params->setSortBy(SearchParams::SortByNewest);
176         params->setTransient(true);
177         VideoSource *vs = nullptr;
178         if (VideoAPI::impl() == VideoAPI::YT3) {
179             YTSearch *videoSource = new YTSearch(params);
180             videoSource->setAsyncDetails(true);
181             vs = videoSource;
182         } else if (VideoAPI::impl() == VideoAPI::IV) {
183             vs = new IVChannelSource(params);
184         } else if (VideoAPI::impl() == VideoAPI::JS) {
185             vs = new YTJSChannelSource(params);
186         }
187         emit activated(vs);
188         channel->updateWatched();
189     } else if (itemType == ChannelModel::ItemAggregate) {
190         AggregateVideoSource *videoSource = new AggregateVideoSource();
191         videoSource->setName(tr("All Videos"));
192         emit activated(videoSource);
193     } else if (itemType == ChannelModel::ItemUnwatched) {
194         AggregateVideoSource *videoSource = new AggregateVideoSource();
195         videoSource->setName(tr("Unwatched Videos"));
196         videoSource->setUnwatched(true);
197         emit activated(videoSource);
198     }
199 }
200
201 void ChannelView::showContextMenu(const QPoint &point) {
202     const QModelIndex index = listView->indexAt(point);
203     if (!index.isValid()) return;
204
205     YTChannel *channel = channelsModel->channelForIndex(index);
206     if (!channel) return;
207
208     unsetCursor();
209
210     QMenu menu;
211
212     if (channel->getNotifyCount() > 0) {
213         QAction *markAsWatchedAction =
214                 menu.addAction(tr("Mark as Watched"), channel, SLOT(updateWatched()));
215         connect(markAsWatchedAction, SIGNAL(triggered()), ChannelAggregator::instance(),
216                 SLOT(updateUnwatchedCount()));
217         menu.addSeparator();
218     }
219
220     /*
221     // TODO
222     QAction *notificationsAction = menu.addAction(tr("Receive Notifications"), user,
223     SLOT(unsubscribe())); notificationsAction->setCheckable(true);
224     notificationsAction->setChecked(true);
225     */
226
227     QAction *unsubscribeAction = menu.addAction(tr("Unsubscribe"), channel, SLOT(unsubscribe()));
228     connect(unsubscribeAction, SIGNAL(triggered()), ChannelAggregator::instance(),
229             SLOT(updateUnwatchedCount()));
230
231     menu.exec(mapToGlobal(point));
232 }
233
234 void ChannelView::toggleShowUpdated(bool enable) {
235     showUpdated = enable;
236     updateQuery(true);
237     QSettings settings;
238     settings.setValue(showUpdatedKey, showUpdated);
239 }
240
241 void ChannelView::updateQuery(bool transition) {
242     Q_UNUSED(transition);
243     listView->clearErrorMessage();
244
245     if (!Database::exists()) {
246         listView->setErrorMessage(noSubscriptionsMessage());
247         return;
248     }
249
250     QString sql = "select user_id from subscriptions";
251     if (showUpdated) sql += " where notify_count>0";
252
253     switch (sortBy) {
254     case SortByUpdated:
255         sql += " order by updated desc";
256         break;
257     case SortByAdded:
258         sql += " order by added desc";
259         break;
260     case SortByLastWatched:
261         sql += " order by watched desc";
262         break;
263     case SortByMostWatched:
264         sql += " order by views desc";
265         break;
266     default:
267         sql += " order by name collate nocase";
268         break;
269     }
270
271 #ifdef APP_EXTRA
272     if (transition) Extra::fadeInWidget(this, this);
273 #endif
274
275     channelsModel->setQuery(sql, Database::instance().getConnection());
276     if (channelsModel->lastError().isValid()) {
277         qWarning() << channelsModel->lastError().text();
278         listView->setErrorMessage(channelsModel->lastError().text());
279     } else if (channelsModel->rowCount() < 3) {
280         QString msg;
281         if (showUpdated)
282             msg = tr("There are no updated subscriptions at this time.");
283         else
284             msg = noSubscriptionsMessage();
285         listView->setErrorMessage(msg);
286     }
287 }
288
289 void ChannelView::setSortBy(SortBy sortBy) {
290     this->sortBy = sortBy;
291     updateQuery(true);
292     QSettings settings;
293     settings.setValue(sortByKey, (int)sortBy);
294 }
295
296 void ChannelView::markAllAsWatched() {
297     ChannelAggregator::instance()->markAllAsWatched();
298     updateQuery();
299     markAsWatchedAction->setEnabled(false);
300 }
301
302 void ChannelView::unwatchedCountChanged(int count) {
303     markAsWatchedAction->setEnabled(count > 0);
304     channelsModel->updateUnwatched();
305     updateQuery();
306 }