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 reallyStopped = false;
17 QBoxLayout *layout = new QHBoxLayout();
20 splitter = new MiniSplitter(this);
21 splitter->setChildrenCollapsible(false);
23 sortBar = new THBlackBar(this);
24 mostRelevantAction = new QAction(tr("Most relevant"), this);
25 QKeySequence keySequence(Qt::CTRL + Qt::Key_1);
26 mostRelevantAction->setShortcut(keySequence);
27 mostRelevantAction->setStatusTip(keySequence.toString(QKeySequence::NativeText));
28 addAction(mostRelevantAction);
29 connect(mostRelevantAction, SIGNAL(triggered()), this, SLOT(searchMostRelevant()), Qt::QueuedConnection);
30 sortBar->addAction(mostRelevantAction);
31 mostRecentAction = new QAction(tr("Most recent"), this);
32 keySequence = QKeySequence(Qt::CTRL + Qt::Key_2);
33 mostRecentAction->setShortcut(keySequence);
34 mostRecentAction->setStatusTip(keySequence.toString(QKeySequence::NativeText));
35 addAction(mostRecentAction);
36 connect(mostRecentAction, SIGNAL(triggered()), this, SLOT(searchMostRecent()), Qt::QueuedConnection);
37 sortBar->addAction(mostRecentAction);
38 mostViewedAction = new QAction(tr("Most viewed"), this);
39 keySequence = QKeySequence(Qt::CTRL + Qt::Key_3);
40 mostViewedAction->setShortcut(keySequence);
41 mostViewedAction->setStatusTip(keySequence.toString(QKeySequence::NativeText));
42 addAction(mostViewedAction);
43 connect(mostViewedAction, SIGNAL(triggered()), this, SLOT(searchMostViewed()), Qt::QueuedConnection);
44 sortBar->addAction(mostViewedAction);
46 listView = new QListView(this);
47 listView->setItemDelegate(new PrettyItemDelegate(this));
48 listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
51 listView->setDragEnabled(true);
52 listView->setAcceptDrops(true);
53 listView->setDropIndicatorShown(true);
54 listView->setDragDropMode(QAbstractItemView::DragDrop);
57 listView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
58 listView->setFrameShape( QFrame::NoFrame );
59 listView->setAttribute(Qt::WA_MacShowFocusRect, false);
60 listView->setMinimumSize(320,240);
62 // respond to the user doubleclicking a playlist item
63 connect(listView, SIGNAL(activated(const QModelIndex &)), this, SLOT(itemActivated(const QModelIndex &)));
65 listModel = new ListModel(this);
66 connect(listModel, SIGNAL(activeRowChanged(int)), this, SLOT(activeRowChanged(int)));
67 // needed to restore the selection after dragndrop
68 connect(listModel, SIGNAL(needSelectionFor(QList<Video*>)), this, SLOT(selectVideos(QList<Video*>)));
69 listView->setModel(listModel);
71 connect(listView->selectionModel(),
72 SIGNAL(selectionChanged ( const QItemSelection & , const QItemSelection & )),
73 this, SLOT(selectionChanged ( const QItemSelection & , const QItemSelection & )));
75 playlistWidget = new PlaylistWidget(this, sortBar, listView);
77 splitter->addWidget(playlistWidget);
79 videoAreaWidget = new VideoAreaWidget(this);
80 videoAreaWidget->setMinimumSize(320,240);
84 // mouse autohide does not work on the Mac (no mouseMoveEvent)
85 videoWidget = new Phonon::VideoWidget(this);
87 videoWidget = new VideoWidget(this);
90 videoAreaWidget->setVideoWidget(videoWidget);
91 videoAreaWidget->setListModel(listModel);
93 loadingWidget = new LoadingWidget(this);
94 videoAreaWidget->setLoadingWidget(loadingWidget);
96 splitter->addWidget(videoAreaWidget);
98 layout->addWidget(splitter);
101 // restore splitter state
103 splitter->restoreState(settings.value("splitter").toByteArray());
105 errorTimer = new QTimer(this);
106 errorTimer->setSingleShot(true);
107 errorTimer->setInterval(3000);
108 connect(errorTimer, SIGNAL(timeout()), SLOT(skipVideo()));
110 workaroundTimer = new QTimer(this);
111 workaroundTimer->setSingleShot(true);
112 workaroundTimer->setInterval(1000);
113 connect(workaroundTimer, SIGNAL(timeout()), SLOT(timerPlay()));
117 MediaView::~MediaView() {
121 void MediaView::initialize() {
122 connect(videoAreaWidget, SIGNAL(doubleClicked()), The::globalActions()->value("fullscreen"), SLOT(trigger()));
123 videoAreaWidget->setContextMenuPolicy(Qt::CustomContextMenu);
124 connect(videoAreaWidget, SIGNAL(customContextMenuRequested(QPoint)),
125 this, SLOT(showVideoContextMenu(QPoint)));
128 void MediaView::setMediaObject(Phonon::MediaObject *mediaObject) {
129 this->mediaObject = mediaObject;
130 Phonon::createPath(this->mediaObject, videoWidget);
131 // connect(mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish()));
132 connect(mediaObject, SIGNAL(finished()), this, SLOT(skip()));
133 connect(mediaObject, SIGNAL(stateChanged(Phonon::State, Phonon::State)),
134 this, SLOT(stateChanged(Phonon::State, Phonon::State)));
135 connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
136 this, SLOT(currentSourceChanged(Phonon::MediaSource)));
137 connect(mediaObject, SIGNAL(bufferStatus(int)), loadingWidget, SLOT(bufferStatus(int)));
140 void MediaView::search(SearchParams *searchParams) {
141 reallyStopped = false;
143 this->searchParams = searchParams;
145 // start serching for videos
146 listModel->search(searchParams);
148 // this implies that the enum and the bar action order is the same
149 sortBar->setCheckedAction(searchParams->sortBy()-1);
151 listView->setFocus();
153 loadingWidget->clear();
156 void MediaView::disappear() {
157 timerPlayFlag = true;
160 void MediaView::handleError(QString message) {
161 videoAreaWidget->showError(message);
162 skippedVideo = listModel->activeVideo();
163 // recover from errors by skipping to the next video
164 errorTimer->start(2000);
167 void MediaView::stateChanged(Phonon::State newState, Phonon::State /*oldState*/)
170 // qDebug() << "Phonon state: " << newState << oldState;
174 case Phonon::ErrorState:
175 qDebug() << "Phonon error:" << mediaObject->errorString() << mediaObject->errorType();
176 handleError(mediaObject->errorString());
179 case Phonon::PlayingState:
181 videoAreaWidget->showVideo();
184 case Phonon::StoppedState:
186 // play() has already been called when setting the source
187 // but Phonon on Linux needs a little more help to start playback
188 if (!reallyStopped) mediaObject->play();
191 // Workaround for Mac playback start problem
192 if (!timerPlayFlag) {
193 workaroundTimer->start();
199 case Phonon::PausedState:
203 case Phonon::BufferingState:
204 //qDebug("buffering");
207 case Phonon::LoadingState:
216 void MediaView::pause() {
217 // qDebug() << "pause() called" << mediaObject->state();
218 switch( mediaObject->state() ) {
219 case Phonon::PlayingState:
220 mediaObject->pause();
228 void MediaView::stop() {
229 listModel->abortSearch();
230 reallyStopped = true;
232 workaroundTimer->stop();
236 void MediaView::activeRowChanged(int row) {
237 if (reallyStopped) return;
239 Video *video = listModel->videoAt(row);
242 // immediately show the loading widget
243 videoAreaWidget->showLoading(video);
245 // mediaObject->pause();
247 connect(video, SIGNAL(gotStreamUrl(QUrl)), SLOT(gotStreamUrl(QUrl)));
248 // TODO handle signal in a proper slot and impl item error status
249 connect(video, SIGNAL(errorStreamUrl(QString)), SLOT(handleError(QString)));
251 video->loadStreamUrl();
253 // reset the timer flag
254 timerPlayFlag = false;
256 // video title in the statusbar
257 QMainWindow* mainWindow = dynamic_cast<QMainWindow*>(qApp->topLevelWidgets().first());
258 if (mainWindow) mainWindow->statusBar()->showMessage(video->title());
260 // see you in gotStreamUrl...
264 void MediaView::gotStreamUrl(QUrl streamUrl) {
265 if (reallyStopped) return;
268 mediaObject->setCurrentSource(streamUrl);
271 // ensure we always have 10 videos ahead
272 listModel->searchNeeded();
274 // ensure active item is visible
275 int row = listModel->activeRow();
277 QModelIndex index = listModel->index(row, 0, QModelIndex());
278 listView->scrollTo(index, QAbstractItemView::EnsureVisible);
283 // get the Video that sent the signal
284 Video *video = static_cast<Video *>(sender());
286 qDebug() << "Cannot get sender";
289 bool ret = QMetaObject::invokeMethod(qApp->topLevelWidgets().first(), "hdIndicator", Qt::DirectConnection, Q_ARG(bool, video->isHd()));
290 if (!ret) qDebug() << "hdIndicator invokeMethod failed";
293 void MediaView::itemActivated(const QModelIndex &index) {
294 if (listModel->rowExists(index.row()))
295 listModel->setActiveRow(index.row());
296 // the user doubleclicked on the "Search More" item
297 else listModel->searchMore();
300 void MediaView::aboutToFinish() {
302 int nextRow = listModel->nextRow();
303 if (nextRow == -1) return;
304 Video* video = listModel->videoAt(nextRow);
305 QUrl streamUrl = video->streamUrl();
306 qDebug() << "Enqueing" << streamUrl;
307 mediaObject->enqueue(streamUrl);
311 void MediaView::currentSourceChanged(const Phonon::MediaSource source) {
312 qDebug() << source.url().toString();
316 void MediaView::skipVideo() {
317 // skippedVideo is useful for DELAYED skip operations
318 // in order to be sure that we're skipping the video we wanted
319 // and not another one
321 if (listModel->activeVideo() != skippedVideo) {
322 qDebug() << "Skip of video canceled";
325 int nextRow = listModel->rowForVideo(skippedVideo);
327 if (nextRow == -1) return;
328 listModel->setActiveRow(nextRow);
332 void MediaView::skip() {
333 int nextRow = listModel->nextRow();
334 if (nextRow == -1) return;
335 listModel->setActiveRow(nextRow);
338 void MediaView::openWebPage() {
339 Video* video = listModel->activeVideo();
341 mediaObject->pause();
342 QDesktopServices::openUrl(video->webpage());
345 void MediaView::removeSelected() {
346 if (!listView->selectionModel()->hasSelection()) return;
347 QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
348 listModel->removeIndexes(indexes);
351 void MediaView::selectVideos(QList<Video*> videos) {
352 foreach (Video *video, videos) {
353 QModelIndex index = listModel->indexForVideo(video);
354 listView->selectionModel()->select(index, QItemSelectionModel::Select);
355 listView->scrollTo(index, QAbstractItemView::EnsureVisible);
359 void MediaView::selectionChanged(const QItemSelection & /*selected*/, const QItemSelection & /*deselected*/) {
360 const bool gotSelection = listView->selectionModel()->hasSelection();
361 The::globalActions()->value("remove")->setEnabled(gotSelection);
362 The::globalActions()->value("moveUp")->setEnabled(gotSelection);
363 The::globalActions()->value("moveDown")->setEnabled(gotSelection);
366 void MediaView::moveUpSelected() {
367 if (!listView->selectionModel()->hasSelection()) return;
368 QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
369 listModel->move(indexes, true);
372 void MediaView::moveDownSelected() {
373 if (!listView->selectionModel()->hasSelection()) return;
374 QModelIndexList indexes = listView->selectionModel()->selectedIndexes();
375 listModel->move(indexes, false);
378 void MediaView::showVideoContextMenu(QPoint point) {
379 The::globalMenus()->value("video")->popup(videoWidget->mapToGlobal(point));
382 void MediaView::searchMostRelevant() {
383 searchParams->setSortBy(SearchParams::SortByRelevance);
384 search(searchParams);
387 void MediaView::searchMostRecent() {
388 searchParams->setSortBy(SearchParams::SortByNewest);
389 search(searchParams);
392 void MediaView::searchMostViewed() {
393 searchParams->setSortBy(SearchParams::SortByViewCount);
394 search(searchParams);
397 void MediaView::setPlaylistVisible(bool visible) {
398 playlistWidget->setVisible(visible);
401 void MediaView::timerPlay() {
402 // qDebug() << mediaObject->currentTime();
403 // Workaround Phonon bug on Mac OSX
404 if (mediaObject->currentTime() <= 0 && mediaObject->state() == Phonon::PlayingState) {
405 mediaObject->pause();
410 void MediaView::saveSplitterState() {
412 settings.setValue("splitter", splitter->saveState());