]> git.sur5r.net Git - minitube/blob - src/channelview.cpp
New upstream version 3.5.1
[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 "videoapi.h"
38 #include "ivchannelsource.h"
39
40 namespace {
41 const QString sortByKey = "subscriptionsSortBy";
42 const QString showUpdatedKey = "subscriptionsShowUpdated";
43 } // namespace
44
45 ChannelView::ChannelView(QWidget *parent) : View(parent), showUpdated(false), sortBy(SortByName) {
46     QBoxLayout *layout = new QVBoxLayout(this);
47     layout->setMargin(0);
48     layout->setSpacing(0);
49
50     listView = new ChannelListView();
51     listView->setItemDelegate(new ChannelItemDelegate(this));
52
53     channelsModel = new ChannelModel(this);
54     listView->setModel(channelsModel);
55
56     connect(listView, SIGNAL(clicked(const QModelIndex &)),
57             SLOT(itemActivated(const QModelIndex &)));
58     connect(listView, SIGNAL(contextMenu(QPoint)), SLOT(showContextMenu(QPoint)));
59     connect(listView, SIGNAL(viewportEntered()), channelsModel, SLOT(clearHover()));
60
61     layout->addWidget(listView);
62
63     setupActions();
64
65     connect(ChannelAggregator::instance(), SIGNAL(channelChanged(YTChannel *)), channelsModel,
66             SLOT(updateChannel(YTChannel *)));
67     connect(ChannelAggregator::instance(), SIGNAL(unwatchedCountChanged(int)),
68             SLOT(unwatchedCountChanged(int)));
69
70     unwatchedCountChanged(ChannelAggregator::instance()->getUnwatchedCount());
71 }
72
73 void ChannelView::setupActions() {
74     QSettings settings;
75
76     sortBy = static_cast<SortBy>(settings.value(sortByKey, SortByName).toInt());
77
78     QMenu *sortMenu = new QMenu(this);
79     QActionGroup *sortGroup = new QActionGroup(this);
80
81     QAction *sortByNameAction = new QAction(tr("Name"), this);
82     sortByNameAction->setActionGroup(sortGroup);
83     sortByNameAction->setCheckable(true);
84     if (sortBy == SortByName) sortByNameAction->setChecked(true);
85     connect(sortByNameAction, SIGNAL(triggered()), SLOT(setSortByName()));
86     sortMenu->addAction(sortByNameAction);
87
88     QAction *sortByUpdatedAction = new QAction(tr("Last Updated"), this);
89     sortByUpdatedAction->setActionGroup(sortGroup);
90     sortByUpdatedAction->setCheckable(true);
91     if (sortBy == SortByUpdated) sortByUpdatedAction->setChecked(true);
92     connect(sortByUpdatedAction, SIGNAL(triggered()), SLOT(setSortByUpdated()));
93     sortMenu->addAction(sortByUpdatedAction);
94
95     QAction *sortByAddedAction = new QAction(tr("Last Added"), this);
96     sortByAddedAction->setActionGroup(sortGroup);
97     sortByAddedAction->setCheckable(true);
98     if (sortBy == SortByAdded) sortByAddedAction->setChecked(true);
99     connect(sortByAddedAction, SIGNAL(triggered()), SLOT(setSortByAdded()));
100     sortMenu->addAction(sortByAddedAction);
101
102     QAction *sortByLastWatched = new QAction(tr("Last Watched"), this);
103     sortByLastWatched->setActionGroup(sortGroup);
104     sortByLastWatched->setCheckable(true);
105     if (sortBy == SortByLastWatched) sortByLastWatched->setChecked(true);
106     connect(sortByLastWatched, SIGNAL(triggered()), SLOT(setSortByLastWatched()));
107     sortMenu->addAction(sortByLastWatched);
108
109     QAction *sortByMostWatched = new QAction(tr("Most Watched"), this);
110     sortByMostWatched->setActionGroup(sortGroup);
111     sortByMostWatched->setCheckable(true);
112     if (sortBy == SortByMostWatched) sortByMostWatched->setChecked(true);
113     connect(sortByMostWatched, SIGNAL(triggered()), SLOT(setSortByMostWatched()));
114     sortMenu->addAction(sortByMostWatched);
115
116     QToolButton *sortButton = new QToolButton(this);
117     sortButton->setText(tr("Sort by"));
118     IconUtils::setIcon(sortButton, "sort");
119     sortButton->setIconSize(QSize(16, 16));
120     sortButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
121     sortButton->setPopupMode(QToolButton::InstantPopup);
122     sortButton->setMenu(sortMenu);
123     QWidgetAction *widgetAction = new QWidgetAction(this);
124     widgetAction->setDefaultWidget(sortButton);
125     widgetAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
126     statusActions << widgetAction;
127
128     markAsWatchedAction = new QAction(tr("Mark all as watched"), this);
129     IconUtils::setIcon(markAsWatchedAction, "mark-watched");
130     markAsWatchedAction->setEnabled(false);
131     markAsWatchedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W));
132     connect(markAsWatchedAction, SIGNAL(triggered()), SLOT(markAllAsWatched()));
133     statusActions << markAsWatchedAction;
134
135     showUpdated = settings.value(showUpdatedKey, false).toBool();
136     QAction *showUpdatedAction = new QAction(tr("Show Updated"), this);
137     IconUtils::setIcon(showUpdatedAction, "show-updated");
138     showUpdatedAction->setCheckable(true);
139     showUpdatedAction->setChecked(showUpdated);
140     showUpdatedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U));
141     connect(showUpdatedAction, SIGNAL(toggled(bool)), SLOT(toggleShowUpdated(bool)));
142     statusActions << showUpdatedAction;
143
144     for (QAction *action : statusActions) {
145         window()->addAction(action);
146         MainWindow::instance()->setupAction(action);
147     }
148 }
149
150 QString ChannelView::noSubscriptionsMessage() {
151     return tr("You have no subscriptions. "
152               "Use the star symbol to subscribe to channels.");
153 }
154
155 void ChannelView::appear() {
156     updateQuery(true);
157     MainWindow::instance()->showActionsInStatusBar(statusActions, true);
158     setFocus();
159     ChannelAggregator::instance()->start();
160 }
161
162 void ChannelView::disappear() {
163     MainWindow::instance()->showActionsInStatusBar(statusActions, false);
164 }
165
166 void ChannelView::itemActivated(const QModelIndex &index) {
167     ChannelModel::ItemTypes itemType = channelsModel->typeForIndex(index);
168     if (itemType == ChannelModel::ItemChannel) {
169         YTChannel *channel = channelsModel->channelForIndex(index);
170         SearchParams *params = new SearchParams();
171         params->setChannelId(channel->getChannelId());
172         params->setSortBy(SearchParams::SortByNewest);
173         params->setTransient(true);
174         VideoSource *vs = nullptr;
175         if (VideoAPI::impl() == VideoAPI::YT3) {
176             YTSearch *videoSource = new YTSearch(params);
177             videoSource->setAsyncDetails(true);
178             vs = videoSource;
179         } else if (VideoAPI::impl() == VideoAPI::IV) {
180             vs = new IVChannelSource(params);
181         }
182         emit activated(vs);
183         channel->updateWatched();
184     } else if (itemType == ChannelModel::ItemAggregate) {
185         AggregateVideoSource *videoSource = new AggregateVideoSource();
186         videoSource->setName(tr("All Videos"));
187         emit activated(videoSource);
188     } else if (itemType == ChannelModel::ItemUnwatched) {
189         AggregateVideoSource *videoSource = new AggregateVideoSource();
190         videoSource->setName(tr("Unwatched Videos"));
191         videoSource->setUnwatched(true);
192         emit activated(videoSource);
193     }
194 }
195
196 void ChannelView::showContextMenu(const QPoint &point) {
197     const QModelIndex index = listView->indexAt(point);
198     if (!index.isValid()) return;
199
200     YTChannel *channel = channelsModel->channelForIndex(index);
201     if (!channel) return;
202
203     unsetCursor();
204
205     QMenu menu;
206
207     if (channel->getNotifyCount() > 0) {
208         QAction *markAsWatchedAction =
209                 menu.addAction(tr("Mark as Watched"), channel, SLOT(updateWatched()));
210         connect(markAsWatchedAction, SIGNAL(triggered()), ChannelAggregator::instance(),
211                 SLOT(updateUnwatchedCount()));
212         menu.addSeparator();
213     }
214
215     /*
216     // TODO
217     QAction *notificationsAction = menu.addAction(tr("Receive Notifications"), user,
218     SLOT(unsubscribe())); notificationsAction->setCheckable(true);
219     notificationsAction->setChecked(true);
220     */
221
222     QAction *unsubscribeAction = menu.addAction(tr("Unsubscribe"), channel, SLOT(unsubscribe()));
223     connect(unsubscribeAction, SIGNAL(triggered()), ChannelAggregator::instance(),
224             SLOT(updateUnwatchedCount()));
225
226     menu.exec(mapToGlobal(point));
227 }
228
229 void ChannelView::toggleShowUpdated(bool enable) {
230     showUpdated = enable;
231     updateQuery(true);
232     QSettings settings;
233     settings.setValue(showUpdatedKey, showUpdated);
234 }
235
236 void ChannelView::updateQuery(bool transition) {
237     Q_UNUSED(transition);
238     listView->clearErrorMessage();
239
240     if (!Database::exists()) {
241         listView->setErrorMessage(noSubscriptionsMessage());
242         return;
243     }
244
245     QString sql = "select user_id from subscriptions";
246     if (showUpdated) sql += " where notify_count>0";
247
248     switch (sortBy) {
249     case SortByUpdated:
250         sql += " order by updated desc";
251         break;
252     case SortByAdded:
253         sql += " order by added desc";
254         break;
255     case SortByLastWatched:
256         sql += " order by watched desc";
257         break;
258     case SortByMostWatched:
259         sql += " order by views desc";
260         break;
261     default:
262         sql += " order by name collate nocase";
263         break;
264     }
265
266 #ifdef APP_EXTRA
267     if (transition) Extra::fadeInWidget(this, this);
268 #endif
269
270     channelsModel->setQuery(sql, Database::instance().getConnection());
271     if (channelsModel->lastError().isValid()) {
272         qWarning() << channelsModel->lastError().text();
273         listView->setErrorMessage(channelsModel->lastError().text());
274     } else if (channelsModel->rowCount() < 3) {
275         QString msg;
276         if (showUpdated)
277             msg = tr("There are no updated subscriptions at this time.");
278         else
279             msg = noSubscriptionsMessage();
280         listView->setErrorMessage(msg);
281     }
282 }
283
284 void ChannelView::setSortBy(SortBy sortBy) {
285     this->sortBy = sortBy;
286     updateQuery(true);
287     QSettings settings;
288     settings.setValue(sortByKey, (int)sortBy);
289 }
290
291 void ChannelView::markAllAsWatched() {
292     ChannelAggregator::instance()->markAllAsWatched();
293     updateQuery();
294     markAsWatchedAction->setEnabled(false);
295 }
296
297 void ChannelView::unwatchedCountChanged(int count) {
298     markAsWatchedAction->setEnabled(count > 0);
299     channelsModel->updateUnwatched();
300     updateQuery();
301 }