Player factory
[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(_player_form, SIGNAL(clearPlaylist()), this, SLOT(_clear_current_playlist()));
70         connect(ui->actionSetTimer, SIGNAL(triggered()), this, SLOT(_set_timer()));
71         connect(ui->actionEqualizer, SIGNAL(triggered()), this, SLOT(_equalizer()));
72         connect(ui->actionOnlineHelp, SIGNAL(triggered()), _about_form, SLOT(onlineHelp()));
73         connect(_library, SIGNAL(done()), _library_form, SLOT(refresh()));
74         connect(_player_form, SIGNAL(refreshLibrary()), _library_form, SLOT(refresh()));
75         connect(_manage_library_form, SIGNAL(refreshLibrary()), _library_form, SLOT(refresh()));
76         connect(_timer, SIGNAL(timeout()), this, SLOT(_timeout()));
77         connect(_equalizer_dialog, SIGNAL(valueChanged(int,int)), this, SLOT(_equalizer_value_changed(int, int)));
78         connect(_equalizer_dialog, SIGNAL(equalizerEnabled()), _player_form, SLOT(enableEqualizer()));
79         connect(_equalizer_dialog, SIGNAL(equalizerDisabled()), _player_form, SLOT(disableEqualizer()));
80         connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(_orientation_changed()));
81         connect(_player_form, SIGNAL(fullscreen(bool)), this, SLOT(_fullscreen(bool)));
82         connect(_library_form, SIGNAL(addAndPlay(Track)), _player_form, SLOT(play(Track)));
83         connect(_directory_form, SIGNAL(addAndPlay(Track)), _player_form, SLOT(play(Track)));
84         connect(_player_form, SIGNAL(dirView()), _directory_form, SLOT(show()));
85         connect(_directory_form, SIGNAL(addTracks(QList<Track>)), this, SLOT(_add_tracks(QList<Track>)));
86         connect(_settings_form, SIGNAL(iconsChanged()), _player_form, SLOT(updateIcons()));
87         connect(_settings_form, SIGNAL(iconsChanged()), _library_form, SLOT(updateIcons()));
88         connect(_settings_form, SIGNAL(iconsChanged()), _manage_library_form, SLOT(updateIcons()));
89         connect(_settings_form, SIGNAL(iconsChanged()), _directory_form, SLOT(updateIcons()));
90         connect(_settings_form, SIGNAL(iconsChanged()), _about_form, SLOT(updateIcons()));
91         connect(_settings_form, SIGNAL(gradientChanged()), _player_form, SLOT(checkGradient()));
92         connect(_settings_form, SIGNAL(gradientChanged()), _library_form, SLOT(checkGradient()));
93         connect(_settings_form, SIGNAL(gradientChanged()), _directory_form, SLOT(checkGradient()));
94         connect(_settings_form, SIGNAL(libraryOptionsChanged()), _library_form, SLOT(refresh()));
95         connect(_settings_form, SIGNAL(orientationChanged()), this, SLOT(_change_orientation()));
96         connect(_settings_form, SIGNAL(translationChanged()), this, SLOT(updateTranslations()));
97         connect(_settings_form, SIGNAL(trackColorChanged()), _player_form, SLOT(updateTrackColor()));
98         connect(_settings_form, SIGNAL(hwZoomPolicyChanged()), this, SLOT(_hw_zoom_policy_changed()));
99         connect(&_dbus_client, SIGNAL(displayStateChanged(bool)), this, SLOT(_set_display_state(bool)));
100         connect(_settings_form, SIGNAL(fmtxSettingsChanged()), this, SLOT(_fmtx_settings_changed()));
101         _player_form->reload(true);
102         QString mode = config.getValue("ui/orientation").toString();
103         if (mode == "landscape") {
104                 setAttribute(Qt::WA_Maemo5LandscapeOrientation);
105                 _player_form->landscapeMode();
106                 _library_form->landscapeMode();
107                 _equalizer_dialog->landscapeMode();
108                 _directory_form->lanscapeMode();
109                 _settings_form->landscapeMode();
110         } else if (mode == "portrait") {
111                 setAttribute(Qt::WA_Maemo5PortraitOrientation);
112                 _player_form->portraitMode();
113                 _library_form->portraitMode();
114                 _equalizer_dialog->portraitMode();
115                 _directory_form->portraitMode();
116                 _settings_form->portraitMode();
117         } else if (mode == "auto") { // initialization in landscape
118                 _player_form->landscapeMode();
119                 _library_form->landscapeMode();
120                 _equalizer_dialog->landscapeMode();
121                 _directory_form->lanscapeMode();
122                 _settings_form->landscapeMode();
123                 setAttribute(Qt::WA_Maemo5AutoOrientation);
124         }
125         _library_form->updateIcons();
126         _player_form->updateIcons();
127         _manage_library_form->updateIcons();
128         _directory_form->updateIcons();
129         _player_form->checkGradient();
130         _library_form->checkGradient();
131         _directory_form->checkGradient();
132         _hw_zoom_policy_changed();
133         config.setValue("fmtx/enabled", "no");
134         setWindowTitle("SomePlayer");
135
136         QList<QWidget *> widgets = ui->centralWidget->findChildren<QWidget *>();
137         foreach (QWidget *widget, widgets) {
138                 if (widget->objectName() != "searchLine") {
139                         widget->installEventFilter(this);
140                 }
141         }
142 }
143
144 MainWindow::~MainWindow()
145 {
146         delete _player_form;
147         delete _library_form;
148         delete ui;
149 }
150
151 void MainWindow::about() {
152         _about_form->show();
153 }
154
155 void MainWindow::player() {
156         _player_form->reload(true);
157         _orientation_changed(); // workaround
158 }
159
160 void MainWindow::library() {
161         ui->menuBar->setEnabled(true);
162         _library_form->show();
163         _orientation_changed(); // workaround
164         _manage_library_form->hide();
165 }
166
167 void MainWindow::_manage_library() {
168         _manage_library_form->refresh();
169         _manage_library_form->show();
170 }
171
172 void MainWindow::_save_playlist() {
173         QList<QString> playlists = _library->getPlaylistsNames();
174         playlists.removeOne(_CURRENT_PLAYLIST_SUBST_);
175         Playlist cur = _library->getCurrentPlaylist();
176         // construct playlist name if possible
177         QString suggest_name;
178         QList<Track> tracks = cur.tracks();
179         QString artist = tracks.at(0).metadata().artist(), album = tracks.at(0).metadata().album();
180         foreach (Track t, tracks) {
181                 if (t.metadata().album() != album)
182                         album = "";
183                 if (t.metadata().artist() != artist)
184                         artist = "";
185         }
186         if (album.isEmpty() && artist.isEmpty()) {
187                 suggest_name = "New playlist";
188         } else if (album.isEmpty()) {
189                 suggest_name = artist;
190         } else {
191                 suggest_name = QString("%1 - %2").arg(artist).arg(album);
192         }
193
194         // constructed
195         SavePlaylistDialog dialog(suggest_name, this);
196         dialog.setPlaylistNames(playlists);
197         if (dialog.exec() == QDialog::Accepted) {
198                 QString name = dialog.selectedName();
199                 bool append = false;
200                 if (playlists.contains(name)) {
201                         if (QMessageBox::question(this, "Overwrite playlist?", "Overwrite playlist \""+name+"\"?", QMessageBox::Ok, QMessageBox::Cancel)
202                                 != QMessageBox::Ok) {
203                                 return;
204                         }
205                         append = (QMessageBox::question(this, "Append to playlist?", "Playlist with name \""+name+"\" already exists.\n"
206                                                   "Dow you want to append current playlist to it?",
207                                                   QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok);
208                 }
209                 if (append) {
210                         Playlist target = _library->getPlaylist(name);
211                         QList<Track> tracks = cur.tracks();
212                         foreach (Track track, tracks) {
213                                 target.addTrack(track);
214                         }
215                         _library->savePlaylist(target);
216                 } else {
217                         cur.setName(name);
218                         _library->savePlaylist(cur);
219                 }
220         }
221 }
222
223 void MainWindow::_clear_current_playlist() {
224         CONFIRM_ACTION(this, tr("Clear playlist?"))
225         Playlist playlist = _library->getCurrentPlaylist();
226         playlist.clear();
227         _library->saveCurrentPlaylist(playlist);
228         _player_form->reload(true);
229 }
230
231 void MainWindow::_set_timer() {
232         TimerDialog dialog(this);
233         dialog.init();
234         if (_timer->isActive()) {
235                 dialog.showDisable();
236                 int msec = _timeout_interval;
237                 int h = msec/3600000;
238                 int m = msec/60000 - h * 60;
239                 int s = msec/1000 - h * 3600 - m * 60;
240                 dialog.setTime(h, m, s);
241         }
242         if (QDialog::Accepted == dialog.exec()) {
243                 if (!dialog.timerDisabled()) {
244                         int h, m, s;
245                         dialog.getTime(&h, &m, &s);
246                         _timeout_interval = h*3600000+m*60000+s*1000;
247                         _timer->setInterval(1000);
248                         _timer->setSingleShot(false);
249                         _timer->start();
250                 } else if (_timer->isActive()) {
251                         _timer->stop();
252                         _player_form->hideCountdown();
253                 }
254         }
255 }
256
257 void MainWindow::_timeout() {
258         _timeout_interval -= 1000;
259         if (_timeout_interval <= 0) {
260                 _player_form->stop();
261                 _player_form->hideCountdown();
262                 _timer->stop();
263         } else {
264                 int h = _timeout_interval / 3600000;
265                 int m = (_timeout_interval / 60000) - 60*h;
266                 int s = (_timeout_interval / 1000) - 3600*h - 60*m;
267                 QString hp = h < 10 ? QString("0%1").arg(h) : QString("%1").arg(h);
268                 QString mp = m < 10 ? QString("0%1").arg(m) : QString("%1").arg(m);
269                 QString sp = s < 10 ? QString("0%1").arg(s) : QString("%1").arg(s);
270                 _player_form->showCountdown(tr("Music off: ")+hp+":"+mp+":"+sp);
271         }
272 }
273
274 void MainWindow::_equalizer() {
275         if (_player_form->isEqualizerAvailable()) {
276                 double val = 0;
277                 for (int i = 0; i < 10; i++) {
278                         _player_form->equalizerValue(i, &val);
279                         _equalizer_dialog->setValue(i, (int)(val * 10 + 0.5));
280                 }
281                 _equalizer_dialog->setEqualizerEnabled(_player_form->isEqualizerEnabled());
282                 _equalizer_dialog->reloadPresets();
283                 _equalizer_dialog->exec();
284         } else {
285                 QMessageBox::information(this, "Error", "No equalizer support. Please install gstreamer0.10-plugins-good-extra");
286         }
287 }
288
289 void MainWindow::_equalizer_value_changed(int band, int val) {
290         _player_form->setEqualizerValue(band, (val / 10.0));
291 }
292
293 void MainWindow::settings() {
294         _settings_form->show();
295 }
296
297 void MainWindow::_change_orientation() {
298         Config config;
299         QString mode = config.getValue("ui/orientation").toString();
300         if (mode == "landscape") {
301                 setAttribute(Qt::WA_Maemo5LandscapeOrientation);
302         } else if (mode == "portrait") {
303                 setAttribute(Qt::WA_Maemo5PortraitOrientation);
304         } else if (mode == "auto") {
305                 setAttribute(Qt::WA_Maemo5AutoOrientation);
306         }
307 }
308
309 void MainWindow::_orientation_changed() {
310         QRect screenGeometry = QApplication::desktop()->screenGeometry();
311         if (screenGeometry.width() > screenGeometry.height()) {
312                 _orientation = ORIENTATION_LANDSCAPE;
313                 _player_form->landscapeMode();
314                 _library_form->landscapeMode();
315                 _equalizer_dialog->landscapeMode();
316                 _directory_form->lanscapeMode();
317                 _settings_form->landscapeMode();
318         } else {
319                 _orientation = ORIENTATION_PORTRAIT;
320                 _player_form->portraitMode();
321                 _library_form->portraitMode();
322                 _equalizer_dialog->portraitMode();
323                 _directory_form->portraitMode();
324                 _settings_form->portraitMode();
325         }
326 }
327
328 void MainWindow::_fullscreen(bool f) {
329         if (f) showFullScreen();
330         else showNormal();
331 }
332
333 void MainWindow::_add_tracks(QList<Track> tracks) {
334         Playlist cur = _library->getCurrentPlaylist();
335         foreach (Track track, tracks) {
336                 cur.addTrack(track);
337         }
338         _library->saveCurrentPlaylist(cur);
339         _player_form->reload(true);
340 }
341
342 void MainWindow::updateTranslations() {
343         Config config;
344         if (config.getValue("ui/language").toString() != "en") {
345                 _translator->load(QString("/opt/someplayer/someplayer_%1").arg(config.getValue("ui/language").toString()));
346                 QApplication::installTranslator(_translator);
347         } else {
348                 QApplication::removeTranslator(_translator);
349         }
350         ui->retranslateUi(this);
351         _player_form->updateTranslations();
352         _library_form->updateTranslations();
353         _equalizer_dialog->updateTranslations();
354         _manage_library_form->updateTranslations();
355         _directory_form->updateTranslations();
356         _settings_form->updateTranslations();
357         _about_form->updateTranslations();
358 }
359
360 void MainWindow::_hw_zoom_policy_changed() {
361         Config config;
362         QString state = config.getValue("hw/zoomkeys").toString();
363         if (state == "enabled") {
364                 _dbus_client.enableKeys();
365                 connect(&_dbus_client, SIGNAL(zoomKeyPressed(quint32)), this, SLOT(_zoom_key_pressed(quint32)));
366         } else {
367                 _dbus_client.disableKeys();
368                 disconnect(&_dbus_client, SIGNAL(zoomKeyPressed(quint32)), this, SLOT(_zoom_key_pressed(quint32)));
369         }
370 }
371
372 void MainWindow::_set_display_state(bool state) {
373         _display_unlocked = state;
374         if (!_display_unlocked) { // remember volume level when blocking screen
375                 _system_volume = _dbus_client.getVolume();
376         }
377 }
378
379 void MainWindow::_zoom_key_pressed(quint32 code) {
380         if (_display_unlocked) {
381                 return;
382         }
383         Config config;
384         QString behavior = config.getValue("hw/zoom_action").toString();
385         bool inverted = config.getValue("hw/zoom_inverted").toBool();
386         if (inverted) {
387                 if (code == MM_KEY_DOWN) {
388                         code = MM_KEY_UP;
389                 } else {
390                         code = MM_KEY_DOWN;
391                 }
392         }
393         if (code == MM_KEY_DOWN) {
394                 if (behavior == "track") {
395                         if (_orientation == ORIENTATION_LANDSCAPE) {
396                                 _player_form->next();
397                         } else {
398                                 _player_form->prev();
399                         }
400                         _dbus_client.setVolume(_system_volume);
401                 }
402         } else if (code == MM_KEY_UP) {
403                 if (behavior == "track") {
404                         if (_orientation == ORIENTATION_LANDSCAPE) {
405                                 _player_form->prev();
406                         } else {
407                                 _player_form->next();
408                         }
409                         _dbus_client.setVolume(_system_volume);
410                 }
411         }
412 }
413
414 void MainWindow::_fmtx_settings_changed() {
415         Config config;
416         if (config.getValue("fmtx/enabled").toString() == "yes") {
417                 QString station_name = config.getValue("fmtx/station_name").toString();
418                 int frequency = config.getValue("fmtx/frequency").toInt();
419                 system(QString("fmtx_client -p1 -f%1 -s\"%2\" -t\"%3\" 2>&1 >/dev/null")
420                        .arg(frequency)
421                        .arg(station_name)
422                        .arg(_player_form->playerCaption()).toAscii());
423         } else {
424                 system("fmtx_client -p 0 2>&1 >/dev/null");
425         }
426 }
427
428 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
429         if (event->type() != QEvent::KeyPress) {
430                 return QObject::eventFilter(obj, event);
431         }
432         QKeyEvent *keyEvent = (QKeyEvent *) event;
433         int mod = keyEvent->modifiers();
434
435         switch (keyEvent->key()) {
436         case Qt::Key_Space:
437                 if ((mod & Qt::ControlModifier) == 0) {
438                         _player_form->toggle();
439                 }
440                 break;
441         case Qt::Key_Enter:
442                 // TODO
443                 break;
444         case Qt::Key_Right:
445                 _player_form->next();
446                 break;
447         case Qt::Key_Left:
448                 _player_form->prev();
449                 break;
450         case Qt::Key_T:
451                 _player_form->stop();
452                 break;
453         case Qt::Key_R:
454                 _player_form->toggleRandom();
455                 break;
456         case Qt::Key_V:
457                 _player_form->toggleView();
458                 break;
459         case Qt::Key_M:
460                 _directory_form->show();
461                 break;
462         case Qt::Key_L:
463                 _library_form->show();
464                 break;
465         case Qt::Key_E:
466                 _player_form->toggleRepeat();
467                 break;
468         case Qt::Key_S:
469                 _player_form->toggleToolsWidget();
470                 break;
471         case Qt::Key_H:
472                 _about_form->onlineHelp();
473                 break;
474         default:
475                 return QObject::eventFilter(obj, event);
476         }
477
478         return true;
479 }