Album art fix for [#6370] Album art fails to load on first track
[vlc-remote] / src / playermainwindow.cpp
1   /*   VLC-REMOTE for MAEMO 5
2   *   Copyright (C) 2010 Schutz Sacha <istdasklar@gmail.com>, Dru Moore <usr@dru-id.co.uk>, Yann Nave <yannux@onbebop.net>
3   *   This program is free software; you can redistribute it and/or modify
4   *   it under the terms of the GNU General Public License version 2,
5   *   or (at your option) any later version, as published by the Free
6   *   Software Foundation
7   *
8   *   This program is distributed in the hope that it will be useful,
9   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   *   GNU General Public License for more details
12   *
13   *   You should have received a copy of the GNU General Public
14   *   License along with this program; if not, write to the
15   *   Free Software Foundation, Inc.,
16   *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17   */
18   #include <QDebug>
19   #include <QTime>
20   #include <QtGui>
21   #include "playermainwindow.h"
22   #include "ui_playermainwindow.h"
23   #include "configdialog.h"
24   #include "aboutdialog.h"
25   #include "accountdialog.h"
26   #include "appsettings.h"
27 #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
28 #include <QMaemo5InformationBox>
29 #endif
30
31   PlayerMainWindow::PlayerMainWindow(QWidget *parent) :
32           QMainWindow(parent),
33           ui(new Ui::PlayerMainWindow)
34   {
35       ui->setupUi(this);
36       setWindowTitle("Vlc remote");
37
38       mIsFirstStatusCall = true;
39
40       mTimer = new QTimer(this);
41       mNetManager = new QNetworkAccessManager(this);
42       mPlayListMainWindow = new PlayListMainWindow;
43       mBrowserMainWindow = new BrowseMainWindow;
44       mFavouritesMainWindow = new FavouritesMainWindow;
45
46       mVolume = 100;
47       mMuted = false;
48
49       mIsLandscape = true;
50
51       ui->playlistButton->setIcon(QIcon::fromTheme("notes_bullets"));
52       ui->browseButton->setIcon(QIcon::fromTheme("filemanager_media_folder"));
53
54       ui->previousButton->setIcon(QIcon::fromTheme("pdf_viewer_first_page"));
55       ui->nextButton->setIcon(QIcon::fromTheme("pdf_viewer_last_page"));
56       ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
57       ui->stopButton->setIcon(QIcon::fromTheme("camera_video_stop"));
58       ui->fullscreenButton->setIcon(QIcon::fromTheme("general_fullsize"));
59       ui->volDown->setIcon(QIcon::fromTheme("statusarea_volumelevel1"));
60       ui->volUp->setIcon(QIcon::fromTheme("statusarea_volumelevel4"));
61       ui->volMute->setIcon(QIcon::fromTheme("statusarea_volume_mute"));
62
63       ui->labelArtPortrait->setVisible(false);
64       ui->labelArtLandscape->setVisible(false);
65
66       ui->labelTitle->setTextFormat(Qt::RichText);
67       ui->labelArtist->setTextFormat(Qt::RichText);
68       ui->labelAlbum->setTextFormat(Qt::RichText);
69
70
71   #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
72
73       mPlayListMainWindow->setParent(this);
74       mPlayListMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow, true);
75       setAttribute(Qt::WA_Maemo5StackedWindow, true);
76       mPlayListMainWindow->setWindowFlags(mPlayListMainWindow->windowFlags() | Qt::Window);
77
78       mBrowserMainWindow->setParent(this);
79       mBrowserMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow, true);
80       setAttribute(Qt::WA_Maemo5StackedWindow, true);
81       mBrowserMainWindow->setWindowFlags(mBrowserMainWindow->windowFlags() | Qt::Window);
82
83       mFavouritesMainWindow->setParent(this);
84       mFavouritesMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow, true);
85       setAttribute(Qt::WA_Maemo5StackedWindow, true);
86       mFavouritesMainWindow->setWindowFlags(mFavouritesMainWindow->windowFlags() | Qt::Window);
87
88       connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(orientationChanged()));
89
90   #endif
91
92       connect(mTimer,SIGNAL(timeout()),this,SLOT(askStatus()));
93       connect(ui->actionConfiguration,SIGNAL(triggered()),this,SLOT(showConfig()));
94       connect(ui->actionAbout,SIGNAL(triggered()),this,SLOT(showAbout()));
95       connect(ui->actionPortrait,SIGNAL(triggered()),this,SLOT(setPortrait()));
96       connect(ui->actionLandscape,SIGNAL(triggered()),this,SLOT(setLandscape()));
97       connect(ui->actionAutoRotate,SIGNAL(triggered()),this,SLOT(setAutoRotate()));
98       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(show()));
99       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(showPlayList()));
100       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(show()));
101       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(showCurrentDirectory()));
102
103       connect(ui->playpauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
104       connect(ui->stopButton,SIGNAL(clicked()),this,SLOT(stop()));
105       connect(ui->previousButton,SIGNAL(clicked()),this,SLOT(previous()));
106       connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(next()));
107       connect(ui->fullscreenButton,SIGNAL(clicked()),this,SLOT(fullscreen()));
108       connect(ui->volUp,SIGNAL(clicked()),this,SLOT(volUp()));
109       connect(ui->volDown,SIGNAL(clicked()),this,SLOT(volDown()));
110       connect(ui->volMute,SIGNAL(clicked()),this,SLOT(volMute()));
111       connect(ui->slider,SIGNAL(sliderMoved(int)),this,SLOT(slide(int)));
112
113       connect(mPlayListMainWindow, SIGNAL(idUpdated(int,bool,QString)), this, SLOT(playlistIdUpdated(int, bool, QString)));
114       connect(mBrowserMainWindow, SIGNAL(showFavouritesWindow()), this, SLOT(showFavourites()));
115
116       connect(mFavouritesMainWindow, SIGNAL(browseDirectory(QString)), mFavouritesMainWindow, SLOT(close()));
117       connect(mFavouritesMainWindow, SIGNAL(browseDirectory(QString)), mBrowserMainWindow, SLOT(browseDirectory(QString)));
118       connect(mFavouritesMainWindow, SIGNAL(closeSignal()), mBrowserMainWindow, SLOT(show()));
119
120
121       // check if last used connection is still valid or showConfig
122
123       // check for network
124       if (AppSettings::isConnected()) {
125           QSettings settings;
126           QString last_ip = AppSettings::getCurrentIp(); // AccountDialog::currentIp();
127           if (!last_ip.isNull() && !last_ip.isEmpty()) {
128               QTcpSocket * socket = new QTcpSocket;
129               if(last_ip.contains(":"))
130               {
131                   QStringList hostSplit = last_ip.split(":");
132                   QString ip   = hostSplit.at(0);
133                   QString port = hostSplit.at(1);
134                   socket->connectToHost(ip,port.toInt());
135               }
136               else {
137                   socket->connectToHost(last_ip,8080);
138               }
139               if (!socket->waitForConnected(AppSettings::getConnectionTimeout())) {
140                      showConfig();
141                  }
142               else {
143                   mIp= last_ip;
144
145                  mPlayListMainWindow->init();
146                  mBrowserMainWindow->init();
147                  mTimer->start(AppSettings::getStatusPollTimeout());
148                  askStatus();
149               }
150               delete socket;
151           }
152           else {
153             showConfig();
154           }
155       }
156       else {
157 #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
158         QMaemo5InformationBox::information(this, tr("Network unavailable!"), QMaemo5InformationBox::DefaultTimeout);
159 #endif
160           showConfig();
161       }
162   }
163   
164
165   PlayerMainWindow::~PlayerMainWindow()
166   {
167       delete ui;
168   }
169
170   void PlayerMainWindow::changeEvent(QEvent *e)
171   {
172       QMainWindow::changeEvent(e);
173       switch (e->type()) {
174       case QEvent::LanguageChange:
175           ui->retranslateUi(this);
176           break;
177       default:
178           break;
179       }
180   }
181
182   void PlayerMainWindow::showFavourites() {
183       mFavouritesMainWindow->show();
184       mFavouritesMainWindow->init();
185   }
186
187   void PlayerMainWindow::setPortrait()
188   {
189     #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
190       AppSettings::setOrientation(PORTRAIT);
191     this->setAttribute(Qt::WA_Maemo5PortraitOrientation, true);
192     #endif
193   }
194
195   void PlayerMainWindow::setLandscape()
196   {
197     #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
198       AppSettings::setOrientation(LANDSCAPE);
199     this->setAttribute(Qt::WA_Maemo5LandscapeOrientation, true);
200     #endif
201   }
202
203   void PlayerMainWindow::setAutoRotate()
204   {
205     #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
206       AppSettings::setOrientation(AUTO_ROTATE);
207     this->setAttribute(Qt::WA_Maemo5AutoOrientation, true);
208     #endif
209   }
210
211   void PlayerMainWindow::orientationChanged() {
212       QRect screenGeometry = QApplication::desktop()->screenGeometry();
213       mIsLandscape = (screenGeometry.width() > screenGeometry.height());
214       if (mHasImage) {
215           if (mIsLandscape) {
216               ui->labelArtPortrait->setVisible(false);
217               ui->labelArtLandscape->setVisible(true);
218           }
219           else {
220               ui->labelArtLandscape->setVisible(false);
221               ui->labelArtPortrait->setVisible(true);
222           }
223       }
224       else {
225           ui->labelArtLandscape->setVisible(false);
226           ui->labelArtPortrait->setVisible(false);
227       }
228   }
229
230   void PlayerMainWindow::playpause()
231   {
232       // NB. There is no guarentee that our current state is the real current state.
233       // This is due to the polling frequency and possibility of user interaction directly on the server.
234       // Still this is probably better than nothing and our next real poll will set us straight again.
235       if (PAUSED == mCurrentStatus.state) {
236         mCurrentStatus.state = PLAYING;
237         pause();
238         updateUiWithCurrentStatus();
239       }
240       else if (PLAYING == mCurrentStatus.state) {
241         mCurrentStatus.state = PAUSED;
242         pause();
243         updateUiWithCurrentStatus();
244       }
245       else {
246         // could be STOP or UNKNOWN, either way there is no guarentee we will enter a playing state next.
247         // So don't update the current state or UI
248         // Ideally we would try to find a way to check the current state again but this could lead to an infinite loop!
249         play();
250       }
251   }
252   void PlayerMainWindow::play()
253   {
254       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_play")));
255   }
256   void PlayerMainWindow::stop()
257   {
258       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_stop")));
259   }
260   void PlayerMainWindow::pause()
261   {
262       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_pause")));
263   }
264   void PlayerMainWindow::previous()
265   {
266       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_previous")));
267   }
268   void PlayerMainWindow::next()
269   {
270       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_next")));
271   }
272   void PlayerMainWindow::fullscreen()
273   {
274       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=fullscreen")));
275   }
276   void PlayerMainWindow::volUp()
277   {
278       QUrl url = QUrl("http://"+mIp+"/requests/status.xml?command=volume");
279       url.addEncodedQueryItem(QByteArray("val"), QByteArray("%2B10"));
280       mNetManager->get(QNetworkRequest(url));
281   }
282   void PlayerMainWindow::volDown()
283   {
284       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=-10")));
285   }
286   void PlayerMainWindow::volMute()
287   {
288       this->mMuted = !this->mMuted;
289       if (this->mMuted) {
290           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=0")));
291       }
292       else {
293           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val="+QString::number(this->mVolume))));
294       }
295   }
296   void PlayerMainWindow::slide(int value)
297   {
298       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=seek&val="+QString::number(value)+"%25")));
299   }
300
301   void PlayerMainWindow::showConfig()
302   {
303       mTimer->stop();
304       // check for network
305       if (AppSettings::isConnected()) {
306           AccountDialog * dialog = new AccountDialog(this);
307           dialog->exec();
308
309           mIp= AppSettings::getCurrentIp(); //AccountDialog::currentIp();
310
311           mPlayListMainWindow->init();
312           mBrowserMainWindow->init();
313           mTimer->start(AppSettings::getStatusPollTimeout());
314           askStatus();
315       }
316       else {
317 #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
318         QMaemo5InformationBox::information(this, tr("Network unavailable!"), QMaemo5InformationBox::DefaultTimeout);
319 #endif
320         QTimer::singleShot(AppSettings::getRetryNetworkTimeout(), this, SLOT(showConfig()));
321       }
322   }
323   void PlayerMainWindow::showAbout()
324   {
325
326       AboutDialog * dialog = new AboutDialog;
327       dialog->exec();
328
329   }
330
331   void PlayerMainWindow::askStatus()
332   {
333       //qDebug() << "Status requested. at:" << QTime::currentTime().toString("hh::mm:ss");
334       if (AppSettings::isConnected()) {
335           QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml")));
336           connect(reply,SIGNAL(readyRead()),this,SLOT(parseXmlStatus()));
337           connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(error(QNetworkReply::NetworkError)));
338       }
339       else {
340         showConfig(); // this will handle stopping and restarting the timer.
341       }
342   }
343
344   void PlayerMainWindow::parseXmlStatus()
345   {
346       QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
347       QDomDocument doc;
348       doc.setContent(reply->readAll());
349       delete reply;
350       QDomElement docElem = doc.documentElement();
351       VlcStatusState oldState = mCurrentStatus.state;
352       // Get the raw values
353       int volume = docElem.namedItem("volume").toElement().text().toInt();
354       int length = docElem.namedItem("length").toElement().text().toInt();
355       int time = docElem.namedItem("time").toElement().text().toInt();
356       int position = docElem.namedItem("position").toElement().text().toInt();
357       int random = docElem.namedItem("random").toElement().text().toInt();
358       int loop = docElem.namedItem("loop").toElement().text().toInt();
359       int repeat = docElem.namedItem("repeat").toElement().text().toInt();
360       QString state = docElem.namedItem("state").toElement().text();
361       QDomNode infoNode =  docElem.namedItem("information");
362       QDomNode metaInfoNode =  infoNode.namedItem("meta-information");
363       QString title = metaInfoNode.namedItem("title").toElement().text().replace("\\\\", "\\");
364       // if it's a file style title fix it up
365       if (40 < title.length()) {
366           if (0 < title.lastIndexOf("\\")) {
367               title = title.right(title.length() - (title.lastIndexOf("\\") + 1));
368           }
369           else if (0 < title.lastIndexOf("/")) {
370               title = title.right(title.length() - (title.lastIndexOf("/") + 1));
371           }
372       }
373       QString artist = metaInfoNode.namedItem("artist").toElement().text();
374       QString album = metaInfoNode.namedItem("album").toElement().text();
375       QString now_playing = metaInfoNode.namedItem("now_playing").toElement().text();
376       QString art_url = metaInfoNode.namedItem("art_url").toElement().text();
377       // Populate the current status structure
378       // now would be a good time to work out if we are a new track / file or not.
379       // key if we are going to look for album art later
380       // for now we check length and title this will require further examination later
381       if (!state.isNull() && !state.isEmpty()) {
382           if (0 == QString::compare("playing", state, Qt::CaseInsensitive)) {
383             mCurrentStatus.state = PLAYING;
384           }
385           else if (0 == QString::compare("paused", state, Qt::CaseInsensitive)) {
386             mCurrentStatus.state = PAUSED;
387           }
388           else if (0 == QString::compare("stop", state, Qt::CaseInsensitive)) {
389             mCurrentStatus.state = STOP;
390           }
391           else {
392             mCurrentStatus.state = UNKNOWN;
393             mIsFirstStatusCall = true;
394           }
395       }
396       else {
397           mCurrentStatus.state = UNKNOWN;
398           mIsFirstStatusCall = true;
399       }
400       mCurrentStatus.newtrack = true;
401       if (mIsFirstStatusCall) {
402           mIsFirstStatusCall = false;
403           mCurrentStatus.newtrack = true; // unneeded but self-documenting
404       }
405       else if (STOP == oldState && STOP != mCurrentStatus.state) {
406           mCurrentStatus.newtrack = true; // unneeded but self-documenting
407       }
408       else {
409           if ( (0 == mCurrentStatus.length || STOP == mCurrentStatus.state) // stopped or null
410                   || // same track as current playing
411                (mCurrentStatus.length == length && !mCurrentStatus.title.isNull() && !title.isNull() && 0 == QString::compare(mCurrentStatus.title, title)) ){
412             mCurrentStatus.newtrack = false;
413           }
414       }
415       mCurrentStatus.volume = volume;
416       mCurrentStatus.length = length;
417       mCurrentStatus.time = time;
418       mCurrentStatus.position = position;
419       mCurrentStatus.random = (1 == random);
420       mCurrentStatus.loop = (1 == loop);
421       mCurrentStatus.repeat = (1 == repeat);
422       mCurrentStatus.title = title;
423       mCurrentStatus.artist = artist;
424       mCurrentStatus.album = album;
425       mCurrentStatus.nowplaying = now_playing;
426       mCurrentStatus.hasart = (!art_url.isNull() && !art_url.isEmpty());
427       // What's our mute status?
428       if (0 < mCurrentStatus.volume) {
429           this->mVolume = mCurrentStatus.volume;
430           this->mMuted = false;
431       }
432       else {
433           this->mMuted = true;
434       }
435       // Update the UI
436       updateUiWithCurrentStatus();
437
438   }
439
440   void PlayerMainWindow::updateUiWithCurrentStatus() {
441       // position
442       QTime timePosition(0,0,0) ;
443       timePosition =  timePosition.addSecs(mCurrentStatus.time);
444
445       ui->timeLabel->setText(timePosition.toString("h:mm:ss"));
446
447       // duration
448       if (0 < mCurrentStatus.length) {
449           QTime timeDuration(0,0,0) ;
450           timeDuration =  timeDuration.addSecs(mCurrentStatus.length);
451
452           ui->durationLabel->setText(timeDuration.toString("h:mm:ss"));
453       }
454       else {
455           ui->durationLabel->setText("0:00:00");
456       }
457
458
459       if (mCurrentStatus.position >= 0 && mCurrentStatus.position <= 100) {
460           ui->slider->blockSignals(true);
461           ui->slider->setValue(mCurrentStatus.position);
462           ui->slider->blockSignals(false);
463       }
464
465       ui->labelTitle->setText(mCurrentStatus.title);
466       ui->labelArtist->setText(mCurrentStatus.artist);
467       ui->labelAlbum->setText(mCurrentStatus.album);
468
469       if (PLAYING == mCurrentStatus.state) {
470           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
471       }
472       else {
473           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
474       }
475
476       if (STOP == mCurrentStatus.state) {
477           ui->labelArtPortrait->setVisible(false);
478           ui->labelArtLandscape->setVisible(false);
479       }
480
481       if (mCurrentStatus.newtrack && STOP != mCurrentStatus.state) {
482           // potential actions:
483           //   rebuild display layout
484           //   retrieve album art
485           mHasImage = false;
486           QTimer::singleShot(AppSettings::getRetrieveArtTimeout(), mPlayListMainWindow, SLOT(requestPlayList()));
487       }
488       // Update the buttons on the playlist window
489       if (NULL != this->mPlayListMainWindow) {
490         this->mPlayListMainWindow->updateUiWithCurrentStatus(& mCurrentStatus);
491       }
492
493   }
494   void PlayerMainWindow::playlistIdUpdated(int id, bool hasart, QString extension) {
495       Q_UNUSED(extension);
496       if (hasart) {
497           getCoverArt(id);
498       }
499       else {
500           ui->labelArtLandscape->setVisible(false);
501           ui->labelArtPortrait->setVisible(false);
502           // could use a default graphic from extension here!
503           // setCoverArtFromPixmap();
504       }
505   }
506   void PlayerMainWindow::error(QNetworkReply::NetworkError code) {
507       qDebug() << "Error Code: " << code;
508   }
509   void PlayerMainWindow::readReady() {
510     QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
511     // append to buffer
512     mResponse += reply->readAll();
513   }
514   void PlayerMainWindow::finished(QNetworkReply * reply) {
515     // now we can call setCoverArt to process the full buffers
516     this->setCoverArt(mResponse);
517     // only interested in finished signals
518     disconnect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
519     delete reply;
520   }
521   void PlayerMainWindow::getCoverArt(int id) {
522       qDebug() << "getCoverArt id=!" << id;
523     mResponse.clear();
524     QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/art?id=" + QString::number(id))));
525     connect(reply,SIGNAL(readyRead()),this,SLOT(readReady()));
526     connect(reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(error(QNetworkReply::NetworkError)));
527     connect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
528
529   }
530   void PlayerMainWindow::setCoverArt(const QByteArray data) {
531     QPixmap* image = new QPixmap();
532     if (image->loadFromData(data)) {
533         mHasImage = true;
534         ui->labelArtLandscape->setPixmap(image->scaledToHeight(120, Qt::SmoothTransformation));
535         ui->labelArtPortrait->setPixmap(image->scaledToHeight(310, Qt::SmoothTransformation));
536         if (mIsLandscape) {
537             ui->labelArtPortrait->setVisible(false);
538             ui->labelArtLandscape->setVisible(true);
539         }
540         else {
541             ui->labelArtLandscape->setVisible(false);
542             ui->labelArtPortrait->setVisible(true);
543         }
544     }
545     else {
546         qDebug() << "image load failed!";
547         qDebug() << "data.length" << data.length();
548         ui->labelArtPortrait->setVisible(false);
549         ui->labelArtLandscape->setVisible(false);
550     }
551     delete image;
552   }
553   void PlayerMainWindow::setCoverArtFromPixmap(QPixmap image) {
554     mHasImage = true;
555     ui->labelArtLandscape->setPixmap(image.scaledToHeight(120, Qt::SmoothTransformation));
556     ui->labelArtPortrait->setPixmap(image.scaledToHeight(320, Qt::SmoothTransformation));
557     if (mIsLandscape) {
558         ui->labelArtPortrait->setVisible(false);
559         ui->labelArtLandscape->setVisible(true);
560     }
561     else {
562         ui->labelArtLandscape->setVisible(false);
563         ui->labelArtPortrait->setVisible(true);
564     }
565   }
566