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 "aggregatevideosource.h"
23 #include "channelaggregator.h"
24 #include "channelitemdelegate.h"
25 #include "channelmodel.h"
27 #include "iconutils.h"
28 #include "mainwindow.h"
29 #include "searchparams.h"
30 #include "ytchannel.h"
35 #include "channellistview.h"
37 #include "ivchannelsource.h"
39 #include "ytjschannelsource.h"
42 const QString sortByKey = "subscriptionsSortBy";
43 const QString showUpdatedKey = "subscriptionsShowUpdated";
46 ChannelView::ChannelView(QWidget *parent) : View(parent), showUpdated(false), sortBy(SortByName) {
47 QBoxLayout *layout = new QVBoxLayout(this);
49 layout->setSpacing(0);
51 listView = new ChannelListView();
52 listView->setItemDelegate(new ChannelItemDelegate(this));
54 channelsModel = new ChannelModel(this);
55 listView->setModel(channelsModel);
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()));
62 layout->addWidget(listView);
66 connect(ChannelAggregator::instance(), SIGNAL(channelChanged(YTChannel *)), channelsModel,
67 SLOT(updateChannel(YTChannel *)));
68 connect(ChannelAggregator::instance(), SIGNAL(unwatchedCountChanged(int)),
69 SLOT(unwatchedCountChanged(int)));
71 unwatchedCountChanged(ChannelAggregator::instance()->getUnwatchedCount());
74 void ChannelView::setupActions() {
77 statusActions << MainWindow::instance()->getAction("importSubscriptions");
79 sortBy = static_cast<SortBy>(settings.value(sortByKey, SortByName).toInt());
81 QMenu *sortMenu = new QMenu(this);
82 QActionGroup *sortGroup = new QActionGroup(this);
84 QAction *sortByNameAction = new QAction(tr("Name"), this);
85 sortByNameAction->setActionGroup(sortGroup);
86 sortByNameAction->setCheckable(true);
87 if (sortBy == SortByName) sortByNameAction->setChecked(true);
88 connect(sortByNameAction, SIGNAL(triggered()), SLOT(setSortByName()));
89 sortMenu->addAction(sortByNameAction);
91 QAction *sortByUpdatedAction = new QAction(tr("Last Updated"), this);
92 sortByUpdatedAction->setActionGroup(sortGroup);
93 sortByUpdatedAction->setCheckable(true);
94 if (sortBy == SortByUpdated) sortByUpdatedAction->setChecked(true);
95 connect(sortByUpdatedAction, SIGNAL(triggered()), SLOT(setSortByUpdated()));
96 sortMenu->addAction(sortByUpdatedAction);
98 QAction *sortByAddedAction = new QAction(tr("Last Added"), this);
99 sortByAddedAction->setActionGroup(sortGroup);
100 sortByAddedAction->setCheckable(true);
101 if (sortBy == SortByAdded) sortByAddedAction->setChecked(true);
102 connect(sortByAddedAction, SIGNAL(triggered()), SLOT(setSortByAdded()));
103 sortMenu->addAction(sortByAddedAction);
105 QAction *sortByLastWatched = new QAction(tr("Last Watched"), this);
106 sortByLastWatched->setActionGroup(sortGroup);
107 sortByLastWatched->setCheckable(true);
108 if (sortBy == SortByLastWatched) sortByLastWatched->setChecked(true);
109 connect(sortByLastWatched, SIGNAL(triggered()), SLOT(setSortByLastWatched()));
110 sortMenu->addAction(sortByLastWatched);
112 QAction *sortByMostWatched = new QAction(tr("Most Watched"), this);
113 sortByMostWatched->setActionGroup(sortGroup);
114 sortByMostWatched->setCheckable(true);
115 if (sortBy == SortByMostWatched) sortByMostWatched->setChecked(true);
116 connect(sortByMostWatched, SIGNAL(triggered()), SLOT(setSortByMostWatched()));
117 sortMenu->addAction(sortByMostWatched);
119 QToolButton *sortButton = new QToolButton(this);
120 sortButton->setText(tr("Sort by"));
121 IconUtils::setIcon(sortButton, "sort");
122 sortButton->setIconSize(QSize(16, 16));
123 sortButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
124 sortButton->setPopupMode(QToolButton::InstantPopup);
125 sortButton->setMenu(sortMenu);
126 QWidgetAction *widgetAction = new QWidgetAction(this);
127 widgetAction->setDefaultWidget(sortButton);
128 widgetAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
129 statusActions << widgetAction;
131 markAsWatchedAction = new QAction(tr("Mark all as watched"), this);
132 IconUtils::setIcon(markAsWatchedAction, "mark-watched");
133 markAsWatchedAction->setEnabled(false);
134 markAsWatchedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_W));
135 connect(markAsWatchedAction, SIGNAL(triggered()), SLOT(markAllAsWatched()));
136 statusActions << markAsWatchedAction;
138 showUpdated = settings.value(showUpdatedKey, false).toBool();
139 QAction *showUpdatedAction = new QAction(tr("Show Updated"), this);
140 IconUtils::setIcon(showUpdatedAction, "show-updated");
141 showUpdatedAction->setCheckable(true);
142 showUpdatedAction->setChecked(showUpdated);
143 showUpdatedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U));
144 connect(showUpdatedAction, SIGNAL(toggled(bool)), SLOT(toggleShowUpdated(bool)));
145 statusActions << showUpdatedAction;
147 for (QAction *action : statusActions) {
148 window()->addAction(action);
149 MainWindow::instance()->setupAction(action);
153 QString ChannelView::noSubscriptionsMessage() {
154 return tr("You have no subscriptions. "
155 "Use the star symbol to subscribe to channels.");
158 void ChannelView::appear() {
160 MainWindow::instance()->showActionsInStatusBar(statusActions, true);
162 ChannelAggregator::instance()->start();
165 void ChannelView::disappear() {
166 MainWindow::instance()->showActionsInStatusBar(statusActions, false);
169 void ChannelView::itemActivated(const QModelIndex &index) {
170 ChannelModel::ItemTypes itemType = channelsModel->typeForIndex(index);
171 if (itemType == ChannelModel::ItemChannel) {
172 YTChannel *channel = channelsModel->channelForIndex(index);
173 SearchParams *params = new SearchParams();
174 params->setChannelId(channel->getChannelId());
175 params->setSortBy(SearchParams::SortByNewest);
176 params->setTransient(true);
177 VideoSource *vs = nullptr;
178 if (VideoAPI::impl() == VideoAPI::YT3) {
179 YTSearch *videoSource = new YTSearch(params);
180 videoSource->setAsyncDetails(true);
182 } else if (VideoAPI::impl() == VideoAPI::IV) {
183 vs = new IVChannelSource(params);
184 } else if (VideoAPI::impl() == VideoAPI::JS) {
185 vs = new YTJSChannelSource(params);
188 channel->updateWatched();
189 } else if (itemType == ChannelModel::ItemAggregate) {
190 AggregateVideoSource *videoSource = new AggregateVideoSource();
191 videoSource->setName(tr("All Videos"));
192 emit activated(videoSource);
193 } else if (itemType == ChannelModel::ItemUnwatched) {
194 AggregateVideoSource *videoSource = new AggregateVideoSource();
195 videoSource->setName(tr("Unwatched Videos"));
196 videoSource->setUnwatched(true);
197 emit activated(videoSource);
201 void ChannelView::showContextMenu(const QPoint &point) {
202 const QModelIndex index = listView->indexAt(point);
203 if (!index.isValid()) return;
205 YTChannel *channel = channelsModel->channelForIndex(index);
206 if (!channel) return;
212 if (channel->getNotifyCount() > 0) {
213 QAction *markAsWatchedAction =
214 menu.addAction(tr("Mark as Watched"), channel, SLOT(updateWatched()));
215 connect(markAsWatchedAction, SIGNAL(triggered()), ChannelAggregator::instance(),
216 SLOT(updateUnwatchedCount()));
222 QAction *notificationsAction = menu.addAction(tr("Receive Notifications"), user,
223 SLOT(unsubscribe())); notificationsAction->setCheckable(true);
224 notificationsAction->setChecked(true);
227 QAction *unsubscribeAction = menu.addAction(tr("Unsubscribe"), channel, SLOT(unsubscribe()));
228 connect(unsubscribeAction, SIGNAL(triggered()), ChannelAggregator::instance(),
229 SLOT(updateUnwatchedCount()));
231 menu.exec(mapToGlobal(point));
234 void ChannelView::toggleShowUpdated(bool enable) {
235 showUpdated = enable;
238 settings.setValue(showUpdatedKey, showUpdated);
241 void ChannelView::updateQuery(bool transition) {
242 Q_UNUSED(transition);
243 listView->clearErrorMessage();
245 if (!Database::exists()) {
246 listView->setErrorMessage(noSubscriptionsMessage());
250 QString sql = "select user_id from subscriptions";
251 if (showUpdated) sql += " where notify_count>0";
255 sql += " order by updated desc";
258 sql += " order by added desc";
260 case SortByLastWatched:
261 sql += " order by watched desc";
263 case SortByMostWatched:
264 sql += " order by views desc";
267 sql += " order by name collate nocase";
272 if (transition) Extra::fadeInWidget(this, this);
275 channelsModel->setQuery(sql, Database::instance().getConnection());
276 if (channelsModel->lastError().isValid()) {
277 qWarning() << channelsModel->lastError().text();
278 listView->setErrorMessage(channelsModel->lastError().text());
279 } else if (channelsModel->rowCount() < 3) {
282 msg = tr("There are no updated subscriptions at this time.");
284 msg = noSubscriptionsMessage();
285 listView->setErrorMessage(msg);
289 void ChannelView::setSortBy(SortBy sortBy) {
290 this->sortBy = sortBy;
293 settings.setValue(sortByKey, (int)sortBy);
296 void ChannelView::markAllAsWatched() {
297 ChannelAggregator::instance()->markAllAsWatched();
299 markAsWatchedAction->setEnabled(false);
302 void ChannelView::unwatchedCountChanged(int count) {
303 markAsWatchedAction->setEnabled(count > 0);
304 channelsModel->updateUnwatched();