Merge branch 'master' of https://vcs.maemo.org/git/vlc-remote
[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 "playermainwindow.h"
21   #include "ui_playermainwindow.h"
22   #include "configdialog.h"
23   #include "aboutdialog.h"
24   #include "accountdialog.h"
25   //#include "vlcstatus.h"
26
27   PlayerMainWindow::PlayerMainWindow(QWidget *parent) :
28           QMainWindow(parent),
29           ui(new Ui::PlayerMainWindow)
30   {
31       ui->setupUi(this);
32       setWindowTitle("Vlc remote");
33
34
35
36       mTimer = new QTimer(this);
37       mNetManager = new QNetworkAccessManager(this);
38       mPlayListMainWindow = new PlayListMainWindow;
39       mBrowserMainWindow = new BrowseMainWindow;
40
41       mVolume = 100;
42       mMuted = false;
43
44       ui->playlistButton->setIcon(QIcon::fromTheme("notes_bullets"));
45       ui->browseButton->setIcon(QIcon::fromTheme("filemanager_media_folder"));
46
47       ui->previousButton->setIcon(QIcon::fromTheme("pdf_viewer_first_page"));
48       ui->nextButton->setIcon(QIcon::fromTheme("pdf_viewer_last_page"));
49       ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
50       ui->stopButton->setIcon(QIcon::fromTheme("camera_video_stop"));
51       //ui->pauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
52       ui->fullscreenButton->setIcon(QIcon::fromTheme("general_fullsize"));
53       ui->volDown->setIcon(QIcon::fromTheme("statusarea_volumelevel1"));
54       ui->volUp->setIcon(QIcon::fromTheme("statusarea_volumelevel4"));
55       ui->volMute->setIcon(QIcon::fromTheme("statusarea_volume_mute"));
56
57
58   #if defined(Q_WS_S60) || defined(Q_WS_MAEMO_5)
59       mPlayListMainWindow->setParent(this);
60       mPlayListMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow);
61       mPlayListMainWindow->setAttribute(Qt::WA_Maemo5LandscapeOrientation,true);
62       mPlayListMainWindow->setAttribute(Qt::WA_Maemo5LandscapeOrientation,true);
63       setAttribute(Qt::WA_Maemo5StackedWindow);
64       mPlayListMainWindow->setWindowFlags(mPlayListMainWindow->windowFlags() | Qt::Window);
65
66       mBrowserMainWindow->setParent(this);
67       mBrowserMainWindow->setAttribute(Qt::WA_Maemo5StackedWindow);
68       mBrowserMainWindow->setAttribute(Qt::WA_Maemo5LandscapeOrientation,true);
69       mBrowserMainWindow->setAttribute(Qt::WA_Maemo5LandscapeOrientation,true);
70       setAttribute(Qt::WA_Maemo5StackedWindow);
71       mBrowserMainWindow->setWindowFlags(mBrowserMainWindow->windowFlags() | Qt::Window);
72
73   #endif
74
75       connect(mTimer,SIGNAL(timeout()),this,SLOT(askStatus()));
76       connect(ui->actionConfiguration,SIGNAL(triggered()),this,SLOT(showConfig()));
77       connect(ui->actionAbout,SIGNAL(triggered()),this,SLOT(showAbout()));
78       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(show()));
79       connect(ui->playlistButton,SIGNAL(clicked()),mPlayListMainWindow,SLOT(showPlayList()));
80       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(show()));
81       connect(ui->browseButton,SIGNAL(clicked()),mBrowserMainWindow,SLOT(showCurrentDirectory()));
82
83       connect(ui->playpauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
84       connect(ui->stopButton,SIGNAL(clicked()),this,SLOT(stop()));
85       //connect(ui->pauseButton,SIGNAL(clicked()),this,SLOT(playpause()));
86       connect(ui->previousButton,SIGNAL(clicked()),this,SLOT(previous()));
87       connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(next()));
88       connect(ui->fullscreenButton,SIGNAL(clicked()),this,SLOT(fullscreen()));
89       connect(ui->volUp,SIGNAL(clicked()),this,SLOT(volUp()));
90       connect(ui->volDown,SIGNAL(clicked()),this,SLOT(volDown()));
91       connect(ui->volMute,SIGNAL(clicked()),this,SLOT(volMute()));
92       connect(ui->slider,SIGNAL(sliderMoved(int)),this,SLOT(slide(int)));
93
94              showConfig();
95
96
97   }
98   
99
100   PlayerMainWindow::~PlayerMainWindow()
101   {
102       delete ui;
103   }
104
105   void PlayerMainWindow::changeEvent(QEvent *e)
106   {
107       QMainWindow::changeEvent(e);
108       switch (e->type()) {
109       case QEvent::LanguageChange:
110           ui->retranslateUi(this);
111           break;
112       default:
113           break;
114       }
115   }
116
117   void PlayerMainWindow::playpause()
118   {
119       // NB. There is no guarentee that our current state is the real current state.
120       // This is due to the polling frequency and possibility of user interaction directly on the server.
121       // Still this is probably better than nothing and our next real poll will set us straight again.
122       if (PAUSED == mCurrentStatus.state) {
123         mCurrentStatus.state = PLAYING;
124         qDebug() << "pause() from PAUSED";
125         pause();
126         updateUiWithCurrentStatus();
127       }
128       else if (PLAYING == mCurrentStatus.state) {
129         mCurrentStatus.state = PAUSED;
130         qDebug() << "pause() from PLAYING";
131         pause();
132         updateUiWithCurrentStatus();
133       }
134       else {
135         // could be STOP or UNKNOWN, either way there is no guarentee we will enter a playing state next.
136         // So don't update the current state or UI
137         // Ideally we would try to find a way to check the current state again but this could lead to an infinite loop!
138         qDebug() << "play() from " << ((STOP == mCurrentStatus.state) ? "STOP" : "UNKNOWN");
139         play();
140       }
141   }
142   void PlayerMainWindow::play()
143   {
144       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_play")));
145   }
146   void PlayerMainWindow::stop()
147   {
148       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_stop")));
149   }
150   void PlayerMainWindow::pause()
151   {
152       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_pause")));
153   }
154   void PlayerMainWindow::previous()
155   {
156       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_previous")));
157   }
158   void PlayerMainWindow::next()
159   {
160       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=pl_next")));
161   }
162   void PlayerMainWindow::fullscreen()
163   {
164       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=fullscreen")));
165   }
166   void PlayerMainWindow::volUp()
167   {
168       QUrl url = QUrl("http://"+mIp+"/requests/status.xml?command=volume");
169       url.addEncodedQueryItem(QByteArray("val"), QByteArray("%2B20"));
170       mNetManager->get(QNetworkRequest(url));
171   }
172   void PlayerMainWindow::volDown()
173   {
174       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=-20")));
175   }
176   void PlayerMainWindow::volMute()
177   {
178       this->mMuted = !this->mMuted;
179       if (this->mMuted) {
180           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val=0")));
181       }
182       else {
183           mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=volume&val="+QString::number(this->mVolume))));
184       }
185   }
186   void PlayerMainWindow::slide(int value)
187   {
188       mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml?command=seek&val="+QString::number(value)+"%25")));
189   }
190
191   void PlayerMainWindow::showConfig()
192   {
193       mTimer->stop();
194       AccountDialog * dialog = new AccountDialog;
195       dialog->exec();
196      
197        mIp= AccountDialog::currentIp();
198
199       mPlayListMainWindow->init();
200       mBrowserMainWindow->init();
201       mTimer->start(5000);
202       askStatus();
203   }
204   void PlayerMainWindow::showAbout()
205   {
206
207       AboutDialog * dialog = new AboutDialog;
208       dialog->exec();
209
210   }
211
212   void PlayerMainWindow::askStatus()
213   {
214
215       QNetworkReply * reply =  mNetManager->get(QNetworkRequest(QUrl("http://"+mIp+"/requests/status.xml")));
216       connect(reply,SIGNAL(readyRead()),this,SLOT(parseXmlStatus()));
217   }
218
219   void PlayerMainWindow::parseXmlStatus()
220   {
221       QNetworkReply * reply = qobject_cast<QNetworkReply*>(sender());
222       QDomDocument doc;
223       doc.setContent(reply->readAll());
224       delete reply;
225       QDomElement docElem = doc.documentElement();
226       // Get the raw values
227       int volume = docElem.namedItem("volume").toElement().text().toInt();
228       int length = docElem.namedItem("length").toElement().text().toInt();
229       int time = docElem.namedItem("time").toElement().text().toInt();
230       int position = docElem.namedItem("position").toElement().text().toInt();
231       int random = docElem.namedItem("random").toElement().text().toInt();
232       int loop = docElem.namedItem("loop").toElement().text().toInt();
233       int repeat = docElem.namedItem("repeat").toElement().text().toInt();
234       QString state = docElem.namedItem("state").toElement().text();
235       QDomNode infoNode =  docElem.namedItem("information");
236       QDomNode metaInfoNode =  infoNode.namedItem("meta-information");
237       QString title = metaInfoNode.namedItem("title").toElement().text().replace("\\\\", "\\");
238       QString artist = metaInfoNode.namedItem("artist").toElement().text();
239       QString album = metaInfoNode.namedItem("album").toElement().text();
240       QString now_playing = metaInfoNode.namedItem("now_playing").toElement().text();
241       QString art_url = metaInfoNode.namedItem("art_url").toElement().text();
242       // Populate the current status structure
243       // now would be a good time to work out if we are a new track / file or not.
244       // key if we are going to look for album art later
245       // for now we check length and title this will require further examination later
246       mCurrentStatus.newtrack = true;
247       if (mCurrentStatus.length == length && !mCurrentStatus.title.isNull() && 0 == QString::compare(mCurrentStatus.title, title)) {
248         mCurrentStatus.newtrack = false;
249       }
250       mCurrentStatus.volume = volume;
251       mCurrentStatus.length = length;
252       mCurrentStatus.time = time;
253       mCurrentStatus.position = position;
254       mCurrentStatus.random = (1 == random);
255       mCurrentStatus.loop = (1 == loop);
256       mCurrentStatus.repeat = (1 == repeat);
257       mCurrentStatus.title = title;
258       mCurrentStatus.artist = artist;
259       mCurrentStatus.album = album;
260       mCurrentStatus.nowplaying = now_playing;
261       mCurrentStatus.hasart = (!art_url.isNull() && !art_url.isEmpty());
262       if (!state.isNull() && !state.isEmpty()) {
263           if (0 == QString::compare("playing", state, Qt::CaseInsensitive)) {
264             mCurrentStatus.state = PLAYING;
265           }
266           else if (0 == QString::compare("paused", state, Qt::CaseInsensitive)) {
267             mCurrentStatus.state = PAUSED;
268           }
269           else if (0 == QString::compare("stop", state, Qt::CaseInsensitive)) {
270             mCurrentStatus.state = STOP;
271           }
272           else {
273             mCurrentStatus.state = UNKNOWN;
274           }
275       }
276       else {
277           mCurrentStatus.state = UNKNOWN;
278       }
279       // What's our mute status?
280       if (0 < mCurrentStatus.volume) {
281           this->mVolume = mCurrentStatus.volume;
282           this->mMuted = false;
283       }
284       else {
285           this->mMuted = true;
286       }
287       // Update the UI
288       updateUiWithCurrentStatus();
289
290   }
291
292   void PlayerMainWindow::updateUiWithCurrentStatus() {
293       // position
294       QTime timePosition(0,0,0) ;
295       timePosition =  timePosition.addSecs(mCurrentStatus.time);
296
297       ui->timeLabel->setText(timePosition.toString("h:mm:ss"));
298
299       // duration
300       if (0 < mCurrentStatus.length) {
301           QTime timeDuration(0,0,0) ;
302           timeDuration =  timeDuration.addSecs(mCurrentStatus.length);
303
304           ui->durationLabel->setText(timeDuration.toString("h:mm:ss"));
305       }
306       else {
307           ui->durationLabel->setText("0:00:00");
308       }
309
310
311       if (mCurrentStatus.position >= 0 && mCurrentStatus.position <= 100)
312           ui->slider->setValue(mCurrentStatus.position);
313
314       ui->label->setText(mCurrentStatus.title);
315
316       if (PLAYING == mCurrentStatus.state) {
317           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_video_pause"));
318       }
319       else {
320           ui->playpauseButton->setIcon(QIcon::fromTheme("camera_playback"));
321       }
322
323       if (mCurrentStatus.newtrack) {
324           // potential actions:
325           //   rebuild display layout
326           //   retrieve album art
327       }
328       // Update the buttons on the playlist window
329       if (NULL != this->mPlayListMainWindow) {
330         this->mPlayListMainWindow->updateUiWithCurrentStatus(& mCurrentStatus);
331       }
332   }
333