]> git.sur5r.net Git - minitube/blob - src/channelview.cpp
205618ac8f0d40226535e569dd7bb133fa8a8ef1
[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 "ytsearch.h"
29 #include "channelaggregator.h"
30 #include "aggregatevideosource.h"
31 #include "painterutils.h"
32 #include "mainwindow.h"
33 #include "iconutils.h"
34 #ifdef APP_EXTRA
35 #include "extra.h"
36 #endif
37
38 static const char *sortByKey = "subscriptionsSortBy";
39 static const char *showUpdatedKey = "subscriptionsShowUpdated";
40
41 ChannelView::ChannelView(QWidget *parent) : QListView(parent),
42     showUpdated(false),
43     sortBy(SortByName) {
44
45     setItemDelegate(new ChannelItemDelegate(this));
46     setSelectionMode(QAbstractItemView::NoSelection);
47
48     // layout
49     setSpacing(15);
50     setFlow(QListView::LeftToRight);
51     setWrapping(true);
52     setResizeMode(QListView::Adjust);
53     setMovement(QListView::Static);
54     setUniformItemSizes(true);
55
56     // cosmetics
57     setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
58     setFrameShape(QFrame::NoFrame);
59     setAttribute(Qt::WA_MacShowFocusRect, false);
60
61     QPalette p = palette();
62     /*
63     p.setColor(QPalette::Base, p.window().color());
64     p.setColor(QPalette::Text, p.windowText().color());
65     */
66     p.setColor(QPalette::Disabled, QPalette::Base, p.base().color());
67     p.setColor(QPalette::Disabled, QPalette::Text, p.text().color());
68     setPalette(p);
69
70     verticalScrollBar()->setPageStep(3);
71     verticalScrollBar()->setSingleStep(1);
72
73     setMouseTracking(true);
74
75     connect(this, SIGNAL(clicked(const QModelIndex &)),
76             SLOT(itemActivated(const QModelIndex &)));
77     connect(this, SIGNAL(entered(const QModelIndex &)),
78             SLOT(itemEntered(const QModelIndex &)));
79
80     channelsModel = new ChannelModel(this);
81     setModel(channelsModel);
82     connect(this, SIGNAL(viewportEntered()),
83             channelsModel, SLOT(clearHover()));
84
85     setupActions();
86
87     connect(ChannelAggregator::instance(), SIGNAL(channelChanged(YTChannel*)),
88             channelsModel, SLOT(updateChannel(YTChannel*)));
89     connect(ChannelAggregator::instance(), SIGNAL(unwatchedCountChanged(int)),
90             SLOT(unwatchedCountChanged(int)));
91
92     unwatchedCountChanged(ChannelAggregator::instance()->getUnwatchedCount());
93 }
94
95 void ChannelView::setupActions() {
96     QSettings settings;
97
98     sortBy = static_cast<SortBy>(settings.value(sortByKey, SortByName).toInt());
99
100     QMenu *sortMenu = new QMenu(this);
101     QActionGroup *sortGroup = new QActionGroup(this);
102
103     QAction *sortByNameAction = new QAction(tr("Name"), this);
104     sortByNameAction->setActionGroup(sortGroup);
105     sortByNameAction->setCheckable(true);
106     if (sortBy == SortByName) sortByNameAction->setChecked(true);
107     connect(sortByNameAction, SIGNAL(triggered()), SLOT(setSortByName()));
108     sortMenu->addAction(sortByNameAction);
109
110     QAction *sortByUpdatedAction = new QAction(tr("Last Updated"), this);
111     sortByUpdatedAction->setActionGroup(sortGroup);
112     sortByUpdatedAction->setCheckable(true);
113     if (sortBy == SortByUpdated) sortByUpdatedAction->setChecked(true);
114     connect(sortByUpdatedAction, SIGNAL(triggered()), SLOT(setSortByUpdated()));
115     sortMenu->addAction(sortByUpdatedAction);
116
117     QAction *sortByAddedAction = new QAction(tr("Last Added"), this);
118     sortByAddedAction->setActionGroup(sortGroup);
119     sortByAddedAction->setCheckable(true);
120     if (sortBy == SortByAdded) sortByAddedAction->setChecked(true);
121     connect(sortByAddedAction, SIGNAL(triggered()), SLOT(setSortByAdded()));
122     sortMenu->addAction(sortByAddedAction);
123
124     QAction *sortByLastWatched = new QAction(tr("Last Watched"), this);
125     sortByLastWatched->setActionGroup(sortGroup);
126     sortByLastWatched->setCheckable(true);
127     if (sortBy == SortByLastWatched) sortByLastWatched->setChecked(true);
128     connect(sortByLastWatched, SIGNAL(triggered()), SLOT(setSortByLastWatched()));
129     sortMenu->addAction(sortByLastWatched);
130
131     QAction *sortByMostWatched = new QAction(tr("Most Watched"), this);
132     sortByMostWatched->setActionGroup(sortGroup);
133     sortByMostWatched->setCheckable(true);
134     if (sortBy == SortByMostWatched) sortByMostWatched->setChecked(true);
135     connect(sortByMostWatched, SIGNAL(triggered()), SLOT(setSortByMostWatched()));
136     sortMenu->addAction(sortByMostWatched);
137
138     QToolButton *sortButton = new QToolButton(this);
139     sortButton->setText(tr("Sort by"));
140     sortButton->setIcon(IconUtils::icon("sort"));
141     sortButton->setIconSize(QSize(16, 16));
142     sortButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
143     sortButton->setPopupMode(QToolButton::InstantPopup);
144     sortButton->setMenu(sortMenu);
145     QWidgetAction *widgetAction = new QWidgetAction(this);
146     widgetAction->setDefaultWidget(sortButton);
147     widgetAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
148     statusActions << widgetAction;
149
150     markAsWatchedAction = new QAction(
151                 IconUtils::icon("mark-watched"), tr("Mark all as watched"), this);
152     markAsWatchedAction->setEnabled(false);
153     markAsWatchedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W));
154     connect(markAsWatchedAction, SIGNAL(triggered()), SLOT(markAllAsWatched()));
155     statusActions << markAsWatchedAction;
156
157     showUpdated = settings.value(showUpdatedKey, false).toBool();
158     QAction *showUpdatedAction = new QAction(
159                 IconUtils::icon("show-updated"), tr("Show Updated"), this);
160     showUpdatedAction->setCheckable(true);
161     showUpdatedAction->setChecked(showUpdated);
162     showUpdatedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U));
163     connect(showUpdatedAction, SIGNAL(toggled(bool)), SLOT(toggleShowUpdated(bool)));
164     statusActions << showUpdatedAction;
165
166     foreach (QAction *action, statusActions) {
167         window()->addAction(action);
168         IconUtils::setupAction(action);
169     }
170 }
171
172 void ChannelView::appear() {
173     updateQuery();
174     foreach (QAction* action, statusActions)
175         MainWindow::instance()->showActionInStatusBar(action, true);
176     setFocus();
177     ChannelAggregator::instance()->start();
178 }
179
180 void ChannelView::disappear() {
181     ChannelAggregator::instance()->stop();
182     foreach (QAction* action, statusActions)
183         MainWindow::instance()->showActionInStatusBar(action, false);
184 }
185
186 void ChannelView::mousePressEvent(QMouseEvent *event) {
187     if (event->button() == Qt::RightButton)
188         showContextMenu(event->pos());
189     else
190         QListView::mousePressEvent(event);
191 }
192
193 void ChannelView::mouseMoveEvent(QMouseEvent *event) {
194     QListView::mouseMoveEvent(event);
195     const QModelIndex index = indexAt(event->pos());
196     if (index.isValid()) setCursor(Qt::PointingHandCursor);
197     else unsetCursor();
198 }
199
200 void ChannelView::leaveEvent(QEvent *event) {
201     QListView::leaveEvent(event);
202     // channelsModel->clearHover();
203 }
204
205 void ChannelView::itemEntered(const QModelIndex &) {
206     // channelsModel->setHoveredRow(index.row());
207 }
208
209 void ChannelView::itemActivated(const QModelIndex &index) {
210     ChannelModel::ItemTypes itemType = channelsModel->typeForIndex(index);
211     if (itemType == ChannelModel::ItemChannel) {
212         YTChannel *channel = channelsModel->channelForIndex(index);
213         SearchParams *params = new SearchParams();
214         params->setChannelId(channel->getChannelId());
215         params->setSortBy(SearchParams::SortByNewest);
216         params->setTransient(true);
217         YTSearch *videoSource = new YTSearch(params, this);
218         videoSource->setAsyncDetails(true);
219         emit activated(videoSource);
220         channel->updateWatched();
221     } else if (itemType == ChannelModel::ItemAggregate) {
222         AggregateVideoSource *videoSource = new AggregateVideoSource(this);
223         videoSource->setName(tr("All Videos"));
224         emit activated(videoSource);
225     } else if (itemType == ChannelModel::ItemUnwatched) {
226         AggregateVideoSource *videoSource = new AggregateVideoSource(this);
227         videoSource->setName(tr("Unwatched Videos"));
228         videoSource->setUnwatched(true);
229         emit activated(videoSource);
230     }
231 }
232
233 void ChannelView::showContextMenu(const QPoint &point) {
234     const QModelIndex index = indexAt(point);
235     if (!index.isValid()) return;
236
237     YTChannel *channel = channelsModel->channelForIndex(index);
238     if (!channel) return;
239
240     unsetCursor();
241
242     QMenu menu;
243
244     if (channel->getNotifyCount() > 0) {
245         QAction *markAsWatchedAction = menu.addAction(tr("Mark as Watched"), channel, SLOT(updateWatched()));
246         connect(markAsWatchedAction, SIGNAL(triggered()),
247                 ChannelAggregator::instance(), SLOT(updateUnwatchedCount()));
248         menu.addSeparator();
249     }
250
251     /*
252     // TODO
253     QAction *notificationsAction = menu.addAction(tr("Receive Notifications"), user, SLOT(unsubscribe()));
254     notificationsAction->setCheckable(true);
255     notificationsAction->setChecked(true);
256     */
257
258     QAction *unsubscribeAction = menu.addAction(tr("Unsubscribe"), channel, SLOT(unsubscribe()));
259     connect(unsubscribeAction, SIGNAL(triggered()),
260             ChannelAggregator::instance(), SLOT(updateUnwatchedCount()));
261
262     menu.exec(mapToGlobal(point));
263 }
264
265 void ChannelView::paintEvent(QPaintEvent *event) {
266     if (model()->rowCount() < 3) {
267         QString msg;
268         if (!errorMessage.isEmpty())
269             msg = errorMessage;
270         else if (showUpdated)
271             msg = tr("There are no updated subscriptions at this time.");
272         else
273             msg = tr("You have no subscriptions. "
274                      "Use the star symbol to subscribe to channels.");
275         PainterUtils::centeredMessage(msg, viewport());
276     } else QListView::paintEvent(event);
277     PainterUtils::topShadow(viewport());
278 }
279
280 void ChannelView::toggleShowUpdated(bool enable) {
281     showUpdated = enable;
282     updateQuery(true);
283     QSettings settings;
284     settings.setValue(showUpdatedKey, showUpdated);
285 }
286
287 void ChannelView::updateQuery(bool transition) {
288     Q_UNUSED(transition);
289     errorMessage.clear();
290     if (!Database::exists()) return;
291
292     QString sql = "select user_id from subscriptions";
293     if (showUpdated)
294         sql += " where notify_count>0";
295
296     switch (sortBy) {
297     case SortByUpdated:
298         sql += " order by updated desc";
299         break;
300     case SortByAdded:
301         sql += " order by added desc";
302         break;
303     case SortByLastWatched:
304         sql += " order by watched desc";
305         break;
306     case SortByMostWatched:
307         sql += " order by views desc";
308         break;
309     default:
310         sql += " order by name collate nocase";
311         break;
312     }
313
314 #ifdef APP_EXTRA
315     if (transition)
316         Extra::fadeInWidget(this, this);
317 #endif
318
319     channelsModel->setQuery(sql, Database::instance().getConnection());
320     if (channelsModel->lastError().isValid()) {
321         qWarning() << channelsModel->lastError().text();
322         errorMessage = channelsModel->lastError().text();
323     }
324 }
325
326 void ChannelView::setSortBy(SortBy sortBy) {
327     this->sortBy = sortBy;
328     updateQuery(true);
329     QSettings settings;
330     settings.setValue(sortByKey, (int)sortBy);
331 }
332
333 void ChannelView::markAllAsWatched() {
334     ChannelAggregator::instance()->markAllAsWatched();
335     updateQuery();
336     markAsWatchedAction->setEnabled(false);
337 }
338
339 void ChannelView::unwatchedCountChanged(int count) {
340     markAsWatchedAction->setEnabled(count > 0);
341     channelsModel->updateUnwatched();
342     updateQuery();
343 }