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