fixed clear playlist (also clear the media object)
[tomamp] / mainwindow.cpp
1 #include <QtGui>
2 #include <QtDebug>
3 #include <QInputDialog>
4
5 #include "mainwindow.h"
6
7 #define AVOID_INPUT_DIALOG 1
8
9 MainWindow::MainWindow()
10     : settings (tr ("TomAmp"), "TomAmp")
11 {
12     audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
13     mediaObject = new Phonon::MediaObject(this);
14     metaInformationResolver = new Phonon::MediaObject(this);
15
16     mediaObject->setTickInterval(500);
17     connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
18     connect(mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
19         this, SLOT(stateChanged(Phonon::State,Phonon::State)));
20     connect(metaInformationResolver, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
21         this, SLOT(metaStateChanged(Phonon::State,Phonon::State)));
22     connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
23         this, SLOT(sourceChanged(Phonon::MediaSource)));
24     connect(mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish()));
25
26     Phonon::createPath(mediaObject, audioOutput);
27
28     repeat = settings.value("repeat", false).toBool();
29     shuffle = settings.value("shuffle", false).toBool();
30     qsrand (time (NULL));
31     setupShuffleList();
32     setupActions();
33     setupMenus();
34     setupUi();
35     timeLcd->display("00:00");
36     addStringList(settings.value("lastPlaylist").toStringList());
37     audioOutput->setVolume(settings.value("volume", .5).toReal());
38 }
39
40 MainWindow::~MainWindow()
41 {
42     settings.setValue("shuffle", shuffle);
43     QStringList curList;
44     foreach (Phonon::MediaSource src, sources)
45     {
46         if (src.type () == Phonon::MediaSource::LocalFile)
47             curList.append(src.fileName());
48         else
49             curList.append(src.url().toString());
50     }
51     settings.setValue("lastPlaylist", curList);
52     settings.setValue("volume", audioOutput->volume());
53 }
54
55 void MainWindow::addFiles()
56 {
57     QString folder = settings.value("LastFolder").toString();
58     if (folder.isEmpty())
59         folder = QDesktopServices::storageLocation(QDesktopServices::MusicLocation);
60     QStringList files = QFileDialog::getOpenFileNames(this, tr("Select Files To Add"),
61                     folder);
62
63     if (files.isEmpty())
64         return;
65
66     QString dir = QFileInfo (files[0]).absoluteDir().absolutePath();
67     settings.setValue("LastFolder", dir);
68     int index = sources.size();
69     foreach (QString string, files)
70     {
71         Phonon::MediaSource source (string);
72         sources.append(source);
73     }
74     if (!sources.isEmpty())
75         metaInformationResolver->setCurrentSource(sources.at(index));
76     setupShuffleList();
77
78 }
79
80 void MainWindow::addFolders()
81 {
82     QString folder = settings.value("LastFolder").toString();
83     if (folder.isEmpty())
84         folder = QDesktopServices::storageLocation(QDesktopServices::MusicLocation);
85     QString dir = QFileDialog::getExistingDirectory(this,
86             tr("Select Directory To Add"),
87             folder);
88
89     QStringList filters;
90     filters << "*.mp3";
91
92     QStringList files = QDir (dir).entryList(filters);
93
94     if (files.isEmpty())
95         return;
96
97     settings.setValue("LastFolder", dir);
98     int index = sources.size();
99     foreach (QString string, files)
100     {
101         QString fname = dir + "/" + string;
102         qDebug () << fname;
103         Phonon::MediaSource source(fname);
104         sources.append(source);
105     }
106     if (!sources.isEmpty())
107         metaInformationResolver->setCurrentSource(sources.at(index));
108     setupShuffleList();
109
110 }
111
112 void MainWindow::addUrl()
113 {
114 #ifdef AVOID_INPUT_DIALOG
115     QString url = "http://war.str3am.com:7970";
116 #else
117     QString url = QInputDialog::getText(this, "Get URL", "Please type in the stream URL");
118 #endif
119     int index = sources.size();
120     if (!url.isEmpty())
121     {
122         Phonon::MediaSource source(url);
123         sources.append(source);
124     }
125     if (!sources.isEmpty())
126         metaInformationResolver->setCurrentSource(sources.at(index));
127     setupShuffleList();
128 }
129
130 void MainWindow::addStringList(const QStringList& list)
131 {
132     int index = sources.size();
133     foreach (QString string, list)
134     {
135         Phonon::MediaSource source(string);
136         sources.append(source);
137     }
138     if (!sources.isEmpty())
139         metaInformationResolver->setCurrentSource(sources.at(index));
140     setupShuffleList();
141 }
142
143 void MainWindow::about()
144 {
145     QMessageBox::information(this, tr("About Music Player"),
146         tr("The Music Player example shows how to use Phonon - the multimedia"
147         " framework that comes with Qt - to create a simple music player."));
148 }
149
150 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */)
151 {
152     qDebug () << "State: " << newState;
153     switch (newState)
154     {
155         case Phonon::ErrorState:
156             if (mediaObject->errorType() == Phonon::FatalError)
157             {
158                 QMessageBox::warning(this, tr("Fatal Error"),
159                 mediaObject->errorString());
160             }
161             else
162             {
163                 QMessageBox::warning(this, tr("Error"),
164                 mediaObject->errorString());
165             }
166             break;
167         case Phonon::PlayingState:
168             setWindowTitle(mediaObject->metaData().value("TITLE") + " - TomAmp");
169             playAction->setEnabled(false);
170             pauseAction->setEnabled(true);
171             stopAction->setEnabled(true);
172             break;
173         case Phonon::StoppedState:
174             stopAction->setEnabled(false);
175             playAction->setEnabled(true);
176             pauseAction->setEnabled(false);
177             timeLcd->display("00:00");
178             break;
179         case Phonon::PausedState:
180             pauseAction->setEnabled(false);
181             stopAction->setEnabled(true);
182             playAction->setEnabled(true);
183             qDebug () << "Queue size: " << mediaObject->queue().size ();
184             if (mediaObject->queue().size ())
185             {
186                 mediaObject->setCurrentSource(mediaObject->queue()[0]);
187                 musicTable->selectRow(sources.indexOf(mediaObject->currentSource()));
188                 mediaObject->play();
189             }
190             mediaObject->clearQueue();
191             break;
192         case Phonon::BufferingState:
193             qDebug () << "Buffering";
194             break;
195         default:
196         ;
197     }
198 }
199
200 void MainWindow::tick(qint64 time)
201 {
202     QTime displayTime(0, (time / 60000) % 60, (time / 1000) % 60);
203
204     timeLcd->display(displayTime.toString("mm:ss"));
205 }
206
207 void MainWindow::tableClicked(int row, int /* column */)
208 {
209 //    bool wasPlaying = mediaObject->state() == Phonon::PlayingState;
210
211     mediaObject->stop();
212     mediaObject->clearQueue();
213
214     if (row >= sources.size())
215         return;
216
217     mediaObject->setCurrentSource(sources[row]);
218
219     mediaObject->play();
220     int ind = shuffleList.indexOf(row);
221     shuffleList.removeAt(ind);
222     shuffleList.insert(0, row);
223     qDebug () << "Modified shuffle list: " << shuffleList;
224 }
225
226 void MainWindow::sourceChanged(const Phonon::MediaSource &source)
227 {
228     musicTable->selectRow(sources.indexOf(source));
229     timeLcd->display("00:00");
230 }
231
232 void MainWindow::metaStateChanged(Phonon::State newState, Phonon::State /* oldState */)
233 {
234     if (newState == Phonon::ErrorState)
235     {
236         QMessageBox::warning(this, tr("Error opening files"),
237         metaInformationResolver->errorString());
238         while (!sources.isEmpty() &&
239             !(sources.takeLast() == metaInformationResolver->currentSource())) {}  /* loop */;
240         return;
241     }
242
243     if (newState != Phonon::StoppedState && newState != Phonon::PausedState)
244     {
245         return;
246     }
247
248     if (metaInformationResolver->currentSource().type() == Phonon::MediaSource::Invalid)
249         return;
250
251     QMap<QString, QString> metaData = metaInformationResolver->metaData();
252
253     QString title = metaData.value("TITLE");
254     if (title == "")
255         title = metaInformationResolver->currentSource().fileName();
256
257     if (title == "")
258         title = metaInformationResolver->currentSource().url().toString();
259
260     QTableWidgetItem *titleItem = new QTableWidgetItem(title);
261     titleItem->setFlags(titleItem->flags() ^ Qt::ItemIsEditable);
262     QTableWidgetItem *artistItem = new QTableWidgetItem(metaData.value("ARTIST"));
263     artistItem->setFlags(artistItem->flags() ^ Qt::ItemIsEditable);
264     QTableWidgetItem *albumItem = new QTableWidgetItem(metaData.value("ALBUM"));
265     albumItem->setFlags(albumItem->flags() ^ Qt::ItemIsEditable);
266     QTableWidgetItem *yearItem = new QTableWidgetItem(metaData.value("DATE"));
267     yearItem->setFlags(yearItem->flags() ^ Qt::ItemIsEditable);
268
269     int currentRow = musicTable->rowCount();
270     musicTable->insertRow(currentRow);
271     musicTable->setItem(currentRow, 0, artistItem);
272     musicTable->setItem(currentRow, 1, titleItem);
273     musicTable->setItem(currentRow, 2, albumItem);
274     musicTable->setItem(currentRow, 3, yearItem);
275
276
277     if (musicTable->selectedItems().isEmpty())
278     {
279         musicTable->selectRow(0);
280         mediaObject->setCurrentSource(metaInformationResolver->currentSource());
281     }
282
283     Phonon::MediaSource source = metaInformationResolver->currentSource();
284     int index = sources.indexOf(metaInformationResolver->currentSource()) + 1;
285     if (sources.size() > index)
286     {
287         metaInformationResolver->setCurrentSource(sources.at(index));
288     }
289     else
290     {
291         musicTable->resizeColumnsToContents();
292         if (musicTable->columnWidth(0) > 300)
293             musicTable->setColumnWidth(0, 300);
294     }
295 }
296
297 void MainWindow::aboutToFinish()
298 {
299     qDebug () << "Abouttotfinish";
300     int index = sources.indexOf(mediaObject->currentSource()) + 1;
301     if (shuffle)
302     {
303         index = shuffleList.indexOf(sources.indexOf(mediaObject->currentSource())) + 1;
304         if (index < shuffleList.size ())
305         {
306             mediaObject->enqueue(sources.at (shuffleList[index]));
307         }
308         else if (repeat)
309         {
310             mediaObject->enqueue(sources.at (shuffleList[0]));
311         }
312
313     }
314     else
315     {
316         if (sources.size() > index)
317         {
318             mediaObject->enqueue(sources.at(index));
319             qDebug () << "Enqueue " << index << " pfm " << mediaObject->prefinishMark();
320         }
321         else if (repeat)
322         {
323             mediaObject->enqueue(sources.at(0));
324             qDebug () << "Enqueue " << 0 << " pfm " << mediaObject->prefinishMark();
325         }
326     }
327 }
328
329 void MainWindow::finished()
330 {
331     qDebug () << "Finished";
332 }
333
334 void MainWindow::setupActions()
335 {
336     playAction = new QAction(style()->standardIcon(QStyle::SP_MediaPlay), tr("Play"), this);
337     playAction->setShortcut(tr("Crl+P"));
338     playAction->setDisabled(true);
339     pauseAction = new QAction(style()->standardIcon(QStyle::SP_MediaPause), tr("Pause"), this);
340     pauseAction->setShortcut(tr("Ctrl+A"));
341     pauseAction->setDisabled(true);
342     stopAction = new QAction(style()->standardIcon(QStyle::SP_MediaStop), tr("Stop"), this);
343     stopAction->setShortcut(tr("Ctrl+S"));
344     stopAction->setDisabled(true);
345     nextAction = new QAction(style()->standardIcon(QStyle::SP_MediaSkipForward), tr("Next"), this);
346     nextAction->setShortcut(tr("Ctrl+N"));
347     previousAction = new QAction(style()->standardIcon(QStyle::SP_MediaSkipBackward), tr("Previous"), this);
348     previousAction->setShortcut(tr("Ctrl+R"));
349     repeatAction = new QAction(style()->standardIcon(QStyle::SP_BrowserReload), tr("Repeat"), this);
350     repeatAction->setCheckable(true);
351     repeatAction->setChecked(repeat);
352     repeatAction->setShortcut(tr("Ctrl+I"));
353     shuffleAction = new QAction(QIcon (QPixmap (":images/shuffle")), tr("Shuffle"), this);
354     shuffleAction->setCheckable(true);
355     shuffleAction->setChecked(shuffle);
356     shuffleAction->setShortcut(tr("Ctrl+H"));
357     volumeAction = new QAction(QIcon (QPixmap (":images/volume")), tr("Volume"), this);
358     volumeAction->setCheckable(true);
359     volumeAction->setShortcut(tr("Ctrl+V"));
360     addFilesAction = new QAction(tr("Add &File"), this);
361     addFilesAction->setShortcut(tr("Ctrl+F"));
362     addFoldersAction = new QAction(tr("Add F&older"), this);
363     addFoldersAction->setShortcut(tr("Ctrl+O"));
364     addUrlAction = new QAction(tr("Add &Url"), this);
365     addUrlAction->setShortcut(tr("Ctrl+U"));
366     savePlaylistAction = new QAction (tr("Sa&ve Playlist"), this);
367     savePlaylistAction->setShortcut(tr ("Ctrl+V"));
368     loadPlaylistAction = new QAction (tr("&Load Playlist"), this);
369     loadPlaylistAction->setShortcut(tr("Ctrl+L"));
370     clearPlaylistAction = new QAction (tr("&Clear Playlist"), this);
371     clearPlaylistAction->setShortcut(tr("Ctrl+C"));
372     exitAction = new QAction(tr("E&xit"), this);
373     exitAction->setShortcut(tr("Ctrl+X"));
374     aboutAction = new QAction(tr("A&bout"), this);
375     aboutAction->setShortcut(tr("Ctrl+B"));
376     aboutQtAction = new QAction(tr("About &Qt"), this);
377     aboutQtAction->setShortcut(tr("Ctrl+Q"));
378
379     connect(playAction, SIGNAL(triggered()), mediaObject, SLOT(play()));
380     connect(pauseAction, SIGNAL(triggered()), mediaObject, SLOT(pause()) );
381     connect(stopAction, SIGNAL(triggered()), mediaObject, SLOT(stop()));
382     connect(repeatAction, SIGNAL(triggered()), this, SLOT(repeatToggle()));
383     connect(shuffleAction, SIGNAL(triggered()), this, SLOT(shuffleToggle()));
384     connect(volumeAction, SIGNAL(triggered()), this, SLOT(volumeToggle()));
385     connect(addFilesAction, SIGNAL(triggered()), this, SLOT(addFiles()));
386     connect(addFoldersAction, SIGNAL(triggered()), this, SLOT(addFolders()));
387     connect(addUrlAction, SIGNAL(triggered()), this, SLOT(addUrl()));
388     connect (savePlaylistAction, SIGNAL (triggered()), this, SLOT (savePlaylist()));
389     connect (loadPlaylistAction, SIGNAL (triggered()), this, SLOT (loadPlaylist()));
390     connect (clearPlaylistAction, SIGNAL (triggered()), this, SLOT (clearPlaylist()));
391     connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
392     connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
393     connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
394 }
395
396 void MainWindow::savePlaylist()
397 {
398     QString filename = QFileDialog::getSaveFileName(this, tr("Please select file name"), "", "*.m3u");
399     if (filename.isEmpty())
400         return;
401     if (filename.length() < 4 || filename.right(4).toLower() != ".m3u")
402         filename += ".m3u";
403     QFile f (filename);
404     try
405     {
406         f.open(QFile::WriteOnly);
407         for (int i = 0; i < sources.size(); ++i)
408         {
409             if (sources[i].type() == Phonon::MediaSource::Stream)
410                 f.write(sources[i].url().toString().toAscii());
411             else
412                 f.write (sources[i].fileName().toAscii());
413             f.write ("\n");
414         }
415         f.close ();
416     }
417     catch (...)
418     {
419         QMessageBox::critical(this, "Write error", "Could not write playlist file", QMessageBox::Ok);
420     }
421 }
422
423 void MainWindow::loadPlaylist()
424 {
425     QString filename = QFileDialog::getOpenFileName(this, tr("Select playlist file to load"), "", "*.m3u");
426     QFile f(filename);
427     f.open (QFile::ReadOnly);
428     QString tmp = f.readAll();
429     f.close ();
430     QStringList lines = tmp.split("\n");
431     clearPlaylist();
432     foreach (QString l, lines)
433     {
434         qDebug () << "Load " << l;
435         Phonon::MediaSource source(l);
436         sources.append(source);
437     }
438     if (!sources.isEmpty())
439         metaInformationResolver->setCurrentSource(sources.at(0));
440     setupShuffleList();
441 }
442
443 void MainWindow::clearPlaylist()
444 {
445     sources.clear();
446     while (musicTable->rowCount())
447         musicTable->removeRow(0);
448     mediaObject->clear();
449 }
450
451 void MainWindow::repeatToggle ()
452 {
453     repeat = !repeat;
454     qDebug() << "Repeat toggled to " << repeat;
455     settings.setValue("repeat", QVariant (repeat));
456 }
457
458 void MainWindow::shuffleToggle ()
459 {
460     shuffle = !shuffle;
461     settings.setValue("shuffle", QVariant (shuffle));
462 }
463
464 void MainWindow::volumeToggle ()
465 {
466     qDebug () << "Volumetoggle: " << volumeAction->isChecked();
467     volumeSlider->setVisible(volumeAction->isChecked());
468 }
469
470
471 void MainWindow::setupMenus()
472 {
473     QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
474     fileMenu->addAction(addFilesAction);
475     fileMenu->addAction(addFoldersAction);
476     fileMenu->addAction(addUrlAction);
477     fileMenu->addAction(savePlaylistAction);
478     fileMenu->addAction(loadPlaylistAction);
479     fileMenu->addAction(clearPlaylistAction);
480     fileMenu->addSeparator();
481     fileMenu->addAction(exitAction);
482
483     QMenu *aboutMenu = menuBar()->addMenu(tr("&Help"));
484     aboutMenu->addAction(aboutAction);
485     aboutMenu->addAction(aboutQtAction);
486 }
487
488 void MainWindow::setupUi()
489 {
490     QToolBar *bar = new QToolBar;
491
492     bar->setOrientation(Qt::Vertical);
493     //bar->addAction(volumeAction);
494
495     seekSlider = new Phonon::SeekSlider(this);
496     seekSlider->setMediaObject(mediaObject);
497
498     volumeSlider = new Phonon::VolumeSlider(this);
499     volumeSlider->setAudioOutput(audioOutput);
500     volumeSlider->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
501     volumeSlider->setOrientation(Qt::Vertical);
502     volumeSlider->setMuteVisible(false);
503 //    volumeAddedAction = bar->addWidget(volumeSlider);
504 //    volumeAddedAction->setVisible(false);
505     bar->addAction(playAction);
506     bar->addAction(pauseAction);
507     bar->addAction(stopAction);
508     bar->addAction(repeatAction);
509     bar->addAction(shuffleAction);
510
511 /*    QLabel *volumeLabel = new QLabel;
512     volumeLabel->setPixmap(QPixmap("images/volume.png"));*/
513
514 /*    QPalette palette;
515     palette.setBrush(QPalette::Light, Qt::darkGray);*/
516
517     timeLcd = new QLCDNumber;
518 //    timeLcd->setPalette(palette);
519
520     QStringList headers;
521     headers << tr("Artist") << tr("Title") << tr("Album") << tr("Year");
522
523     musicTable = new QTableWidget(0, 4);
524     musicTable->setHorizontalHeaderLabels(headers);
525     musicTable->setSelectionMode(QAbstractItemView::SingleSelection);
526     musicTable->setSelectionBehavior(QAbstractItemView::SelectRows);
527     connect(musicTable, SIGNAL(cellDoubleClicked(int,int)),
528         this, SLOT(tableClicked(int,int)));
529
530     QHBoxLayout *seekerLayout = new QHBoxLayout;
531     QToolBar* bar2 = new QToolBar;
532     bar2->addAction(volumeAction);
533     seekerLayout->addWidget(bar2);
534     seekerLayout->addWidget(seekSlider);
535     seekerLayout->addWidget(timeLcd);
536
537     QVBoxLayout *playbackLayout = new QVBoxLayout;
538     volumeSlider->hide ();
539     playbackLayout->addWidget(bar);
540 //    playbackLayout->addStretch();
541 //    playbackLayout->addWidget(volumeSlider);
542 //    playbackLayout->addWidget(volumeLabel);
543
544     QVBoxLayout *seekAndTableLayout = new QVBoxLayout;
545
546     seekAndTableLayout->addWidget(musicTable);
547     seekAndTableLayout->addLayout(seekerLayout);
548
549     QHBoxLayout *mainLayout = new QHBoxLayout;
550     mainLayout->addWidget(volumeSlider);
551     mainLayout->addLayout(seekAndTableLayout);
552     mainLayout->addLayout(playbackLayout);
553
554     QWidget *widget = new QWidget;
555     widget->setLayout(mainLayout);
556
557     setCentralWidget(widget);
558     setWindowTitle("TomAmp");
559 //    ui.setupUi(this);
560 }
561
562
563 void MainWindow::setupShuffleList()
564 {
565     QList<int> tmp;
566     for (int i = 0; i < sources.size(); ++i)
567         tmp.append(i);
568     shuffleList.clear();
569     while (tmp.size ())
570     {
571         int ind = qrand () % tmp.size();
572         shuffleList.append(tmp[ind]);
573         tmp.removeAt(ind);
574     }
575     qDebug () << shuffleList;
576 }