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