Version bump
[someplayer] / src / mainwindow.cpp
1 /*
2  * SomePlayer - An alternate music player for Maemo 5
3  * Copyright (C) 2010 Nikolay (somebody) Tischenko <niktischenko@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20 #include "mainwindow.h"
21 #include "ui_mainwindow.h"
22 #include <QFileDialog>
23 #include <QMessageBox>
24 #include <QInputDialog>
25 #include <QFile>
26 #include <QDesktopWidget>
27 #include <QTranslator>
28 #include <QKeyEvent>
29
30 #include "library.h"
31 #include "timerdialog.h"
32 #include "equalizerdialog.h"
33 #include "saveplaylistdialog.h"
34 #include "settingsform.h"
35
36 using namespace SomePlayer::DataObjects;
37 using namespace SomePlayer::Storage;
38 using namespace SomePlayer::Playback;
39
40 MainWindow::MainWindow(QWidget *parent) :
41         QMainWindow(parent),
42         ui(new Ui::MainWindow)
43 {
44         _display_unlocked = true; // in most cases
45         Config config;
46         _library = new Library(config.applicationDir(), config.applicationDir());
47         _translator = new QTranslator(this);
48         ui->setupUi(this);
49         connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about()));
50         connect(ui->actionSettings, SIGNAL(triggered()), this, SLOT(settings()));
51         setAnimated(true);
52         setAttribute(Qt::WA_Maemo5StackedWindow);
53         _player_form = new PlayerForm(_library, this);
54         ui->centralWidget->layout()->addWidget(_player_form);
55         _library_form = new LibraryForm(_library, this);
56         _directory_form = new DirectoryView(this);
57         _directory_form->hide();
58         _timer = new QTimer(this);
59         _equalizer_dialog = new EqualizerDialog(this);
60         _manage_library_form = new ManageLibraryForm(_library, this);
61         _settings_form = new SettingsForm(this);
62         _settings_form->hide();
63         _about_form = new AboutForm(this);
64         _about_form->hide();
65         connect(_player_form, SIGNAL(library()), this, SLOT(library()));
66         connect(_library_form, SIGNAL(refreshPlayer()), this, SLOT(player()));
67         connect(ui->actionManageLibrary, SIGNAL(triggered()), this, SLOT(_manage_library()));
68         connect(ui->actionSavePlaylist, SIGNAL(triggered()), this, SLOT(_save_playlist()));
69         connect(ui->actionImport, SIGNAL(triggered()), this, SLOT(_import_playlits()));
70         connect(_player_form, SIGNAL(clearPlaylist()), this, SLOT(_clear_current_playlist()));
71         connect(ui->actionSetTimer, SIGNAL(triggered()), this, SLOT(_set_timer()));
72         connect(ui->actionEqualizer, SIGNAL(triggered()), this, SLOT(_equalizer()));
73         connect(ui->actionOnlineHelp, SIGNAL(triggered()), _about_form, SLOT(onlineHelp()));
74         connect(_library, SIGNAL(done()), _library_form, SLOT(refresh()));
75         connect(_player_form, SIGNAL(refreshLibrary()), _library_form, SLOT(refresh()));
76         connect(_manage_library_form, SIGNAL(refreshLibrary()), _library_form, SLOT(refresh()));
77         connect(_timer, SIGNAL(timeout()), this, SLOT(_timeout()));
78         connect(_equalizer_dialog, SIGNAL(valueChanged(int,int)), this, SLOT(_equalizer_value_changed(int, int)));
79         connect(_equalizer_dialog, SIGNAL(equalizerEnabled()), _player_form, SLOT(enableEqualizer()));
80         connect(_equalizer_dialog, SIGNAL(equalizerDisabled()), _player_form, SLOT(disableEqualizer()));
81         connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(_orientation_changed()));
82         connect(_player_form, SIGNAL(fullscreen(bool)), this, SLOT(_fullscreen(bool)));
83         connect(_library_form, SIGNAL(addAndPlay(Track)), _player_form, SLOT(play(Track)));
84         connect(_directory_form, SIGNAL(addAndPlay(Track)), _player_form, SLOT(play(Track)));
85         connect(_player_form, SIGNAL(dirView()), _directory_form, SLOT(show()));
86         connect(_directory_form, SIGNAL(addTracks(QList<Track>)), this, SLOT(_add_tracks(QList<Track>)));
87         connect(_directory_form, SIGNAL(clearCurrent()), this, SLOT(_clear_current()));
88         connect(_settings_form, SIGNAL(iconsChanged()), _player_form, SLOT(updateIcons()));
89         connect(_settings_form, SIGNAL(iconsChanged()), _library_form, SLOT(updateIcons()));
90         connect(_settings_form, SIGNAL(iconsChanged()), _manage_library_form, SLOT(updateIcons()));
91         connect(_settings_form, SIGNAL(iconsChanged()), _directory_form, SLOT(updateIcons()));
92         connect(_settings_form, SIGNAL(iconsChanged()), _about_form, SLOT(updateIcons()));
93         connect(_settings_form, SIGNAL(gradientChanged()), _player_form, SLOT(checkGradient()));
94         connect(_settings_form, SIGNAL(gradientChanged()), _library_form, SLOT(checkGradient()));
95         connect(_settings_form, SIGNAL(gradientChanged()), _directory_form, SLOT(checkGradient()));
96         connect(_settings_form, SIGNAL(libraryOptionsChanged()), _library_form, SLOT(refresh()));
97         connect(_settings_form, SIGNAL(orientationChanged()), this, SLOT(_change_orientation()));
98         connect(_settings_form, SIGNAL(translationChanged()), this, SLOT(updateTranslations()));
99         connect(_settings_form, SIGNAL(trackColorChanged()), _player_form, SLOT(updateTrackColor()));
100         connect(_settings_form, SIGNAL(hwZoomPolicyChanged()), this, SLOT(_hw_zoom_policy_changed()));
101         connect(&_dbus_client, SIGNAL(displayStateChanged(bool)), this, SLOT(_set_display_state(bool)));
102         connect(_settings_form, SIGNAL(fmtxSettingsChanged()), this, SLOT(_fmtx_settings_changed()));
103         _player_form->reload(true);
104         QString mode = config.getValue("ui/orientation").toString();
105         if (mode == "landscape") {
106                 setAttribute(Qt::WA_Maemo5LandscapeOrientation);
107                 _player_form->landscapeMode();
108                 _library_form->landscapeMode();
109                 _equalizer_dialog->landscapeMode();
110                 _directory_form->lanscapeMode();
111                 _settings_form->landscapeMode();
112         } else if (mode == "portrait") {
113                 setAttribute(Qt::WA_Maemo5PortraitOrientation);
114                 _player_form->portraitMode();
115                 _library_form->portraitMode();
116                 _equalizer_dialog->portraitMode();
117                 _directory_form->portraitMode();
118                 _settings_form->portraitMode();
119         } else if (mode == "auto") { // initialization in landscape
120                 _player_form->landscapeMode();
121                 _library_form->landscapeMode();
122                 _equalizer_dialog->landscapeMode();
123                 _directory_form->lanscapeMode();
124                 _settings_form->landscapeMode();
125                 setAttribute(Qt::WA_Maemo5AutoOrientation);
126         }
127         _library_form->updateIcons();
128         _player_form->updateIcons();
129         _manage_library_form->updateIcons();
130         _directory_form->updateIcons();
131         _player_form->checkGradient();
132         _library_form->checkGradient();
133         _directory_form->checkGradient();
134         _hw_zoom_policy_changed();
135         config.setValue("fmtx/enabled", "no");
136         setWindowTitle("SomePlayer");
137
138         QList<QWidget *> widgets = ui->centralWidget->findChildren<QWidget *>();
139         foreach (QWidget *widget, widgets) {
140                 if (widget->objectName() != "searchLine") {
141                         widget->installEventFilter(this);
142                 }
143         }
144 }
145
146 MainWindow::~MainWindow()
147 {
148         delete _player_form;
149         delete _library_form;
150         delete ui;
151 }
152
153 void MainWindow::about() {
154         _about_form->show();
155 }
156
157 void MainWindow::player() {
158         _player_form->reload(true);
159         _orientation_changed(); // workaround
160 }
161
162 void MainWindow::library() {
163         ui->menuBar->setEnabled(true);
164         _library_form->show();
165         _orientation_changed(); // workaround
166         _manage_library_form->hide();
167 }
168
169 void MainWindow::_manage_library() {
170         _manage_library_form->refresh();
171         _manage_library_form->show();
172 }
173
174 void MainWindow::_save_playlist() {
175         QList<QString> playlists = _library->getPlaylistsNames();
176         playlists.removeOne(_CURRENT_PLAYLIST_SUBST_);
177         Playlist cur = _library->getCurrentPlaylist();
178         // construct playlist name if possible
179         QString suggest_name;
180         QList<Track> tracks = cur.tracks();
181         QString artist = tracks.at(0).metadata().artist(), album = tracks.at(0).metadata().album();
182         foreach (Track t, tracks) {
183                 if (t.metadata().album() != album)
184                         album = "";
185                 if (t.metadata().artist() != artist)
186                         artist = "";
187         }
188         if (album.isEmpty() && artist.isEmpty()) {
189                 suggest_name = "New playlist";
190         } else if (album.isEmpty()) {
191                 suggest_name = artist;
192         } else {
193                 suggest_name = QString("%1 - %2").arg(artist).arg(album);
194         }
195
196         // constructed
197         SavePlaylistDialog dialog(suggest_name, this);
198         dialog.setPlaylistNames(playlists);
199         if (dialog.exec() == QDialog::Accepted) {
200                 QString name = dialog.selectedName();
201                 bool append = false;
202                 if (playlists.contains(name)) {
203                         if (QMessageBox::question(this, "Overwrite playlist?", "Overwrite playlist \""+name+"\"?", QMessageBox::Ok, QMessageBox::Cancel)
204                                 != QMessageBox::Ok) {
205                                 return;
206                         }
207                         append = (QMessageBox::question(this, "Append to playlist?", "Playlist with name \""+name+"\" already exists.\n"
208                                                   "Dow you want to append current playlist to it?",
209                                                   QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok);
210                 }
211                 if (append) {
212                         Playlist target = _library->getPlaylist(name);
213                         QList<Track> tracks = cur.tracks();
214                         foreach (Track track, tracks) {
215                                 target.addTrack(track);
216                         }
217                         _library->savePlaylist(target);
218                 } else {
219                         cur.setName(name);
220                         _library->savePlaylist(cur);
221                 }
222         }
223 }
224
225 void MainWindow::_clear_current_playlist() {
226         CONFIRM_ACTION(this, tr("Clear playlist?"))
227         Playlist playlist = _library->getCurrentPlaylist();
228         playlist.clear();
229         _library->saveCurrentPlaylist(playlist);
230         _player_form->reload(true);
231 }
232
233 void MainWindow::_set_timer() {
234         TimerDialog dialog(this);
235         dialog.init();
236         if (_timer->isActive()) {
237                 dialog.showDisable();
238                 int msec = _timeout_interval;
239                 int h = msec/3600000;
240                 int m = msec/60000 - h * 60;
241                 int s = msec/1000 - h * 3600 - m * 60;
242                 dialog.setTime(h, m, s);
243         }
244         if (QDialog::Accepted == dialog.exec()) {
245                 if (!dialog.timerDisabled()) {
246                         int h, m, s;
247                         dialog.getTime(&h, &m, &s);
248                         _timeout_interval = h*3600000+m*60000+s*1000;
249                         _timer->setInterval(1000);
250                         _timer->setSingleShot(false);
251                         _timer->start();
252                 } else if (_timer->isActive()) {
253                         _timer->stop();
254                         _player_form->hideCountdown();
255                 }
256         }
257 }
258
259 void MainWindow::_timeout() {
260         _timeout_interval -= 1000;
261         if (_timeout_interval <= 0) {
262                 _player_form->stop();
263                 _player_form->hideCountdown();
264                 _timer->stop();
265         } else {
266                 int h = _timeout_interval / 3600000;
267                 int m = (_timeout_interval / 60000) - 60*h;
268                 int s = (_timeout_interval / 1000) - 3600*h - 60*m;
269                 QString hp = h < 10 ? QString("0%1").arg(h) : QString("%1").arg(h);
270                 QString mp = m < 10 ? QString("0%1").arg(m) : QString("%1").arg(m);
271                 QString sp = s < 10 ? QString("0%1").arg(s) : QString("%1").arg(s);
272                 _player_form->showCountdown(tr("Music off: ")+hp+":"+mp+":"+sp);
273         }
274 }
275
276 void MainWindow::_equalizer() {
277         if (_player_form->isEqualizerAvailable()) {
278                 double val = 0;
279                 for (int i = 0; i < 10; i++) {
280                         _player_form->equalizerValue(i, &val);
281                         _equalizer_dialog->setValue(i, (int)(val * 10 + 0.5));
282                 }
283                 _equalizer_dialog->setEqualizerEnabled(_player_form->isEqualizerEnabled());
284                 _equalizer_dialog->reloadPresets();
285                 _equalizer_dialog->exec();
286         } else {
287                 QMessageBox::information(this, "Error", "No equalizer support. Please install gstreamer0.10-plugins-good-extra");
288         }
289 }
290
291 void MainWindow::_equalizer_value_changed(int band, int val) {
292         _player_form->setEqualizerValue(band, (val / 10.0));
293 }
294
295 void MainWindow::settings() {
296         _settings_form->show();
297 }
298
299 void MainWindow::_change_orientation() {
300         Config config;
301         QString mode = config.getValue("ui/orientation").toString();
302         if (mode == "landscape") {
303                 setAttribute(Qt::WA_Maemo5LandscapeOrientation);
304         } else if (mode == "portrait") {
305                 setAttribute(Qt::WA_Maemo5PortraitOrientation);
306         } else if (mode == "auto") {
307                 setAttribute(Qt::WA_Maemo5AutoOrientation);
308         }
309 }
310
311 void MainWindow::_orientation_changed() {
312         QRect screenGeometry = QApplication::desktop()->screenGeometry();
313         if (screenGeometry.width() > screenGeometry.height()) {
314                 _orientation = ORIENTATION_LANDSCAPE;
315                 _player_form->landscapeMode();
316                 _library_form->landscapeMode();
317                 _equalizer_dialog->landscapeMode();
318                 _directory_form->lanscapeMode();
319                 _settings_form->landscapeMode();
320         } else {
321                 _orientation = ORIENTATION_PORTRAIT;
322                 _player_form->portraitMode();
323                 _library_form->portraitMode();
324                 _equalizer_dialog->portraitMode();
325                 _directory_form->portraitMode();
326                 _settings_form->portraitMode();
327         }
328 }
329
330 void MainWindow::_fullscreen(bool f) {
331         if (f) showFullScreen();
332         else showNormal();
333 }
334
335 void MainWindow::_add_tracks(QList<Track> tracks) {
336         Playlist cur = _library->getCurrentPlaylist();
337         foreach (Track track, tracks) {
338                 cur.addTrack(track);
339         }
340         _library->saveCurrentPlaylist(cur);
341         _player_form->reload(true);
342 }
343
344 void MainWindow::updateTranslations() {
345         Config config;
346         if (config.getValue("ui/language").toString() != "en") {
347                 _translator->load(QString("/opt/someplayer/someplayer_%1").arg(config.getValue("ui/language").toString()));
348                 QApplication::installTranslator(_translator);
349         } else {
350                 QApplication::removeTranslator(_translator);
351         }
352         ui->retranslateUi(this);
353         _player_form->updateTranslations();
354         _library_form->updateTranslations();
355         _equalizer_dialog->updateTranslations();
356         _manage_library_form->updateTranslations();
357         _directory_form->updateTranslations();
358         _settings_form->updateTranslations();
359         _about_form->updateTranslations();
360 }
361
362 void MainWindow::_hw_zoom_policy_changed() {
363         Config config;
364         QString state = config.getValue("hw/zoomkeys").toString();
365         if (state == "enabled") {
366                 _dbus_client.enableKeys();
367                 connect(&_dbus_client, SIGNAL(zoomKeyPressed(quint32)), this, SLOT(_zoom_key_pressed(quint32)));
368         } else {
369                 _dbus_client.disableKeys();
370                 disconnect(&_dbus_client, SIGNAL(zoomKeyPressed(quint32)), this, SLOT(_zoom_key_pressed(quint32)));
371         }
372 }
373
374 void MainWindow::_set_display_state(bool state) {
375         _display_unlocked = state;
376         if (!_display_unlocked) { // remember volume level when blocking screen
377                 _system_volume = _dbus_client.getVolume();
378         }
379 }
380
381 void MainWindow::_zoom_key_pressed(quint32 code) {
382         if (_display_unlocked) {
383                 return;
384         }
385         Config config;
386         QString behavior = config.getValue("hw/zoom_action").toString();
387         bool inverted = config.getValue("hw/zoom_inverted").toBool();
388         if (inverted) {
389                 if (code == MM_KEY_DOWN) {
390                         code = MM_KEY_UP;
391                 } else {
392                         code = MM_KEY_DOWN;
393                 }
394         }
395         if (code == MM_KEY_DOWN) {
396                 if (behavior == "track") {
397                         if (_orientation == ORIENTATION_LANDSCAPE) {
398                                 _player_form->next();
399                         } else {
400                                 _player_form->prev();
401                         }
402                         _dbus_client.setVolume(_system_volume);
403                 }
404         } else if (code == MM_KEY_UP) {
405                 if (behavior == "track") {
406                         if (_orientation == ORIENTATION_LANDSCAPE) {
407                                 _player_form->prev();
408                         } else {
409                                 _player_form->next();
410                         }
411                         _dbus_client.setVolume(_system_volume);
412                 }
413         }
414 }
415
416 void MainWindow::_fmtx_settings_changed() {
417         Config config;
418         if (config.getValue("fmtx/enabled").toString() == "yes") {
419                 QString station_name = config.getValue("fmtx/station_name").toString();
420                 int frequency = config.getValue("fmtx/frequency").toInt();
421                 system(QString("fmtx_client -p1 -f%1 -s\"%2\" -t\"%3\" 2>&1 >/dev/null")
422                        .arg(frequency)
423                        .arg(station_name)
424                        .arg(_player_form->playerCaption()).toAscii());
425         } else {
426                 system("fmtx_client -p 0 2>&1 >/dev/null");
427         }
428 }
429
430 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
431         if (event->type() != QEvent::KeyPress) {
432                 return QObject::eventFilter(obj, event);
433         }
434         QKeyEvent *keyEvent = (QKeyEvent *) event;
435         int mod = keyEvent->modifiers();
436
437         switch (keyEvent->key()) {
438         case Qt::Key_Space:
439                 if ((mod & Qt::ControlModifier) == 0) {
440                         _player_form->toggle();
441                 }
442                 break;
443         case Qt::Key_Enter:
444                 // TODO
445                 break;
446         case Qt::Key_Right:
447                 _player_form->next();
448                 break;
449         case Qt::Key_Left:
450                 _player_form->prev();
451                 break;
452         case Qt::Key_T:
453                 _player_form->stop();
454                 break;
455         case Qt::Key_R:
456                 _player_form->toggleRandom();
457                 break;
458         case Qt::Key_V:
459                 _player_form->toggleView();
460                 break;
461         case Qt::Key_M:
462                 _directory_form->show();
463                 break;
464         case Qt::Key_L:
465                 _library_form->show();
466                 break;
467         case Qt::Key_E:
468                 _player_form->toggleRepeat();
469                 break;
470         case Qt::Key_S:
471                 _player_form->toggleToolsWidget();
472                 break;
473         case Qt::Key_H:
474                 _about_form->onlineHelp();
475                 break;
476         default:
477                 return QObject::eventFilter(obj, event);
478         }
479
480         return true;
481 }
482
483
484 void MainWindow::_import_playlits() {
485         QString directory = QFileDialog::getExistingDirectory (this, tr("Select directory"), "", QFileDialog::ShowDirsOnly );
486         _library->scanAndImportPlaylists(directory);
487 }
488
489 void MainWindow::_clear_current() {
490         Playlist pl;
491         _library->saveCurrentPlaylist(pl);
492 }