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