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