3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
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.
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.
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/>.
21 #include "channelview.h"
22 #include "ytchannel.h"
24 #include "searchparams.h"
25 #include "channelmodel.h"
26 #include "channelitemdelegate.h"
29 #include "channelaggregator.h"
30 #include "aggregatevideosource.h"
31 #include "painterutils.h"
32 #include "mainwindow.h"
33 #include "iconutils.h"
38 static const char *sortByKey = "subscriptionsSortBy";
39 static const char *showUpdatedKey = "subscriptionsShowUpdated";
41 ChannelView::ChannelView(QWidget *parent) : QListView(parent),
45 setItemDelegate(new ChannelItemDelegate(this));
46 setSelectionMode(QAbstractItemView::NoSelection);
50 setFlow(QListView::LeftToRight);
52 setResizeMode(QListView::Adjust);
53 setMovement(QListView::Static);
54 setUniformItemSizes(true);
57 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
58 setFrameShape(QFrame::NoFrame);
59 setAttribute(Qt::WA_MacShowFocusRect, false);
61 QPalette p = palette();
63 p.setColor(QPalette::Base, p.window().color());
64 p.setColor(QPalette::Text, p.windowText().color());
66 p.setColor(QPalette::Disabled, QPalette::Base, p.base().color());
67 p.setColor(QPalette::Disabled, QPalette::Text, p.text().color());
70 verticalScrollBar()->setPageStep(3);
71 verticalScrollBar()->setSingleStep(1);
73 setMouseTracking(true);
75 connect(this, SIGNAL(clicked(const QModelIndex &)),
76 SLOT(itemActivated(const QModelIndex &)));
77 connect(this, SIGNAL(entered(const QModelIndex &)),
78 SLOT(itemEntered(const QModelIndex &)));
80 channelsModel = new ChannelModel(this);
81 setModel(channelsModel);
82 connect(this, SIGNAL(viewportEntered()),
83 channelsModel, SLOT(clearHover()));
87 connect(ChannelAggregator::instance(), SIGNAL(channelChanged(YTChannel*)),
88 channelsModel, SLOT(updateChannel(YTChannel*)));
89 connect(ChannelAggregator::instance(), SIGNAL(unwatchedCountChanged(int)),
90 SLOT(unwatchedCountChanged(int)));
92 unwatchedCountChanged(ChannelAggregator::instance()->getUnwatchedCount());
95 void ChannelView::setupActions() {
98 sortBy = static_cast<SortBy>(settings.value(sortByKey, SortByName).toInt());
100 QMenu *sortMenu = new QMenu(this);
101 QActionGroup *sortGroup = new QActionGroup(this);
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);
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);
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);
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);
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);
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;
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;
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;
166 foreach (QAction *action, statusActions) {
167 window()->addAction(action);
168 IconUtils::setupAction(action);
172 void ChannelView::appear() {
174 foreach (QAction* action, statusActions)
175 MainWindow::instance()->showActionInStatusBar(action, true);
177 ChannelAggregator::instance()->start();
180 void ChannelView::disappear() {
181 ChannelAggregator::instance()->stop();
182 foreach (QAction* action, statusActions)
183 MainWindow::instance()->showActionInStatusBar(action, false);
186 void ChannelView::mousePressEvent(QMouseEvent *event) {
187 if (event->button() == Qt::RightButton)
188 showContextMenu(event->pos());
190 QListView::mousePressEvent(event);
193 void ChannelView::mouseMoveEvent(QMouseEvent *event) {
194 QListView::mouseMoveEvent(event);
195 const QModelIndex index = indexAt(event->pos());
196 if (index.isValid()) setCursor(Qt::PointingHandCursor);
200 void ChannelView::leaveEvent(QEvent *event) {
201 QListView::leaveEvent(event);
202 // channelsModel->clearHover();
205 void ChannelView::itemEntered(const QModelIndex &) {
206 // channelsModel->setHoveredRow(index.row());
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);
233 void ChannelView::showContextMenu(const QPoint &point) {
234 const QModelIndex index = indexAt(point);
235 if (!index.isValid()) return;
237 YTChannel *channel = channelsModel->channelForIndex(index);
238 if (!channel) return;
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()));
253 QAction *notificationsAction = menu.addAction(tr("Receive Notifications"), user, SLOT(unsubscribe()));
254 notificationsAction->setCheckable(true);
255 notificationsAction->setChecked(true);
258 QAction *unsubscribeAction = menu.addAction(tr("Unsubscribe"), channel, SLOT(unsubscribe()));
259 connect(unsubscribeAction, SIGNAL(triggered()),
260 ChannelAggregator::instance(), SLOT(updateUnwatchedCount()));
262 menu.exec(mapToGlobal(point));
265 void ChannelView::paintEvent(QPaintEvent *event) {
266 if (model()->rowCount() < 3) {
268 if (!errorMessage.isEmpty())
270 else if (showUpdated)
271 msg = tr("There are no updated subscriptions at this time.");
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());
280 void ChannelView::toggleShowUpdated(bool enable) {
281 showUpdated = enable;
284 settings.setValue(showUpdatedKey, showUpdated);
287 void ChannelView::updateQuery(bool transition) {
288 Q_UNUSED(transition);
289 errorMessage.clear();
290 if (!Database::exists()) return;
292 QString sql = "select user_id from subscriptions";
294 sql += " where notify_count>0";
298 sql += " order by updated desc";
301 sql += " order by added desc";
303 case SortByLastWatched:
304 sql += " order by watched desc";
306 case SortByMostWatched:
307 sql += " order by views desc";
310 sql += " order by name collate nocase";
316 Extra::fadeInWidget(this, this);
319 channelsModel->setQuery(sql, Database::instance().getConnection());
320 if (channelsModel->lastError().isValid()) {
321 qWarning() << channelsModel->lastError().text();
322 errorMessage = channelsModel->lastError().text();
326 void ChannelView::setSortBy(SortBy sortBy) {
327 this->sortBy = sortBy;
330 settings.setValue(sortByKey, (int)sortBy);
333 void ChannelView::markAllAsWatched() {
334 ChannelAggregator::instance()->markAllAsWatched();
336 markAsWatchedAction->setEnabled(false);
339 void ChannelView::unwatchedCountChanged(int count) {
340 markAsWatchedAction->setEnabled(count > 0);
341 channelsModel->updateUnwatched();