FMTX support
[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 "player/player.h"
31
32 #include "library.h"
33 #include "timerdialog.h"
34 #include "equalizerdialog.h"
35 #include "saveplaylistdialog.h"
36 #include "settingsform.h"
37 #include <QtGui/QX11Info>
38 #include <X11/Xlib.h>
39 #include <X11/Xatom.h>
40
41
42 using namespace SomePlayer::DataObjects;
43 using namespace SomePlayer::Storage;
44 using namespace SomePlayer::Playback;
45
46 MainWindow::MainWindow(QWidget *parent) :
47         QMainWindow(parent),
48         ui(new Ui::MainWindow)
49 {
50         _display_unlocked = true; // in most cases
51         Config config;
52         _library = new Library(config.applicationDir(), config.applicationDir());
53         _translator = new QTranslator(this);
54         ui->setupUi(this);
55         connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about()));
56         connect(ui->actionSettings, SIGNAL(triggered()), this, SLOT(settings()));
57         setAnimated(true);
58         setAttribute(Qt::WA_Maemo5StackedWindow);
59         _player_form = new PlayerForm(_library, this);
60         ui->centralWidget->layout()->addWidget(_player_form);
61         _library_form = new LibraryForm(_library, this);
62         _directory_form = new DirectoryView(this);
63         _directory_form->hide();
64         _timer = new QTimer(this);
65         _equalizer_dialog = new EqualizerDialog(this);
66         _manage_library_form = new ManageLibraryForm(_library, this);
67         _settings_form = new SettingsForm(this);
68         _settings_form->hide();
69         connect(_player_form, SIGNAL(library()), this, SLOT(library()));
70         connect(_library_form, SIGNAL(refreshPlayer()), this, SLOT(player()));
71         connect(ui->actionManageLibrary, SIGNAL(triggered()), this, SLOT(_manage_library()));
72         connect(ui->actionSavePlaylist, SIGNAL(triggered()), this, SLOT(_save_playlist()));
73         connect(_player_form, SIGNAL(clearPlaylist()), this, SLOT(_clear_current_playlist()));
74         connect(ui->actionSetTimer, SIGNAL(triggered()), this, SLOT(_set_timer()));
75         connect(ui->actionEqualizer, SIGNAL(triggered()), this, SLOT(_equalizer()));
76         connect(_library, SIGNAL(done()), _library_form, SLOT(refresh()));
77         connect(_player_form, SIGNAL(refreshLibrary()), _library_form, SLOT(refresh()));
78         connect(_manage_library_form, SIGNAL(refreshLibrary()), _library_form, SLOT(refresh()));
79         connect(_timer, SIGNAL(timeout()), this, SLOT(_timeout()));
80         connect(_equalizer_dialog, SIGNAL(valueChanged(int,int)), this, SLOT(_equalizer_value_changed(int, int)));
81         connect(_equalizer_dialog, SIGNAL(equalizerEnabled()), _player_form, SLOT(enableEqualizer()));
82         connect(_equalizer_dialog, SIGNAL(equalizerDisabled()), _player_form, SLOT(disableEqualizer()));
83         connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(_orientation_changed()));
84         connect(_player_form, SIGNAL(fullscreen(bool)), this, SLOT(_fullscreen(bool)));
85         connect(_library_form, SIGNAL(addAndPlay(Track)), _player_form, SLOT(play(Track)));
86         connect(_directory_form, SIGNAL(addAndPlay(Track)), _player_form, SLOT(play(Track)));
87         connect(_player_form, SIGNAL(dirView()), _directory_form, SLOT(show()));
88         connect(_directory_form, SIGNAL(addTracks(QList<Track>)), this, SLOT(_add_tracks(QList<Track>)));
89         connect(_settings_form, SIGNAL(iconsChanged()), _player_form, SLOT(updateIcons()));
90         connect(_settings_form, SIGNAL(iconsChanged()), _library_form, SLOT(updateIcons()));
91         connect(_settings_form, SIGNAL(iconsChanged()), _manage_library_form, SLOT(updateIcons()));
92         connect(_settings_form, SIGNAL(iconsChanged()), _directory_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         connect(_player_form, SIGNAL(trackChanged()), this, SLOT(_update_fmtx_text()));
104         _player_form->reload(true);
105         QString mode = config.getValue("ui/orientation").toString();
106         if (mode == "landscape") {
107                 setAttribute(Qt::WA_Maemo5LandscapeOrientation);
108                 _player_form->landscapeMode();
109                 _library_form->landscapeMode();
110                 _equalizer_dialog->landscapeMode();
111                 _directory_form->lanscapeMode();
112                 _settings_form->landscapeMode();
113         } else if (mode == "portrait") {
114                 setAttribute(Qt::WA_Maemo5PortraitOrientation);
115                 _player_form->portraitMode();
116                 _library_form->portraitMode();
117                 _equalizer_dialog->portraitMode();
118                 _directory_form->portraitMode();
119                 _settings_form->portraitMode();
120         } else if (mode == "auto") { // initialization in landscape
121                 _player_form->landscapeMode();
122                 _library_form->landscapeMode();
123                 _equalizer_dialog->landscapeMode();
124                 _directory_form->lanscapeMode();
125                 _settings_form->landscapeMode();
126                 setAttribute(Qt::WA_Maemo5AutoOrientation);
127         }
128         _library_form->updateIcons();
129         _player_form->updateIcons();
130         _manage_library_form->updateIcons();
131         _directory_form->updateIcons();
132         _player_form->checkGradient();
133         _library_form->checkGradient();
134         _directory_form->checkGradient();
135         _hw_zoom_policy_changed();
136         setWindowTitle("SomePlayer");
137 }
138
139 MainWindow::~MainWindow()
140 {
141         delete _player_form;
142         delete _library_form;
143         delete ui;
144 }
145
146 void MainWindow::about() {
147         QMessageBox::about(this, QString("About SomePlayer v")+_SOMEPLAYER_VERSION_, "Alternate music player for Maemo 5 "
148                                            "written in C++ with Qt4\n\n"
149                                            "Author: Nikolay Tischenko aka \"somebody\" <niktischenko@gmail.com>");
150 }
151
152 void MainWindow::player() {
153         _player_form->reload(true);
154         _orientation_changed(); // workaround
155 }
156
157 void MainWindow::library() {
158         ui->menuBar->setEnabled(true);
159         _library_form->show();
160         _orientation_changed(); // workaround
161         _manage_library_form->hide();
162 }
163
164 void MainWindow::_manage_library() {
165         _manage_library_form->refresh();
166         _manage_library_form->show();
167 }
168
169 void MainWindow::_save_playlist() {
170         QList<QString> playlists = _library->getPlaylistsNames();
171         playlists.removeOne(_CURRENT_PLAYLIST_SUBST_);
172         Playlist cur = _library->getCurrentPlaylist();
173         // construct playlist name if possible
174         QString suggest_name;
175         QList<Track> tracks = cur.tracks();
176         QString artist = tracks.at(0).metadata().artist(), album = tracks.at(0).metadata().album();
177         foreach (Track t, tracks) {
178                 if (t.metadata().album() != album)
179                         album = "";
180                 if (t.metadata().artist() != artist)
181                         artist = "";
182         }
183         if (album.isEmpty() && artist.isEmpty()) {
184                 suggest_name = "New playlist";
185         } else if (album.isEmpty()) {
186                 suggest_name = artist;
187         } else {
188                 suggest_name = QString("%1 - %2").arg(artist).arg(album);
189         }
190
191         // constructed
192         SavePlaylistDialog dialog(suggest_name, this);
193         dialog.setPlaylistNames(playlists);
194         if (dialog.exec() == QDialog::Accepted) {
195                 QString name = dialog.selectedName();
196                 bool append = false;
197                 if (playlists.contains(name)) {
198                         if (QMessageBox::question(this, "Append to playlist?", "Playlist with name \""+name+"\" already exists.\n"
199                                                   "Dow you want to append current playlist to it?",
200                                                   QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok) {
201                                 append = true;
202                         } else {
203                                 append = false;
204                         }
205                 }
206                 if (append) {
207                         Playlist target = _library->getPlaylist(name);
208                         QList<Track> tracks = cur.tracks();
209                         foreach (Track track, tracks) {
210                                 target.addTrack(track);
211                         }
212                         _library->savePlaylist(target);
213                 } else {
214                         cur.setName(name);
215                         _library->savePlaylist(cur);
216                 }
217         }
218 }
219
220 void MainWindow::_clear_current_playlist() {
221         Playlist playlist = _library->getCurrentPlaylist();
222         playlist.clear();
223         _library->saveCurrentPlaylist(playlist);
224         _player_form->reload(true);
225 }
226
227 void MainWindow::_set_timer() {
228         TimerDialog dialog(this);
229         dialog.init();
230         if (_timer->isActive()) {
231                 dialog.showDisable();
232                 int msec = _timeout_interval;
233                 int h = msec/3600000;
234                 int m = msec/60000 - h * 60;
235                 int s = msec/1000 - h * 3600 - m * 60;
236                 dialog.setTime(h, m, s);
237         }
238         if (QDialog::Accepted == dialog.exec()) {
239                 if (!dialog.timerDisabled()) {
240                         int h, m, s;
241                         dialog.getTime(&h, &m, &s);
242                         _timeout_interval = h*3600000+m*60000+s*1000;
243                         _timer->setInterval(1000);
244                         _timer->setSingleShot(false);
245                         _timer->start();
246                 } else if (_timer->isActive()) {
247                         _timer->stop();
248                         _player_form->hideCountdown();
249                 }
250         }
251 }
252
253 void MainWindow::_timeout() {
254         _timeout_interval -= 1000;
255         if (_timeout_interval <= 0) {
256                 _player_form->stop();
257                 _player_form->hideCountdown();
258                 _timer->stop();
259         } else {
260                 int h = _timeout_interval / 3600000;
261                 int m = (_timeout_interval / 60000) - 60*h;
262                 int s = (_timeout_interval / 1000) - 3600*h - 60*m;
263                 QString hp = h < 10 ? QString("0%1").arg(h) : QString("%1").arg(h);
264                 QString mp = m < 10 ? QString("0%1").arg(m) : QString("%1").arg(m);
265                 QString sp = s < 10 ? QString("0%1").arg(s) : QString("%1").arg(s);
266                 _player_form->showCountdown(tr("Music off: ")+hp+":"+mp+":"+sp);
267         }
268 }
269
270 void MainWindow::_equalizer() {
271         if (_player_form->isEqualizerAvailable()) {
272                 double val = 0;
273                 for (int i = 0; i < 10; i++) {
274                         _player_form->equalizerValue(i, &val);
275                         _equalizer_dialog->setValue(i, (int)(val * 10 + 0.5));
276                 }
277                 _equalizer_dialog->setEqualizerEnabled(_player_form->isEqualizerEnabled());
278                 _equalizer_dialog->reloadPresets();
279                 _equalizer_dialog->exec();
280         } else {
281                 QMessageBox::information(this, "Error", "No equalizer support. Please install gstreamer0.10-plugins-good-extra");
282         }
283 }
284
285 void MainWindow::_equalizer_value_changed(int band, int val) {
286         _player_form->setEqualizerValue(band, (val / 10.0));
287 }
288
289 void MainWindow::settings() {
290         _settings_form->show();
291 }
292
293 void MainWindow::_change_orientation() {
294         Config config;
295         QString mode = config.getValue("ui/orientation").toString();
296         if (mode == "landscape") {
297                 setAttribute(Qt::WA_Maemo5LandscapeOrientation);
298         } else if (mode == "portrait") {
299                 setAttribute(Qt::WA_Maemo5PortraitOrientation);
300         } else if (mode == "auto") {
301                 setAttribute(Qt::WA_Maemo5AutoOrientation);
302         }
303 }
304
305 void MainWindow::_orientation_changed() {
306         QRect screenGeometry = QApplication::desktop()->screenGeometry();
307         if (screenGeometry.width() > screenGeometry.height()) {
308                 _player_form->landscapeMode();
309                 _library_form->landscapeMode();
310                 _equalizer_dialog->landscapeMode();
311                 _directory_form->lanscapeMode();
312                 _settings_form->landscapeMode();
313         } else {
314                 _player_form->portraitMode();
315                 _library_form->portraitMode();
316                 _equalizer_dialog->portraitMode();
317                 _directory_form->portraitMode();
318                 _settings_form->portraitMode();
319         }
320 }
321
322 void MainWindow::_fullscreen(bool f) {
323         if (f) showFullScreen();
324         else showNormal();
325 }
326
327 void MainWindow::_add_tracks(QList<Track> tracks) {
328         Playlist cur = _library->getCurrentPlaylist();
329         foreach (Track track, tracks) {
330                 cur.addTrack(track);
331         }
332         _library->saveCurrentPlaylist(cur);
333         _player_form->reload(true);
334 }
335
336 void MainWindow::updateTranslations() {
337         Config config;
338         if (config.getValue("ui/language").toString() != "en") {
339                 _translator->load(QString("/opt/someplayer/someplayer_%1").arg(config.getValue("ui/language").toString()));
340                 QApplication::installTranslator(_translator);
341         } else {
342                 QApplication::removeTranslator(_translator);
343         }
344         ui->retranslateUi(this);
345         _player_form->updateTranslations();
346         _library_form->updateTranslations();
347         _equalizer_dialog->updateTranslations();
348         _manage_library_form->updateTranslations();
349         _directory_form->updateTranslations();
350         _settings_form->updateTranslations();
351 }
352
353 void MainWindow::_hw_zoom_policy_changed() {
354         Config config;
355         QString state = config.getValue("hw/zoomkeys").toString();
356         if (state == "enabled") {
357                 _dbus_client.enableKeys();
358                 connect(&_dbus_client, SIGNAL(zoomKeyPressed(quint32)), this, SLOT(_zoom_key_pressed(quint32)));
359         } else {
360                 _dbus_client.disableKeys();
361                 disconnect(&_dbus_client, SIGNAL(zoomKeyPressed(quint32)), this, SLOT(_zoom_key_pressed(quint32)));
362         }
363 }
364
365 void MainWindow::_set_display_state(bool state) {
366         _display_unlocked = state;
367         if (!_display_unlocked) { // remember volume level when blocking screen
368                 _system_volume = _dbus_client.getVolume();
369         }
370 }
371
372 void MainWindow::_zoom_key_pressed(quint32 code) {
373         if (_display_unlocked) {
374                 return;
375         }
376         Config config;
377         QString behavior = config.getValue("hw/zoom_action").toString();
378         if (code == MM_KEY_DOWN) {
379                 if (behavior == "track") {
380                         _player_form->prev();
381                         _dbus_client.setVolume(_system_volume);
382                 }
383         } else if (code == MM_KEY_UP) {
384                 if (behavior == "track") {
385                         _player_form->next();
386                         _dbus_client.setVolume(_system_volume);
387                 }
388         }
389 }
390
391 void MainWindow::_fmtx_settings_changed() {
392         Config config;
393         if (config.getValue("fmtx/enabled").toString() == "yes") {
394                 QString station_name = config.getValue("fmtx/station_name").toString();
395                 int frequency = config.getValue("fmtx/frequency").toInt();
396                 system(QString("fmtx_client -p1 -f%1 -s\"%2\" -t\"%3\" 2>&1 >/dev/null")
397                        .arg(frequency)
398                        .arg(station_name)
399                        .arg(_player_form->playerCaption()).toAscii());
400         } else {
401                 system("fmtx_client -p 0 2>&1 >/dev/null");
402         }
403 }
404
405 void MainWindow::_update_fmtx_text() {
406         Config config;
407         if (config.getValue("fmtx/enabled").toString() == "yes") {
408                 system(QString("fmtx_client -t \"%1\" 2>&1 >/dev/null").arg(_player_form->playerCaption()).toAscii());
409         }
410 }