Began refactoring settings handling.
[vlc-remote] / 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 "vlcstatus.h"
27
28   PlayerMainWindow::PlayerMainWindow(QWidget *parent) :
29           QMainWindow(parent),
30           ui(new Ui::PlayerMainWindow)
31   {
32       ui->setupUi(this);
33       setWindowTitle("Vlc remote");
34
35
36
37       mTimer = new QTimer(this);
38       mNetManager = new QNetworkAccessManager(this);
39       mPlayListMainWindow = new PlayListMainWindow;
40       mBrowserMainWindow = new BrowseMainWindow;
41
42       mVolume = 100;
43       mMuted = false;
44
45       mIsLandscape = true;
46
47       ui->playlistButton->setIcon(QIcon::fromTheme("notes_bullets"));
48       ui->browseButton->setIcon(QIcon::fromTheme("filemanager_media_folder"));
49
50       ui->previousButton->setIcon(QIcon::fromTheme("pdf_viewer_first_page"));
51       ui->nextButton->setIcon(QIcon::fromTheme("pdf_viewer_last_page"));
52       ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
53       ui->stopButton->setIcon(QIcon::fromTheme("camera_video_stop"));
54       //ui->pauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
55       ui->fullscreenButton->setIcon(QIcon::fromTheme("general_fullsize"));
56       ui->volDown->setIcon(QIcon::fromTheme("statusarea_volumelevel1"));
57       ui->volUp->setIcon(QIcon::fromTheme("statusarea_volumelevel4"));
58       ui->volMute->setIcon(QIcon::fromTheme("statusarea_volume_mute"));
59
60       ui->labelArtPortrait->setVisible(false);
61       ui->labelArtLandscape->setVisible(false);
62
63       ui->labelTitle->setTextFormat(Qt::RichText);
64       ui->labelArtist->setTextFormat(Qt::RichText);
65       ui->labelAlbum->setTextFormat(Qt::RichText);
66
67
68   #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
69
70       mPlayListMainWindow->setParent(this);
71       mPlayListMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow);
72       setAttribute(Qt::WA_Maemo5StackedWindow);
73       mPlayListMainWindow->setWindowFlags(mPlayListMainWindow->windowFlags() | Qt::Window);
74
75       mBrowserMainWindow->setParent(this);
76       mBrowserMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow);
77       setAttribute(Qt::WA_Maemo5StackedWindow);
78       mBrowserMainWindow->setWindowFlags(mBrowserMainWindow->windowFlags() | Qt::Window);
79
80       connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(orientationChanged()));
81
82   #endif
83
84       connect(mTimer,SIGNAL(timeout()),this,SLOT(askStatus()));
85       connect(ui->actionConfiguration,SIGNAL(triggered()),this,SLOT(showConfig()));
86       connect(ui->actionAbout,SIGNAL(triggered()),this,SLOT(showAbout()));
87       connect(ui->actionPortrait,SIGNAL(triggered()),this,SLOT(setPortrait()));
88       connect(ui->actionLandscape,SIGNAL(triggered()),this,SLOT(setLandscape()));
89       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(show()));
90       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(showPlayList()));
91       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(show()));
92       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(showCurrentDirectory()));
93
94       connect(ui->playpauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
95       connect(ui->stopButton,SIGNAL(clicked()),this,SLOT(stop()));
96       //connect(ui->pauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
97       connect(ui->previousButton,SIGNAL(clicked()),this,SLOT(previous()));
98       connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(next()));
99       connect(ui->fullscreenButton,SIGNAL(clicked()),this,SLOT(fullscreen()));
100       connect(ui->volUp,SIGNAL(clicked()),this,SLOT(volUp()));
101       connect(ui->volDown,SIGNAL(clicked()),this,SLOT(volDown()));
102       connect(ui->volMute,SIGNAL(clicked()),this,SLOT(volMute()));
103       connect(ui->slider,SIGNAL(sliderMoved(int)),this,SLOT(slide(int)));
104
105       connect(mPlayListMainWindow, SIGNAL(idUpdated(int,bool,QString)), this, SLOT(playlistIdUpdated(int, bool, QString)));
106
107
108       // check if last used connection is still valid or showConfig
109       QSettings settings;
110       QString last_ip = AccountDialog::currentIp();
111       if (!last_ip.isNull() && !last_ip.isEmpty()) {
112           QTcpSocket * socket = new QTcpSocket;
113           if(last_ip.contains(":"))
114           {
115               QStringList hostSplit = last_ip.split(":");
116               QString ip   = hostSplit.at(0);
117               QString port = hostSplit.at(1);
118               socket->connectToHost(ip,port.toInt());
119           }
120           else {
121               socket->connectToHost(last_ip,8080);
122           }
123           if (!socket->waitForConnected(1000)) {
124                  showConfig();
125              }
126           else {
127               mIp= last_ip;
128
129              mPlayListMainWindow->init();
130              mBrowserMainWindow->init();
131              mTimer->start(5000);
132              askStatus();
133           }
134           delete socket;
135       }
136       else {
137         showConfig();
138       }
139
140
141   }
142   
143
144   PlayerMainWindow::~PlayerMainWindow()
145   {
146       delete ui;
147   }
148
149   void PlayerMainWindow::changeEvent(QEvent *e)
150   {
151       QMainWindow::changeEvent(e);
152       switch (e->type()) {
153       case QEvent::LanguageChange:
154           ui->retranslateUi(this);
155           break;
156       default:
157           break;
158       }
159   }
160
161   void PlayerMainWindow::setPortrait()
162   {
163      #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
164      this->setAttribute(Qt::WA_Maemo5PortraitOrientation, true);
165     #endif
166     
167       
168   }
169
170   void PlayerMainWindow::setLandscape()
171   {
172       #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
173       this->setAttribute(Qt::WA_Maemo5LandscapeOrientation, true);
174       #endif
175   }
176
177   void PlayerMainWindow::setAutoRotate()
178   {
179     #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
180    this->setAttribute(Qt::WA_Maemo5AutoOrientation, true);
181    #endif
182   }
183
184   void PlayerMainWindow::orientationChanged() {
185       QRect screenGeometry = QApplication::desktop()->screenGeometry();
186       mIsLandscape = (screenGeometry.width() > screenGeometry.height());
187       if (mHasImage) {
188           if (mIsLandscape) {
189               ui->labelArtPortrait->setVisible(false);
190               ui->labelArtLandscape->setVisible(true);
191           }
192           else {
193               ui->labelArtLandscape->setVisible(false);
194               ui->labelArtPortrait->setVisible(true);
195           }
196       }
197       else {
198           ui->labelArtLandscape->setVisible(false);
199           ui->labelArtPortrait->setVisible(false);
200       }
201   }
202
203   void PlayerMainWindow::playpause()
204   {
205       // NB. There is no guarentee that our current state is the real current state.
206       // This is due to the polling frequency and possibility of user interaction directly on the server.
207       // Still this is probably better than nothing and our next real poll will set us straight again.
208       if (PAUSED == mCurrentStatus.state) {
209         mCurrentStatus.state = PLAYING;
210         pause();
211         updateUiWithCurrentStatus();
212       }
213       else if (PLAYING == mCurrentStatus.state) {
214         mCurrentStatus.state = PAUSED;
215         pause();
216         updateUiWithCurrentStatus();
217       }
218       else {
219         // could be STOP or UNKNOWN, either way there is no guarentee we will enter a playing state next.
220         // So don't update the current state or UI
221         // Ideally we would try to find a way to check the current state again but this could lead to an infinite loop!
222         play();
223       }
224   }
225   void PlayerMainWindow::play()
226   {
227       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_play")));
228   }
229   void PlayerMainWindow::stop()
230   {
231       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_stop")));
232   }
233   void PlayerMainWindow::pause()
234   {
235       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_pause")));
236   }
237   void PlayerMainWindow::previous()
238   {
239       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_previous")));
240   }
241   void PlayerMainWindow::next()
242   {
243       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_next")));
244   }
245   void PlayerMainWindow::fullscreen()
246   {
247       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=fullscreen")));
248   }
249   void PlayerMainWindow::volUp()
250   {
251       QUrl url = QUrl("http://"+mIp+"/requests/status.xml?command=volume");
252       url.addEncodedQueryItem(QByteArray("val"), QByteArray("%2B20"));
253       mNetManager->get(QNetworkRequest(url));
254   }
255   void PlayerMainWindow::volDown()
256   {
257       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=-20")));
258   }
259   void PlayerMainWindow::volMute()
260   {
261       this->mMuted = !this->mMuted;
262       if (this->mMuted) {
263           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=0")));
264       }
265       else {
266           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val="+QString::number(this->mVolume))));
267       }
268   }
269   void PlayerMainWindow::slide(int value)
270   {
271       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=seek&val="+QString::number(value)+"%25")));
272   }
273
274   void PlayerMainWindow::showConfig()
275   {
276       mTimer->stop();
277       AccountDialog * dialog = new AccountDialog;
278       dialog->exec();
279      
280        mIp= AccountDialog::currentIp();
281
282       mPlayListMainWindow->init();
283       mBrowserMainWindow->init();
284       mTimer->start(5000);
285       askStatus();
286   }
287   void PlayerMainWindow::showAbout()
288   {
289
290       AboutDialog * dialog = new AboutDialog;
291       dialog->exec();
292
293   }
294
295   void PlayerMainWindow::askStatus()
296   {
297
298       QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml")));
299       connect(reply,SIGNAL(readyRead()),this,SLOT(parseXmlStatus()));
300   }
301
302   void PlayerMainWindow::parseXmlStatus()
303   {
304       QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
305       QDomDocument doc;
306       doc.setContent(reply->readAll());
307       delete reply;
308       QDomElement docElem = doc.documentElement();
309       // Get the raw values
310       int volume = docElem.namedItem("volume").toElement().text().toInt();
311       int length = docElem.namedItem("length").toElement().text().toInt();
312       int time = docElem.namedItem("time").toElement().text().toInt();
313       int position = docElem.namedItem("position").toElement().text().toInt();
314       int random = docElem.namedItem("random").toElement().text().toInt();
315       int loop = docElem.namedItem("loop").toElement().text().toInt();
316       int repeat = docElem.namedItem("repeat").toElement().text().toInt();
317       QString state = docElem.namedItem("state").toElement().text();
318       QDomNode infoNode =  docElem.namedItem("information");
319       QDomNode metaInfoNode =  infoNode.namedItem("meta-information");
320       QString title = metaInfoNode.namedItem("title").toElement().text().replace("\\\\", "\\");
321       // if it's a file style title fix it up
322       if (40 < title.length()) {
323           if (0 < title.lastIndexOf("\\")) {
324               title = title.right(title.length() - (title.lastIndexOf("\\") + 1));
325           }
326           else if (0 < title.lastIndexOf("/")) {
327               title = title.right(title.length() - (title.lastIndexOf("/") + 1));
328           }
329       }
330       QString artist = metaInfoNode.namedItem("artist").toElement().text();
331       QString album = metaInfoNode.namedItem("album").toElement().text();
332       QString now_playing = metaInfoNode.namedItem("now_playing").toElement().text();
333       QString art_url = metaInfoNode.namedItem("art_url").toElement().text();
334       // Populate the current status structure
335       // now would be a good time to work out if we are a new track / file or not.
336       // key if we are going to look for album art later
337       // for now we check length and title this will require further examination later
338       mCurrentStatus.newtrack = true;
339       if (mCurrentStatus.length == length && !mCurrentStatus.title.isNull() && 0 == QString::compare(mCurrentStatus.title, title)) {
340         mCurrentStatus.newtrack = false;
341       }
342       mCurrentStatus.volume = volume;
343       mCurrentStatus.length = length;
344       mCurrentStatus.time = time;
345       mCurrentStatus.position = position;
346       mCurrentStatus.random = (1 == random);
347       mCurrentStatus.loop = (1 == loop);
348       mCurrentStatus.repeat = (1 == repeat);
349       mCurrentStatus.title = title;
350       mCurrentStatus.artist = artist;
351       mCurrentStatus.album = album;
352       mCurrentStatus.nowplaying = now_playing;
353       mCurrentStatus.hasart = (!art_url.isNull() && !art_url.isEmpty());
354       if (!state.isNull() && !state.isEmpty()) {
355           if (0 == QString::compare("playing", state, Qt::CaseInsensitive)) {
356             mCurrentStatus.state = PLAYING;
357           }
358           else if (0 == QString::compare("paused", state, Qt::CaseInsensitive)) {
359             mCurrentStatus.state = PAUSED;
360           }
361           else if (0 == QString::compare("stop", state, Qt::CaseInsensitive)) {
362             mCurrentStatus.state = STOP;
363           }
364           else {
365             mCurrentStatus.state = UNKNOWN;
366           }
367       }
368       else {
369           mCurrentStatus.state = UNKNOWN;
370       }
371       // What's our mute status?
372       if (0 < mCurrentStatus.volume) {
373           this->mVolume = mCurrentStatus.volume;
374           this->mMuted = false;
375       }
376       else {
377           this->mMuted = true;
378       }
379       // Update the UI
380       updateUiWithCurrentStatus();
381
382   }
383
384   void PlayerMainWindow::updateUiWithCurrentStatus() {
385       // position
386       QTime timePosition(0,0,0) ;
387       timePosition =  timePosition.addSecs(mCurrentStatus.time);
388
389       ui->timeLabel->setText(timePosition.toString("h:mm:ss"));
390
391       // duration
392       if (0 < mCurrentStatus.length) {
393           QTime timeDuration(0,0,0) ;
394           timeDuration =  timeDuration.addSecs(mCurrentStatus.length);
395
396           ui->durationLabel->setText(timeDuration.toString("h:mm:ss"));
397       }
398       else {
399           ui->durationLabel->setText("0:00:00");
400       }
401
402
403       if (mCurrentStatus.position >= 0 && mCurrentStatus.position <= 100)
404           ui->slider->setValue(mCurrentStatus.position);
405
406       ui->labelTitle->setText(mCurrentStatus.title);
407       ui->labelArtist->setText(mCurrentStatus.artist);
408       ui->labelAlbum->setText(mCurrentStatus.album);
409
410       if (PLAYING == mCurrentStatus.state) {
411           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
412       }
413       else {
414           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
415       }
416
417       if (mCurrentStatus.newtrack) {
418           // potential actions:
419           //   rebuild display layout
420           //   retrieve album art
421           mHasImage = false;
422           QTimer::singleShot(500, mPlayListMainWindow, SLOT(requestPlayList()));
423       }
424       // Update the buttons on the playlist window
425       if (NULL != this->mPlayListMainWindow) {
426         this->mPlayListMainWindow->updateUiWithCurrentStatus(& mCurrentStatus);
427       }
428
429   }
430   void PlayerMainWindow::playlistIdUpdated(int id, bool hasart, QString extension) {
431       if (hasart) {
432           getCoverArt(id);
433       }
434       else {
435           ui->labelArtLandscape->setVisible(false);
436           ui->labelArtPortrait->setVisible(false);
437           // could use a default graphic here!
438           // setCoverArtFromPixmap();
439       }
440   }
441   void PlayerMainWindow::readReady() {
442     QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
443     // append to buffer
444     mResponse += reply->readAll();
445   }
446   void PlayerMainWindow::finished(QNetworkReply * reply) {
447     // now we can call setCoverArt to process the full buffers
448     this->setCoverArt(mResponse);
449     // only interested in finished signals
450     disconnect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
451   }
452   void PlayerMainWindow::getCoverArt(int id) {
453     mResponse.clear();
454     QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/art?id=" + QString::number(id))));
455     connect(reply,SIGNAL(readyRead()),this,SLOT(readReady()));
456     connect(mNetManager,SIGNAL(finished(QNetworkReply *)),this,SLOT(finished(QNetworkReply *)));
457
458   }
459   void PlayerMainWindow::setCoverArt(const QByteArray data) {
460     QPixmap* image = new QPixmap();
461     if (image->loadFromData(data)) {
462         mHasImage = true;
463         ui->labelArtLandscape->setPixmap(image->scaledToHeight(120, Qt::SmoothTransformation));
464         ui->labelArtPortrait->setPixmap(image->scaledToHeight(310, Qt::SmoothTransformation));
465         if (mIsLandscape) {
466             ui->labelArtPortrait->setVisible(false);
467             ui->labelArtLandscape->setVisible(true);
468         }
469         else {
470             ui->labelArtLandscape->setVisible(false);
471             ui->labelArtPortrait->setVisible(true);
472         }
473     }
474     else {
475         ui->labelArtPortrait->setVisible(false);
476         ui->labelArtLandscape->setVisible(false);
477     }
478   }
479   void PlayerMainWindow::setCoverArtFromPixmap(QPixmap image) {
480     mHasImage = true;
481     ui->labelArtLandscape->setPixmap(image.scaledToHeight(120, Qt::SmoothTransformation));
482     ui->labelArtPortrait->setPixmap(image.scaledToHeight(320, Qt::SmoothTransformation));
483     if (mIsLandscape) {
484         ui->labelArtPortrait->setVisible(false);
485         ui->labelArtLandscape->setVisible(true);
486     }
487     else {
488         ui->labelArtLandscape->setVisible(false);
489         ui->labelArtPortrait->setVisible(true);
490     }
491   }
492