2 #include "playlist/PrettyItemDelegate.h"
3 #include "networkaccess.h"
4 #include "videowidget.h"
5 #include "minisplitter.h"
8 QMap<QString, QAction*>* globalActions();
9 QMap<QString, QMenu*>* globalMenus();
10 QNetworkAccessManager* networkAccessManager();
13 MediaView::MediaView(QWidget *parent) : QWidget(parent) {
15 QBoxLayout *layout = new QHBoxLayout();
18 splitter = new MiniSplitter(this);
19 splitter->setChildrenCollapsible(false);
21 sortBar = new THBlackBar(this);
22 mostRelevantAction = new QAction(tr("Most relevant"), this);
23 mostRelevantAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_1));
24 addAction(mostRelevantAction);
25 connect(mostRelevantAction, SIGNAL(triggered()), this, SLOT(searchMostRelevant()), Qt::QueuedConnection);
26 sortBar->addAction(mostRelevantAction);
27 mostRecentAction = new QAction(tr("Most recent"), this);
28 mostRecentAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_2));
29 addAction(mostRecentAction);
30 connect(mostRecentAction, SIGNAL(triggered()), this, SLOT(searchMostRecent()), Qt::QueuedConnection);
31 sortBar->addAction(mostRecentAction);
32 mostViewedAction = new QAction(tr("Most viewed"), this);
33 mostViewedAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_3));
34 addAction(mostViewedAction);
35 connect(mostViewedAction, SIGNAL(triggered()), this, SLOT(searchMostViewed()), Qt::QueuedConnection);
36 sortBar->addAction(mostViewedAction);
38 listView = new QListView(this);
39 listView->setItemDelegate(new PrettyItemDelegate(this));
40 listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
43 listView->setDragEnabled(true);
44 listView->setAcceptDrops(true);
45 listView->setDropIndicatorShown(true);
46 listView->setDragDropMode(QAbstractItemView::DragDrop);
49 listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
50 listView->setFrameShape( QFrame::NoFrame );
51 listView->setAttribute(Qt::WA_MacShowFocusRect, false);
52 listView->setMinimumSize(320,240);
54 // respond to the user doubleclicking a playlist item
55 connect(listView, SIGNAL(activated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
57 listModel = new ListModel(this);
58 connect(listModel, SIGNAL(activeRowChanged(int)), this, SLOT(activeRowChanged(int)));
59 // needed to restore the selection after dragndrop
60 connect(listModel, SIGNAL(needSelectionFor(QList<Video*>)), this, SLOT(selectVideos(QList<Video*>)));
61 listView->setModel(listModel);
63 connect(listView->selectionModel(),
64 SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & )),
65 this, SLOT(selectionChanged ( const QItemSelection & , const QItemSelection & )));
67 playlistWidget = new PlaylistWidget(this, sortBar, listView);
69 splitter->addWidget(playlistWidget);
71 videoAreaWidget = new VideoAreaWidget(this);
72 videoAreaWidget->setMinimumSize(320,240);
74 videoWidget = new Phonon::VideoWidget(this);
75 videoAreaWidget->setVideoWidget(videoWidget);
76 videoAreaWidget->setListModel(listModel);
78 loadingWidget = new LoadingWidget(this);
79 videoAreaWidget->setLoadingWidget(loadingWidget);
81 splitter->addWidget(videoAreaWidget);
83 layout->addWidget(splitter);
86 errorTimer = new QTimer(this);
87 errorTimer->setSingleShot(true);
88 errorTimer->setInterval(3000);
89 connect(errorTimer, SIGNAL(timeout()), SLOT(skipVideo()));
92 MediaView::~MediaView() {
96 void MediaView::initialize() {
97 connect(videoAreaWidget, SIGNAL(doubleClicked()), The::globalActions()->value("fullscreen"), SLOT(trigger()));
98 videoAreaWidget->setContextMenuPolicy(Qt::CustomContextMenu);
99 connect(videoAreaWidget, SIGNAL(customContextMenuRequested(QPoint)),
100 this, SLOT(showVideoContextMenu(QPoint)));
103 void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
104 this->mediaObject = mediaObject;
105 Phonon::createPath(this->mediaObject, videoWidget);
106 // connect(mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish()));
107 connect(mediaObject, SIGNAL(finished()), this, SLOT(skip()));
108 connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
109 this, SLOT(stateChanged(Phonon::State, Phonon::State)));
110 connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
111 this, SLOT(currentSourceChanged(Phonon::MediaSource)));
112 connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int)));
115 void MediaView::search(SearchParams *searchParams) {
116 this->searchParams = searchParams;
118 // start serching for videos
119 listModel->search(searchParams);
121 // this implies that the enum and the bar action order is the same
122 sortBar->setCheckedAction(searchParams->sortBy()-1);
124 listView->setFocus();
126 loadingWidget->clear();
129 void MediaView::disappear() {
130 timerPlayFlag = true;
133 void MediaView::handleError(QString message) {
134 videoAreaWidget->showError(message);
135 skippedVideo = listModel->activeVideo();
136 // recover from errors by skipping to the next video
137 errorTimer->start(2000);
140 void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/)
143 // qDebug() << "Phonon state: " << newState << oldState;
147 case Phonon::ErrorState:
148 qDebug() << "Phonon error:" << mediaObject->errorString() << mediaObject->errorType();
149 handleError(mediaObject->errorString());
152 case Phonon::PlayingState:
154 videoAreaWidget->showVideo();
157 case Phonon::StoppedState:
159 // play() has already been called when setting the source
160 // but Phonon on Linux needs a little more help to start playback
163 // Workaround for Mac playback start problem
164 if (!timerPlayFlag) {
165 QTimer::singleShot(1000, this, SLOT(timerPlay()));
170 case Phonon::PausedState:
174 case Phonon::BufferingState:
178 case Phonon::LoadingState:
187 void MediaView::pause() {
188 // qDebug() << "pause() called" << mediaObject->state();
189 switch( mediaObject->state() ) {
190 case Phonon::PlayingState:
191 mediaObject->pause();
199 void MediaView::stop() {
200 listModel->abortSearch();
202 mediaObject->clear();
205 void MediaView::activeRowChanged(int row) {
206 Video *video = listModel->videoAt(row);
209 // immediately show the loading widget
210 videoAreaWidget->showLoading(video);
212 // mediaObject->pause();
214 connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
215 // TODO handle signal in a proper slot and impl item error status
216 connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)));
217 video->loadStreamUrl();
219 // reset the timer flag
220 timerPlayFlag = false;
222 // video title in the statusbar
223 QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(qApp->topLevelWidgets().first());
224 if (mainWindow) mainWindow->statusBar()->showMessage(video->title());
226 // see you in gotStreamUrl...
230 void MediaView::gotStreamUrl(QUrl streamUrl) {
233 mediaObject->setCurrentSource(streamUrl);
236 // ensure we always have 10 videos ahead
237 listModel->searchNeeded();
239 // ensure active item is visible
240 int row = listModel->activeRow();
242 QModelIndex index = listModel->index(row, 0, QModelIndex());
243 listView->scrollTo(index, QAbstractItemView::EnsureVisible);
248 void MediaView::itemActivated(const QModelIndex &index) {
249 if (listModel->rowExists(index.row()))
250 listModel->setActiveRow(index.row());
251 // the user doucleclicked on the "Search More" item
252 else listModel->searchMore();
255 void MediaView::aboutToFinish() {
257 int nextRow = listModel->nextRow();
258 if (nextRow == -1) return;
259 Video* video = listModel->videoAt(nextRow);
260 QUrl streamUrl = video->streamUrl();
261 qDebug() << "Enqueing" << streamUrl;
262 mediaObject->enqueue(streamUrl);
266 void MediaView::currentSourceChanged(const Phonon::MediaSource source) {
267 qDebug() << "Source changed:" << source.url();
271 void MediaView::skipVideo() {
272 // skippedVideo is useful for DELAYED skip operations
273 // in order to be sure that we're skipping the video we wanted
274 // and not another one
276 if (listModel->activeVideo() != skippedVideo) {
277 qDebug() << "Skip of video canceled";
280 int nextRow = listModel->rowForVideo(skippedVideo);
282 if (nextRow == -1) return;
283 listModel->setActiveRow(nextRow);
287 void MediaView::skip() {
288 int nextRow = listModel->nextRow();
289 if (nextRow == -1) return;
290 listModel->setActiveRow(nextRow);
293 void MediaView::openWebPage() {
294 Video* video = listModel->activeVideo();
296 mediaObject->pause();
297 QDesktopServices::openUrl(video->webpage());
300 void MediaView::removeSelected() {
301 if (!listView->selectionModel()->hasSelection()) return;
302 QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
303 listModel->removeIndexes(indexes);
306 void MediaView::selectVideos(QList<Video*> videos) {
307 foreach (Video *video, videos) {
308 QModelIndex index = listModel->indexForVideo(video);
309 listView->selectionModel()->select(index, QItemSelectionModel::Select);
310 listView->scrollTo(index, QAbstractItemView::EnsureVisible);
314 void MediaView::selectionChanged(const QItemSelection & /*selected*/, const QItemSelection & /*deselected*/) {
315 const bool gotSelection = listView->selectionModel()->hasSelection();
316 The::globalActions()->value("remove")->setEnabled(gotSelection);
317 The::globalActions()->value("moveUp")->setEnabled(gotSelection);
318 The::globalActions()->value("moveDown")->setEnabled(gotSelection);
321 void MediaView::moveUpSelected() {
322 if (!listView->selectionModel()->hasSelection()) return;
323 QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
324 listModel->move(indexes, true);
327 void MediaView::moveDownSelected() {
328 if (!listView->selectionModel()->hasSelection()) return;
329 QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
330 listModel->move(indexes, false);
333 void MediaView::showVideoContextMenu(QPoint point) {
334 The::globalMenus()->value("video")->popup(videoWidget->mapToGlobal(point));
337 void MediaView::searchMostRelevant() {
338 searchParams->setSortBy(SearchParams::SortByRelevance);
339 search(searchParams);
342 void MediaView::searchMostRecent() {
343 searchParams->setSortBy(SearchParams::SortByNewest);
344 search(searchParams);
347 void MediaView::searchMostViewed() {
348 searchParams->setSortBy(SearchParams::SortByViewCount);
349 search(searchParams);
352 void MediaView::setPlaylistVisible(bool visible) {
353 playlistWidget->setVisible(visible);
356 void MediaView::timerPlay() {
357 // qDebug() << mediaObject->currentTime();
358 // Workaround Phonon bug on Mac OSX
359 if (mediaObject->currentTime() <= 0 && mediaObject->state() == Phonon::PlayingState) {
360 mediaObject->pause();