notification on playlist save error, small changes
[tomamp] / tomamp / mainwindow.cpp
1 #include <QtGui>
2 #include <QtDebug>
3 #include <QInputDialog>
4 #ifdef Q_WS_MAEMO_5
5 #include <QtMaemo5/QMaemo5InformationBox>
6 #endif
7 #include "mainwindow.h"
8 #include "optiondialog.h"
9 #include "time.h"
10
11 //#define AVOID_INPUT_DIALOG 0
12
13 MainWindow::MainWindow()
14     : plman (this), settings (tr ("TomAmp"), "TomAmp"), isPlaying (false)
15 {
16     setOrientation();
17     audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, this);
18     mediaObject = new Phonon::MediaObject(this);
19
20     mediaObject->setTickInterval(1000);
21     connect(mediaObject, SIGNAL(tick(qint64)), this, SLOT(tick(qint64)));
22     connect(mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
23         this, SLOT(stateChanged(Phonon::State,Phonon::State)));
24     connect(mediaObject, SIGNAL(currentSourceChanged(Phonon::MediaSource)),
25         this, SLOT(sourceChanged(Phonon::MediaSource)));
26     connect(mediaObject, SIGNAL(aboutToFinish()), this, SLOT(aboutToFinish()));
27     connect (&plman, SIGNAL (playlistChanged (int)), this, SLOT (playlistChanged(int)));
28     connect (&plman, SIGNAL (itemUpdated(int)), this, SLOT (itemUpdated (int)));
29     connect (&plman, SIGNAL (itemRemoved(int)), this, SLOT (itemRemoved (int)));
30
31     Phonon::createPath(mediaObject, audioOutput);
32
33     qsrand (time (NULL));
34     repeat = settings.value("repeat", false).toBool();
35     shuffle = settings.value("shuffle", false).toBool();
36     headers = settings.value ("headers", QStringList()).toStringList();
37     setupShuffleList();
38     setupActions();
39     setupMenus();
40     setupUi();
41     show ();
42     timeLcd->display("00:00:00");
43     plman.addStringList(settings.value("lastPlaylist").toStringList());
44     setupShuffleList();
45     int curind = settings.value("currentIndex", -1).toInt ();
46     if (curind >= 0)
47         setItem (curind, false);
48     audioOutput->setVolume(settings.value("volume", .5).toReal());
49     QApplication::setWindowIcon(QIcon (QPixmap (":images/tomamp")));
50 }
51
52 MainWindow::~MainWindow()
53 {
54     settings.setValue("shuffle", shuffle);
55     settings.setValue("repeat", repeat);
56     settings.setValue("lastPlaylist", plman.playlistStrings());
57     settings.setValue("volume", audioOutput->volume());
58     settings.setValue("currentIndex", plman.indexOf(mediaObject->currentSource()));
59     settings.setValue("headers", headers);
60     for (int i = 0; i < musicTable->columnCount(); ++i)
61     {
62         QString lab = QString ("colWidth_%1").arg (i);
63         settings.setValue(lab, musicTable->columnWidth(i));
64     }
65 }
66
67 void MainWindow::setOrientation ()
68 {
69 #ifdef Q_WS_MAEMO_5
70     QString orient = settings.value("orientation", "Automatic").toString();
71     if (orient == "Portrait")
72         setAttribute(Qt::WA_Maemo5PortraitOrientation, true);
73     else if (orient == "Landscape")
74         setAttribute(Qt::WA_Maemo5LandscapeOrientation, true);
75     else
76         setAttribute(Qt::WA_Maemo5AutoOrientation, true);
77 #endif
78 }
79
80 void MainWindow::addFiles()
81 {
82     QString folder = settings.value("LastFolder").toString();
83     if (folder.isEmpty())
84         folder = QDesktopServices::storageLocation(QDesktopServices::MusicLocation);
85     QStringList files = QFileDialog::getOpenFileNames(this, tr("Select Files To Add"),
86                     folder, "Music files (*.mp3 *.ogg *.wav *.flac);;Playlists (*.m3u *.pls)");
87
88     if (files.isEmpty())
89         return;
90
91     QString dir = QFileInfo (files[0]).absoluteDir().absolutePath();
92     settings.setValue("LastFolder", dir);
93     QStringList toadd;
94     foreach (QString string, files)
95     {
96         if (string.toLower().endsWith(".pls") || string.toLower().endsWith(".m3u"))
97             plman.addPlaylist(string);
98         else
99             toadd.append (string);
100     }
101     plman.addStringList(toadd);
102 }
103
104 void MainWindow::addFolder()
105 {
106     QString folder = settings.value("LastFolder").toString();
107     if (folder.isEmpty())
108         folder = QDesktopServices::storageLocation(QDesktopServices::MusicLocation);
109     QString dir = QFileDialog::getExistingDirectory(this,
110             tr("Select Directory To Add"),
111             folder);
112
113     if (dir.isEmpty())
114         return;
115
116     settings.setValue("LastFolder", dir);
117
118     QStringList filters;
119     QStringList files = QDir (dir).entryList(filters, QDir::AllDirs);
120     files.removeAll(".");
121     files.removeAll("..");
122     bool recursive = false;
123     if (files.size())
124         recursive = QMessageBox::question(this, "Add all folders", "Subfolders have been detected, add everything?", QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
125     plman.parseAndAddFolder(dir, recursive);
126 }
127
128
129 void MainWindow::addUrl()
130 {
131 #ifdef AVOID_INPUT_DIALOG
132     QString url = "http://war.str3am.com:7970";
133 #else
134     QString url = QInputDialog::getText(this, "Get URL", "Please type in the stream URL");
135 #endif
136     if (url.isEmpty() || !url.toLower().startsWith("http"))
137         return;
138     QStringList toadd;
139     toadd << url;
140     plman.addStringList(toadd);
141 }
142
143
144 void MainWindow::about()
145 {
146     QMessageBox::information(this, tr("About TomAmp v0.2"),
147         tr("TomAmp is a simple playlist-based music player.\n\n"
148         "(c) 2010 Tamas Marki <tmarki@gmail.com>\n\n"
149         "Please send comments and bug reports to the above e-mail address.\n\n"
150         "Icons by http://itweek.deviantart.com/"));
151 }
152
153 void MainWindow::stateChanged(Phonon::State newState, Phonon::State /* oldState */)
154 {
155     switch (newState)
156     {
157         case Phonon::ErrorState:
158             if (mediaObject->errorType() == Phonon::FatalError)
159             {
160 //                QMessageBox::warning(this, tr("Fatal Error"),
161 //                mediaObject->errorString() + mediaObject->currentSource().fileName() + ", " + mediaObject->currentSource().url().toString());
162             }
163             else
164             {
165 //                QMessageBox::warning(this, tr("Error"),
166 //                mediaObject->errorString());
167             }
168             next ();
169             break;
170         case Phonon::PlayingState:
171             setWindowTitle(mediaObject->metaData().value("TITLE") + "(" + mediaObject->metaData().value("ARTIST") + ") - TomAmp");
172             pauseAction->setVisible(true);
173             playAction->setVisible (false);
174             playAction->setEnabled(false);
175             pauseAction->setEnabled(true);
176             stopAction->setEnabled(true);
177             //lastPlayed = plman.indexOf(mediaObject->currentSource());
178             break;
179         case Phonon::StoppedState:
180             setWindowTitle("TomAmp");
181             stopAction->setEnabled(false);
182             playAction->setEnabled(true);
183             pauseAction->setVisible(false);
184             playAction->setVisible(true);
185             pauseAction->setEnabled(false);
186             timeLcd->display("00:00:00");
187             unhighlightRow(plman.indexOf(mediaObject->currentSource()));
188             break;
189         case Phonon::PausedState:
190             pauseAction->setEnabled(false);
191             stopAction->setEnabled(true);
192             pauseAction->setVisible(false);
193             playAction->setVisible(true);
194             playAction->setEnabled(true);
195             break;
196         case Phonon::BufferingState:
197             break;
198         default:
199         ;
200     }
201 }
202
203 void MainWindow::next()
204 {
205     bool wasPlaying = isPlaying;
206     if (mediaObject->state () == Phonon::ErrorState)
207         wasPlaying = true;
208     if (mediaObject->queue().size())
209     {
210         setItem (plman.indexOf(mediaObject->queue()[0]), wasPlaying);
211         return;
212     }
213     int index = plman.indexOf(mediaObject->currentSource());
214     if (shuffle)
215     {
216         index = shuffleList.indexOf(plman.indexOf(mediaObject->currentSource())) + 1;
217         while (index < shuffleList.size () && !plman.getItem(shuffleList[index]).playable)
218         {
219             index += 1;
220         }
221         if (index < shuffleList.size ())
222         {
223             setItem (index, wasPlaying);
224         }
225         else if (repeat)
226         {
227             index = 0;
228             while ((index) < shuffleList.size () && !plman.getItem(shuffleList[index]).playable)
229             {
230                 index += 1;
231             }
232             setItem (index, wasPlaying);
233         }
234     }
235     else
236     {
237         index++;
238         while ((index) < plman.size () && !plman.getItem(index).playable)
239         {
240             index += 1;
241         }
242         if (index < plman.size())
243         {
244             setItem (index, wasPlaying);
245         }
246         else if (repeat)
247         {
248             index = 0;
249             while ((index) < plman.size () && !plman.getItem(index).playable)
250             {
251                 index += 1;
252             }
253             setItem (index, wasPlaying);
254         }
255     }
256 }
257
258 void MainWindow::setItem(int i, bool doplay)
259 {
260     if (i < plman.size() && i >= 0)
261     {
262         if (lastPlayed >= 0)
263             unhighlightRow(lastPlayed);
264         if (shuffle)
265         {
266             mediaObject->setCurrentSource(plman.at (shuffleList[i]));
267         }
268         else
269         {
270             mediaObject->setCurrentSource(plman.at(i));
271         }
272     }
273     if (doplay && mediaObject->currentSource().type() != Phonon::MediaSource::Invalid)
274     {
275         play();
276     }
277     else
278         stop ();
279 }
280
281 void MainWindow::previous()
282 {
283     bool wasPlaying = isPlaying;//(mediaObject->state () == Phonon::PlayingState);
284     if (mediaObject->state () == Phonon::ErrorState)
285         wasPlaying = true;
286     int index = plman.indexOf(mediaObject->currentSource());
287     if (shuffle)
288     {
289         index = shuffleList.indexOf(plman.indexOf(mediaObject->currentSource())) - 1;
290         while (index >= 0 && !plman.getItem(shuffleList[index]).playable)
291         {
292             index--;
293         }
294         if (index >= 0)
295         {
296             setItem (index, wasPlaying);
297         }
298         else if (repeat)
299         {
300             index = plman.size () - 1;
301             while (index >= 0 && !plman.getItem(shuffleList[index]).playable)
302             {
303                 index--;
304             }
305             setItem (index, wasPlaying);
306         }
307 /*        if (index < 0)
308             wasPlaying = false;*/
309
310     }
311     else
312     {
313         index--;
314         while ((index) >= 0 && !plman.getItem(index).playable)
315         {
316             index--;
317         }
318         if (index >= 0)
319         {
320             setItem (index, wasPlaying);
321         }
322         else if (repeat)
323         {
324             index = plman.size() - 1;
325             while ((index) >= 0 && !plman.getItem(index).playable)
326             {
327                 index--;
328             }
329             setItem (index, wasPlaying);
330         }
331     }
332 }
333
334 void MainWindow::highlightRow (int i)
335 {
336     for (int j = 0; j < 3; ++j)
337     {
338         QTableWidgetItem* item = musicTable->item(i, j);
339         if (item)
340         {
341             QFont font = item->font();
342             font.setBold(true);
343             font.setItalic(true);
344             item->setFont(font);
345         }
346     }
347 }
348
349 void MainWindow::unhighlightRow (int i)
350 {
351     for (int j = 0; j < 3; ++j)
352     {
353         QTableWidgetItem* item = musicTable->item(i, j);
354         if (item)
355         {
356             QFont font = item->font();
357             font.setBold(false);
358             font.setItalic(false);
359             item->setFont(font);
360         }
361     }
362 }
363
364
365 void MainWindow::tick(qint64 time)
366 {
367     QTime displayTime((time / 3600000), (time / 60000) % 60, (time / 1000) % 60);
368
369     timeLcd->display(displayTime.toString("HH:mm:ss"));
370 }
371
372 void MainWindow::tableClicked(int row, int /* column */)
373 {
374 //    bool wasPlaying = mediaObject->state() == Phonon::PlayingState;
375
376 /*    mediaObject->stop();
377     mediaObject->clearQueue();*/
378
379     if (row >= plman.size())
380         return;
381
382     int index = row;
383     while (index < shuffleList.size () && !plman.getItem(index).playable)
384     {
385         index += 1;
386     }
387     if (plman.size() > index)
388     {
389         if (shuffle)
390             index = shuffleList.indexOf(index);
391         setItem (index, true);
392 //        mediaObject->play();
393     }
394     else
395     {
396         index = 0;
397         while (index < plman.size () && !plman.getItem(index).playable)
398         {
399             index += 1;
400         }
401         if (plman.size() > index)
402         {
403             if (shuffle)
404                 index = shuffleList.indexOf(index);
405             setItem (index, true);
406 //            mediaObject->play();
407         }
408     }
409
410 }
411
412 void MainWindow::sourceChanged(const Phonon::MediaSource &source)
413 {
414     int ind = plman.indexOf(source);
415     highlightRow(ind);
416     unhighlightRow(lastPlayed);
417     lastPlayed = ind;
418     musicTable->selectRow(ind);
419     timeLcd->display("00:00:00");
420 }
421
422
423 void MainWindow::aboutToFinish()
424 {
425     if (mediaObject->queue().size())
426         return;
427     int index = plman.indexOf(mediaObject->currentSource()) + 1;
428     if (shuffle)
429     {
430         index = shuffleList.indexOf(plman.indexOf(mediaObject->currentSource())) + 1;
431         if (index < shuffleList.size ())
432         {
433             mediaObject->enqueue(plman.at (shuffleList[index]));
434         }
435         else if (repeat)
436         {
437             mediaObject->enqueue(plman.at (shuffleList[0]));
438         }
439
440     }
441     else
442     {
443         if (plman.size() > index)
444         {
445             mediaObject->enqueue(plman.at(index));
446         }
447         else if (repeat)
448         {
449             mediaObject->enqueue(plman.at(0));
450         }
451     }
452 }
453
454 void MainWindow::finished()
455 {
456 }
457
458 void MainWindow::setupActions()
459 {
460     playAction = new QAction(QIcon (QPixmap (":images/play")), "", this);
461     playAction->setShortcut(tr("Crl+P"));
462     playAction->setDisabled(true);
463     pauseAction = new QAction(QIcon (QPixmap (":images/pause")), "", this);
464     pauseAction->setShortcut(tr("Ctrl+A"));
465     pauseAction->setDisabled(true);
466     pauseAction->setVisible(false);
467     stopAction = new QAction(QIcon (QPixmap (":images/stop")), "", this);
468     stopAction->setShortcut(tr("Ctrl+S"));
469     stopAction->setDisabled(true);
470     nextAction = new QAction(QIcon (QPixmap (":images/next")), "", this);
471     nextAction->setShortcut(tr("Ctrl+N"));
472     upAction = new QAction (QString::fromUtf8("▲"), this);
473     downAction = new QAction (QString::fromUtf8("▼"), this);
474     delAction = new QAction (QString::fromUtf8("╳"), this);
475     previousAction = new QAction(QIcon (QPixmap (":images/previous")), "", this);
476     previousAction->setShortcut(tr("Ctrl+R"));
477     if (repeat)
478         repeatAction = new QAction(QIcon (QPixmap (":images/repeatActive")), "", this);
479     else
480         repeatAction = new QAction(QIcon (QPixmap (":images/repeat")), "", this);
481     repeatAction->setCheckable(true);
482     repeatAction->setChecked(repeat);
483     repeatAction->setShortcut(tr("Ctrl+I"));
484     if (shuffle)
485         shuffleAction = new QAction(QIcon (QPixmap (":images/shuffleActive")), "", this);
486     else
487         shuffleAction = new QAction(QIcon (QPixmap (":images/shuffle")), "", this);
488     shuffleAction->setCheckable(true);
489     shuffleAction->setChecked(shuffle);
490     shuffleAction->setShortcut(tr("Ctrl+H"));
491     volumeAction = new QAction(QIcon (QPixmap (":images/volume")), "", this);
492     volumeAction->setCheckable(true);
493     volumeAction->setShortcut(tr("Ctrl+V"));
494     addFilesAction = new QAction(tr("Add &File"), this);
495     addFilesAction->setShortcut(tr("Ctrl+F"));
496     addFoldersAction = new QAction(tr("Add F&older"), this);
497     addFoldersAction->setShortcut(tr("Ctrl+O"));
498     addUrlAction = new QAction(tr("Add &Url"), this);
499     addUrlAction->setShortcut(tr("Ctrl+U"));
500     savePlaylistAction = new QAction (tr("Sa&ve Playlist"), this);
501     savePlaylistAction->setShortcut(tr ("Ctrl+V"));
502     loadPlaylistAction = new QAction (tr("&Load Playlist"), this);
503     loadPlaylistAction->setShortcut(tr("Ctrl+L"));
504     clearPlaylistAction = new QAction (tr("&Clear Playlist"), this);
505     clearPlaylistAction->setShortcut(tr("Ctrl+C"));
506     optionAction = new QAction (tr("Op&tions"), this);
507     optionAction->setShortcut(tr("Ctrl+T"));
508     exitAction = new QAction(tr("E&xit"), this);
509     exitAction->setShortcut(tr("Ctrl+X"));
510     aboutAction = new QAction(tr("A&bout"), this);
511     aboutAction->setShortcut(tr("Ctrl+B"));
512     aboutQtAction = new QAction(tr("About &Qt"), this);
513     aboutQtAction->setShortcut(tr("Ctrl+Q"));
514 /*    removeSelected = new QAction (tr("&Delete from playlist"));
515     removeSelected->setShortcut(tr ("Ctrl+D"));*/
516
517     connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
518     connect(pauseAction, SIGNAL(triggered()), mediaObject, SLOT(pause()) );
519     connect(stopAction, SIGNAL(triggered()), this, SLOT(stop()));
520     connect(repeatAction, SIGNAL(triggered()), this, SLOT(repeatToggle()));
521     connect(shuffleAction, SIGNAL(triggered()), this, SLOT(shuffleToggle()));
522     connect(volumeAction, SIGNAL(triggered()), this, SLOT(volumeToggle()));
523
524     connect(addFilesAction, SIGNAL(triggered()), this, SLOT(addFiles()));
525     connect(addFoldersAction, SIGNAL(triggered()), this, SLOT(addFolder()));
526     connect(addUrlAction, SIGNAL(triggered()), this, SLOT(addUrl()));
527     connect (savePlaylistAction, SIGNAL (triggered()), this, SLOT (savePlaylist()));
528     connect (loadPlaylistAction, SIGNAL (triggered()), this, SLOT (loadPlaylist()));
529     connect (clearPlaylistAction, SIGNAL (triggered()), &plman, SLOT (clearPlaylist()));
530     connect (optionAction, SIGNAL (triggered()), this, SLOT (showOptions()));
531     connect (nextAction, SIGNAL(triggered()), this, SLOT(next()));
532     connect (previousAction, SIGNAL(triggered()), this, SLOT(previous()));
533     connect (upAction, SIGNAL(triggered()), this, SLOT(upSelected()));
534     connect (downAction, SIGNAL(triggered()), this, SLOT(downSelected()));
535     connect (delAction, SIGNAL(triggered()), this, SLOT(removeSelectedItem()));
536     connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
537     connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
538     connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
539 //    connect (removeSelected, SIGNAL (triggered()), this, SLOT (removeSelectedItem()));
540 }
541
542 void MainWindow::removeSelectedItem()
543 {
544     if (QMessageBox::question(this, "Confirm remove", "Are you sure you want to remove this item?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
545         return;
546     int row = musicTable->currentRow();
547     if (row >= 0)
548         plman.removeItem(row);
549 }
550
551 void MainWindow::removeAllButSelectedItem()
552 {
553     if (QMessageBox::question(this, "Confirm remove", "Are you sure you want to remove all other items?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
554         return;
555     int row = musicTable->currentRow();
556     if (row >= 0)
557     {
558         QString uri = plman.getItem(row).uri;
559         QStringList lst;
560         lst << uri;
561         plman.clearPlaylist();
562         plman.addStringList(lst);
563     }
564 }
565
566 void MainWindow::repeatToggle ()
567 {
568     repeat = !repeat;
569     settings.setValue("repeat", QVariant (repeat));
570     if (repeat)
571         repeatAction->setIcon(QIcon (QPixmap (":images/repeatActive")));
572     else
573         repeatAction->setIcon(QIcon (QPixmap (":images/repeat")));
574 }
575
576 void MainWindow::shuffleToggle ()
577 {
578     shuffle = !shuffle;
579     settings.setValue("shuffle", QVariant (shuffle));
580     if (shuffle)
581         shuffleAction->setIcon(QIcon (QPixmap (":images/shuffleActive")));
582     else
583         shuffleAction->setIcon(QIcon (QPixmap (":images/shuffle")));
584 }
585
586 void MainWindow::volumeToggle ()
587 {
588     volumeSlider->setVisible(volumeAction->isChecked());
589 }
590
591 void MainWindow::play()
592 {
593     mediaObject->play();
594     lastPlayed = plman.indexOf(mediaObject->currentSource());
595     highlightRow(lastPlayed);
596     isPlaying = true;
597 }
598
599 void MainWindow::stop()
600 {
601     mediaObject->stop();
602     isPlaying = false;
603 }
604
605
606 void MainWindow::setupMenus()
607 {
608     QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
609     fileMenu->addAction(addFilesAction);
610     fileMenu->addAction(addFoldersAction);
611     fileMenu->addAction(addUrlAction);
612     fileMenu->addSeparator();
613     fileMenu->addAction(savePlaylistAction);
614     fileMenu->addAction(loadPlaylistAction);
615     fileMenu->addAction(clearPlaylistAction);
616     fileMenu->addAction(optionAction);
617 //    fileMenu->addAction(exitAction);
618
619     QMenu *aboutMenu = menuBar()->addMenu(tr("&Help"));
620     aboutMenu->addAction(aboutAction);
621     aboutMenu->addAction(aboutQtAction);
622 }
623
624 void MainWindow::setupUi()
625 {
626     QToolBar *bar = new QToolBar;
627
628     bar->setOrientation(Qt::Vertical);
629     bar->setStyleSheet("padding:7px");
630     //bar->addAction(volumeAction);
631
632     seekSlider = new Phonon::SeekSlider(this);
633     seekSlider->setMediaObject(mediaObject);
634
635     volumeSlider = new Phonon::VolumeSlider(this);
636     volumeSlider->setAudioOutput(audioOutput);
637     volumeSlider->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
638     volumeSlider->setOrientation(Qt::Horizontal);
639     volumeSlider->setMuteVisible(false);
640 //    volumeAddedAction = bar->addWidget(volumeSlider);
641 //    volumeAddedAction->setVisible(false);
642     bar->addAction(playAction);
643     bar->addAction(pauseAction);
644     bar->addAction(stopAction);
645     bar->addAction(repeatAction);
646     bar->addAction(shuffleAction);
647     bar->addAction(nextAction);
648     bar->addAction(previousAction);
649     bar->addAction(upAction);
650     bar->addAction(downAction);
651     bar->addAction(delAction);
652
653     contextMenu = new QMenu (this);
654     enqueueAction = contextMenu->addAction(tr ("Enqueue"));
655     removeSelected = contextMenu->addAction(tr ("Remove selected"));
656     removeAllButSelected = contextMenu->addAction(tr("Remove all but selected"));
657     connect (removeSelected, SIGNAL (triggered()), this, SLOT (removeSelectedItem()));
658     connect (removeAllButSelected, SIGNAL (triggered()), this, SLOT (removeAllButSelectedItem()));
659     connect (enqueueAction, SIGNAL (triggered()), this, SLOT (enqueueSelected()));
660
661
662     timeLcd = new QLCDNumber;
663
664     if (!headers.size ())
665     {
666         headers << "Artist" << "Title" << "Album";
667         settings.setValue("headers", headers);
668     }
669
670     musicTable = new QTableWidget(0, headers.size ());
671     musicTable->setHorizontalHeaderLabels(headers);
672     musicTable->setSelectionMode(QAbstractItemView::SingleSelection);
673     musicTable->setSelectionBehavior(QAbstractItemView::SelectRows);
674     connect(musicTable, SIGNAL(cellDoubleClicked(int,int)),
675         this, SLOT(tableClicked(int,int)));
676     connect(musicTable, SIGNAL(cellClicked(int,int)),
677         this, SLOT(cellClicked(int,int)));
678     for (int i = 0; i < musicTable->columnCount(); ++i)
679     {
680         QString lab = QString ("colWidth_%1").arg (i);
681         int val = settings.value(lab, 0).toInt();
682         if (val)
683             musicTable->setColumnWidth(i, val);
684     }
685
686
687     QHBoxLayout *seekerLayout = new QHBoxLayout;
688     QToolBar* bar2 = new QToolBar;
689     bar2->addAction(volumeAction);
690     seekerLayout->addWidget(bar2);
691     seekerLayout->addWidget(volumeSlider);
692     seekerLayout->addWidget(seekSlider);
693     seekerLayout->addWidget(timeLcd);
694
695     QVBoxLayout *playbackLayout = new QVBoxLayout;
696     volumeSlider->hide ();
697     playbackLayout->addWidget(bar);
698
699     QVBoxLayout *seekAndTableLayout = new QVBoxLayout;
700
701     seekAndTableLayout->addWidget(musicTable);
702     seekAndTableLayout->addLayout(seekerLayout);
703
704     QHBoxLayout *mainLayout = new QHBoxLayout;
705     mainLayout->addLayout(seekAndTableLayout);
706     mainLayout->addLayout(playbackLayout);
707
708     QWidget *widget = new QWidget;
709     widget->setLayout(mainLayout);
710
711     setCentralWidget(widget);
712     setWindowTitle("TomAmp");
713 }
714
715 void MainWindow::cellClicked(int /*row*/, int)
716 {
717 }
718
719 void MainWindow::enqueueSelected()
720 {
721     int sel = musicTable->currentRow();
722     if (sel >= 0)
723     {
724         mediaObject->queue().clear();
725         mediaObject->enqueue(plman.at(sel));
726 #ifdef Q_WS_MAEMO_5
727         QMaemo5InformationBox::information(this, tr ("Enqueued as next song"),
728         QMaemo5InformationBox::DefaultTimeout);
729 #endif
730
731     }
732 }
733
734 void MainWindow::contextMenuEvent (QContextMenuEvent*e)
735 {
736     if (!childAt (e->pos()))
737         return;
738     if (childAt (e->pos())->parentWidget() != musicTable)
739         return;
740     contextMenu->popup(e->globalPos());
741 }
742
743
744 void MainWindow::setupShuffleList()
745 {
746     QList<int> tmp;
747     int index = plman.indexOf(mediaObject->currentSource());
748     if (index < 0)
749         index = 0;
750     for (int i = 0; i < plman.size(); ++i)
751     {
752         if ((i != index))
753             tmp.append(i);
754     }
755     shuffleList.clear();
756     shuffleList.append (index);
757     while (tmp.size ())
758     {
759         int ind = qrand () % tmp.size();
760         shuffleList.append(tmp[ind]);
761         tmp.removeAt(ind);
762     }
763 }
764
765 void MainWindow::savePlaylist ()
766 {
767     QString filename = QFileDialog::getSaveFileName(this, tr("Please select file name"), "", "Playlist Files (*.m3u *.pls)");
768     if (filename.isEmpty())
769         return;
770     if (!plman.savePlaylist(filename))
771     {
772 #ifdef Q_WS_MAEMO_5
773         QMaemo5InformationBox::information(this, tr ("Error writing playlist file"),
774         QMaemo5InformationBox::DefaultTimeout);
775 #else
776         QMessageBox::critical(this, "Write error", "Error writing playlist file", QMessageBox::Ok);
777 #endif
778     }
779
780 }
781
782 void MainWindow::loadPlaylist ()
783 {
784     QString filename = QFileDialog::getOpenFileName(this, tr("Select playlist file to load"), "", "*.m3u *.pls");
785     if (filename.isEmpty())
786         return;
787     plman.loadPlaylist (filename);
788 }
789
790 void MainWindow::playlistChanged(int from)
791 {
792     while (musicTable->rowCount() > from)
793     {
794         musicTable->removeRow(musicTable->rowCount () - 1);
795     }
796     int firstGood = -1;
797     for (int i = from; i < plman.size (); ++i)
798     {
799         if (firstGood < 0 && plman.getItem (i).playable)
800             firstGood = i;
801         int currentRow = musicTable->rowCount();
802         musicTable->insertRow(currentRow);
803         setRowFromItem (currentRow, plman.getItem(i));
804     }
805 /*    if (plman.indexOf(mediaObject->currentSource()) < 0)
806     {
807         setItem (firstGood, false);
808     }*/
809     setupShuffleList();
810 }
811
812 void MainWindow::setRowFromItem (int row, const PlaylistItem& item)
813 {
814     if (row >= musicTable->rowCount())
815         return;
816     if (item.artist.isEmpty() && item.title.isEmpty())
817     {
818         int col = headers.indexOf("Title");
819         if (col >= 0)
820         {
821             QTableWidgetItem *item1 = new QTableWidgetItem(item.uri);
822             item1->setFlags(item1->flags() ^ Qt::ItemIsEditable);
823             musicTable->setItem(row, col, item1);
824         }
825     }
826     else
827     {
828         int col = headers.indexOf("Artist");
829         if (col >= 0)
830         {
831             QTableWidgetItem *item1 = new QTableWidgetItem(item.artist);
832             item1->setFlags(item1->flags() ^ Qt::ItemIsEditable);
833             musicTable->setItem(row, col, item1);
834         }
835         col = headers.indexOf("Title");
836         if (col >= 0)
837         {
838             if (!musicTable->item (row, col))
839             {
840                 QTableWidgetItem *item2 = new QTableWidgetItem(item.title);
841                 item2->setFlags(item2->flags() ^ Qt::ItemIsEditable);
842                 musicTable->setItem(row, col, item2);
843             }
844             else
845             {
846                 musicTable->item (row, col)->setText(item.title);
847             }
848         }
849         col = headers.indexOf("Album");
850         if (col >= 0)
851         {
852             QTableWidgetItem *item3 = new QTableWidgetItem(item.album);
853             item3->setFlags(item3->flags() ^ Qt::ItemIsEditable);
854
855             musicTable->setItem(row, col, item3);
856         }
857     }
858
859     int controlCol = headers.indexOf("Controls");
860     if (controlCol >= 0 && !musicTable->cellWidget(row, controlCol))
861     {
862         QLabel* label = new QLabel;
863         label->setText(QString::fromUtf8("<b><a style='text-decoration:none' href='up'>▲</a> <a style='text-decoration:none' href='down'>▼</a> <a style='text-decoration:none' href='del'>╳</a> <a style='text-decoration:none' href='info'>i</a></b>"));
864         label->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
865         label->setProperty("row", row);
866         musicTable->setCellWidget(row, controlCol, label);
867         connect (label, SIGNAL (linkActivated (const QString&)),  this, SLOT (playlistControl (const QString&)));
868     }
869 }
870
871 void MainWindow::playlistControl (const QString& con)
872 {
873     int i = sender ()->property("row").toInt();
874     qDebug () << "Playlist control: " << con << " on " << i;
875     if (con == "up")
876         buttonUp(i);
877     else if (con == "down")
878         buttonDown(i);
879     else if (con == "del")
880         buttonDel (i);
881     else
882         QMessageBox::information(this, tr ("Coming up..."), tr ("This feature is not implemented yet."));
883 }
884
885
886 void MainWindow::buttonUp(int i)
887 {
888     qDebug () << "Presses up on " << i;
889     if (i)
890     {
891         plman.moveItemUp(i);
892         setRowFromItem (i, plman.getItem(i));
893         setRowFromItem (i - 1, plman.getItem(i - 1));
894         int controlCol = headers.indexOf("Controls");
895         if (controlCol >= 0)
896         {
897             musicTable->cellWidget(i, controlCol)->setProperty("row", i);
898             musicTable->cellWidget(i - 1, controlCol)->setProperty("row", i - 1);
899         }
900         musicTable->selectRow(i - 1);
901     }
902 }
903
904 void MainWindow::buttonDown(int i)
905 {
906     qDebug () << "Presses down on " << i;
907     if (i < plman.size() - 1)
908     {
909         plman.moveItemDown(i);
910         setRowFromItem (i, plman.getItem(i));
911         setRowFromItem (i + 1, plman.getItem(i + 1));
912         int controlCol = headers.indexOf("Controls");
913         if (controlCol >= 0)
914         {
915             musicTable->cellWidget(i, controlCol)->setProperty("row", i);
916             musicTable->cellWidget(i + 1, controlCol)->setProperty("row", i + 1);
917         }
918         musicTable->selectRow(i + 1);
919     }
920 }
921
922 void MainWindow::buttonDel(int i)
923 {
924     qDebug () << "Presses del on " << i;
925     if (QMessageBox::question(this, "Confirm remove", "Are you sure you want to remove this item?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
926         return;
927     if (i < plman.size())
928     {
929         plman.removeItem(i);
930     }
931 }
932
933 void MainWindow::itemUpdated(int index)
934 {
935     if (plman.indexOf(mediaObject->currentSource()) < 0 && plman.getItem (index).playable)
936     {
937         setItem (index, false);
938     }
939     setRowFromItem (index, plman.getItem(index));
940     if (plman.indexOf(mediaObject->currentSource()) == index)
941     {
942         if (shuffle) index = shuffleList.indexOf(index);
943         setItem (index, false);
944     }
945 }
946
947 void MainWindow::itemRemoved (int i)
948 {
949     musicTable->removeRow(i);
950     int controlCol = headers.indexOf("Controls");
951     if (controlCol < 0)
952         return;
953     for (int j = i ? (i - 1) : 0; j < musicTable->rowCount(); ++j)
954     {
955         if (musicTable->cellWidget(j, controlCol))
956             musicTable->cellWidget(j, controlCol)->setProperty("row", j);
957     }
958 }
959
960 void MainWindow::upSelected()
961 {
962     int sel = musicTable->currentRow();
963     if (sel > 0)
964         buttonUp(sel);
965 }
966
967 void MainWindow::downSelected()
968 {
969     int sel = musicTable->currentRow();
970     if (sel >= 0)
971         buttonDown(sel);
972 }
973
974
975
976 void MainWindow::showOptions ()
977 {
978     OptionDialog* dlg = new OptionDialog (this, settings);
979     dlg->exec();
980     delete dlg;
981     setOrientation ();
982     if (headers != settings.value("headers", QStringList ()).toStringList())
983     {
984         headers = settings.value("headers", QStringList ()).toStringList();
985         musicTable->setColumnCount(headers.size ());
986         musicTable->setHorizontalHeaderLabels(headers);
987         playlistChanged(0);
988     }
989 }