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