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