Reverted a change introducing a bug
[icord-viewer:icord-viewer.git] / src / icordviewer.cpp
1 /*****************************************************************************
2  *  iCord Viewer - Download recordings from Humax iCord PVRs                 *
3  *  Copyright © 2010, Martin Engelmann <murphi.oss@googlemail.com>           *
4  *                                                                           *
5  *  This program is free software: you can redistribute it and/or modify     *
6  *  it under the terms of the GNU General Public License as published by     *
7  *  the Free Software Foundation, either version 3 of the License, or        *
8  *  (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, see <http://www.gnu.org/licenses/>.    *
17  *****************************************************************************/
18
19 #include "icordviewer.h"
20
21 #include <QtCore/QFile>
22 #include <QtGui/QFileDialog>
23 #include <QtGui/QMenu>
24 #include <QtGui/QMenuBar>
25 #include <QtGui/QAction>
26 #include <QtGui/QVBoxLayout>
27 #include <QtGui/QToolBar>
28 #include <QtGui/QMessageBox>
29 #include <QtGui/QStatusBar>
30 #include <QtNetwork/QUrlInfo>
31 #include <QtNetwork/QFtp>
32
33 #include "config.h"
34 #include "filterwidget.h"
35 #include "ftp.h"
36 #include "recording.h"
37 #include "recordinglistview.h"
38 #include "downloadprogressdialog.h"
39 #include "connectionsettingswidget.h"
40 #include "aboutdialog.h"
41 #include "recordingcriteriamatcher.h"
42 #include "deleterecordingsdialog.h"
43
44
45 ICordViewer::ICordViewer()
46     : settings(0), filter(0), lvRecordings(0),
47       ftp(new Ftp()),
48       downloadProgress(0),
49       downloadFile(0),
50       m_progress(0)
51 {
52     setMinimumSize(QSize(640, 480));
53     setupWidgets();
54     setupMenusAndToolbars();
55
56     connect(ftp, SIGNAL(done(bool)), this, SLOT(slotFtpDone(bool)));
57     connect(ftp, SIGNAL(recordingListInfo(QUrlInfo)),
58             this, SLOT(slotRecordingListInfo(QUrlInfo)));
59     connect(ftp, SIGNAL(previewListInfo(QString, QUrlInfo)),
60             this, SLOT(slotPreviewListInfo(QString, QUrlInfo)));
61     connect(ftp, SIGNAL(dataTransferProgress(QString, qint64, qint64)),
62             this, SLOT(slotUpdateDownloadProgress(QString, qint64, qint64)));
63     connect(ftp, SIGNAL(fileTransferCompleted(QString)),
64             this, SLOT(slotDownloadDone(QString)));
65     connect(ftp, SIGNAL(recordingRemovalStarted(const Recording*)),
66             this, SLOT(slotRecordingRemovalStarted(const Recording*)));
67     connect(ftp, SIGNAL(recordingRemoved(const Recording*)),
68             this, SLOT(slotRecordingRemoved(const Recording*)));
69
70     statusBar()->showMessage(tr("Ready.", "@message"));
71 }
72
73 ICordViewer::~ICordViewer()
74 {
75     if(ftp->isConnected()) ftp->disconnect();
76     clearRecordings();
77     delete m_progress;
78 }
79
80 void ICordViewer::slotAbout()
81 {
82     AboutDialog::show();
83 }
84
85 void ICordViewer::slotConnectClicked()
86 {
87     clearRecordings();
88     if(!ftp->isConnected()) ftp->connectToHost();
89     ftp->listRecordings();
90     statusBar()->showMessage(tr("Reading recordings", "@message"));
91 }
92
93 void ICordViewer::slotDeleteSelectedRecording()
94 {
95     Recording * recording = lvRecordings->selectedRecording();
96     QMessageBox::StandardButton answer = QMessageBox::question(
97             this, tr("Delete Recording", "@title"),
98             tr("Really delete the following recording:\n%1", "@message")
99             .arg(recording->toString()),
100             QMessageBox::Yes | QMessageBox::No, QMessageBox::No
101                                          );
102     if(answer != QMessageBox::Yes) return;
103
104     ftp->removeRecording(recording);
105
106     delete m_progress;
107     m_progress = new QProgressDialog(this);
108     m_progress->setWindowTitle(tr("Deleting recordings", "@title"));
109     m_progress->setLabelText(tr("Deleting %1", "@message").arg(recording->toString()));
110     m_progress->setCancelButton(0);
111     m_progress->setMinimumDuration(0);
112     m_progress->setMinimum(0);
113     m_progress->setMaximum(ftp->numberOfRecordingsToRemove());
114     m_progress->setValue(0);
115     connect(m_progress, SIGNAL(canceled()), ftp, SLOT(abort()));
116     connect(m_progress, SIGNAL(canceled()), m_progress, SLOT(close()));
117 }
118
119
120 void ICordViewer::slotDeleteRecordings()
121 {
122     DeleteRecordingsDialog dlg;
123     dlg.setRecordings(recordings);
124     dlg.exec();
125     if(dlg.result() != QDialog::Accepted) return;
126
127     foreach(Recording * recording, dlg.selectedRecordings()) {
128         ftp->removeRecording(recording);
129     }
130
131     delete m_progress;
132     m_progress = new QProgressDialog(this);
133     m_progress->setWindowTitle(tr("Deleting recordings", "@title"));
134     m_progress->setMinimumDuration(0);
135     m_progress->setMinimum(0);
136     m_progress->setMaximum(ftp->numberOfRecordingsToRemove());
137     m_progress->setValue(0);
138     connect(m_progress, SIGNAL(canceled()), ftp, SLOT(abort()));
139     connect(m_progress, SIGNAL(canceled()), m_progress, SLOT(close()));
140 }
141
142
143 void ICordViewer::slotDownloadRecording()
144 {
145     Recording * recording = lvRecordings->selectedRecording();
146     if(recording == 0) return;
147     if(downloadFile != 0) return;
148
149
150     QString proposedFilename =
151         QString("%1--%2.ts")
152         .arg(recording->name())
153         .arg(recording->dateTime().toString(Qt::ISODate));
154
155     if(currentOperatingSystem == Windows) {
156         proposedFilename.replace(':', '_');
157     }
158
159     QString filename = QFileDialog::getSaveFileName(this,
160                        tr("Select location to save %1, %2 MB", "@window")
161                        .arg(recording->toString()).arg(recording->size() / (1024 * 1024)),
162                        proposedFilename,
163                        tr("Video files (*.ts)"));
164     if(filename.isEmpty()) return;
165
166     downloadFile = new QFile(filename);
167     downloadFile->open(QIODevice::WriteOnly);
168     ftp->getFile(recording->recordingUrlInfo().name(), downloadFile);
169
170     if(downloadProgress != 0) {
171         delete downloadProgress;
172     }
173     downloadProgress = new DownloadProgressDialog(this);
174     downloadProgress->setFilename(recording->toString());
175     downloadProgress->setFileSize(recording->size());
176     connect(downloadProgress, SIGNAL(canceled()), ftp, SLOT(abort()));
177 }
178
179
180 void ICordViewer::slotUpdateDownloadProgress(const QString& filename, qint64 done, qint64 total)
181 {
182     if(downloadProgress == 0) return;
183     downloadProgress->setValue(done);
184 }
185
186 void ICordViewer::slotDownloadDone(const QString& filename)
187 {
188     statusBar()->showMessage(tr("Download finished.", "@message"), 5000);
189     if(downloadProgress != 0) {
190         delete downloadProgress;
191         downloadProgress = 0;
192     }
193
194     if(downloadFile != 0) {
195         downloadFile->close();
196         delete downloadFile;
197         downloadFile = 0;
198     }
199 }
200
201 void ICordViewer::slotRecordingRemovalStarted(const Recording* recording)
202 {
203     m_progress->setLabelText(tr("Deleting %1 - %2", "@message")
204                              .arg(recording->name())
205                              .arg(recording->dateTime().toString(Qt::DefaultLocaleShortDate))
206                             );
207 }
208
209 void ICordViewer::slotRecordingRemoved(const Recording* recording)
210 {
211     recordings.erase(recordings.find(recording->hash()));
212     lvRecordings->setRecordings(recordings, filter->criteriaMatcher());
213     delete recording;
214
215     m_progress->setValue(m_progress->value() + 1);
216
217     if(m_progress->value() == m_progress->maximum()) {
218         delete m_progress;
219         m_progress = 0;
220     }
221 }
222
223
224 void ICordViewer::slotRecordingListInfo(const QUrlInfo& urlInfo)
225 {
226     if(!urlInfo.isValid()) return;
227
228     QString hash(Recording::hash(urlInfo));
229     if(hash == "-") return;
230
231     Recording * recording = 0;
232     RecordingList::iterator itRecording = recordings.find(hash);
233     if(itRecording == recordings.end()) {
234         recording = new Recording();
235         recordings.insert(hash, recording);
236     } else {
237         recording = itRecording.value();
238     }
239
240     recording->setUrlInfo(urlInfo);
241
242     if(Recording::isPreviewDir(urlInfo)) {
243         ftp->listPreviews(urlInfo.name());
244     }
245 }
246
247 void ICordViewer::slotPreviewListInfo(const QString& previewsDir, const QUrlInfo& urlInfo)
248 {
249     RecordingList::iterator itRecording = recordings.find(Recording::hash(previewsDir));
250     if(itRecording == recordings.end()) return;
251
252     itRecording.value()->addPreviewUrlInfo(urlInfo);
253 }
254
255
256 void ICordViewer::slotFtpDone(bool result)
257 {
258     statusBar()->showMessage(tr("Ready.", "@message"));
259     lvRecordings->setRecordings(recordings, filter->criteriaMatcher());
260 }
261
262 void ICordViewer::clearRecordings()
263 {
264     lvRecordings->clearRecordings();
265     qDeleteAll(recordings);
266     recordings.clear();
267 }
268
269 void ICordViewer::slotEnableFilterWidget()
270 {
271     filter->setEnabled(recordings.count() > 0);
272 }
273
274 void ICordViewer::slotFilterChanged()
275 {
276     statusBar()->showMessage(filter->criteriaMatcher()->toString());
277     lvRecordings->setRecordings(recordings, filter->criteriaMatcher());
278 }
279
280 void ICordViewer::setupWidgets()
281 {
282     settings = new ConnectionSettingsWidget(this);
283     connect(settings, SIGNAL(hostnameChanged(QString)), ftp, SLOT(setHostname(QString)));
284     connect(settings, SIGNAL(pinChanged(QString)), ftp, SLOT(setPin(QString)));
285
286     filter = new FilterWidget(this);
287     filter->setEnabled(false);
288     connect(filter, SIGNAL(filterChanged()),
289             this, SLOT(slotFilterChanged()));
290
291     lvRecordings = new RecordingListView();
292     connect(lvRecordings, SIGNAL(listContentsChanged(bool)),
293             this, SLOT(slotEnableFilterWidget()));
294
295     QWidget * main = new QWidget();
296     setCentralWidget(main);
297     QVBoxLayout * mainLayout = new QVBoxLayout(main);
298     mainLayout->addWidget(settings);
299     mainLayout->addWidget(filter);
300     mainLayout->addWidget(lvRecordings);
301
302 }
303
304 void ICordViewer::setupMenusAndToolbars()
305 {
306     QList<QAction *> recodingActions;
307
308     QAction * aboutAction = new QAction(
309         QIcon(":/icons/video-television.png"),
310         tr("&About iCord Viewer", "@action"),
311         this);
312     addAction(aboutAction);
313     connect(aboutAction, SIGNAL(triggered(bool)), this, SLOT(slotAbout()));
314
315     QAction * quitAction = new QAction(
316         QIcon(":/icons/application-exit.png"),
317         tr("&Quit", "@action"),
318         this);
319     connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
320
321
322     QAction * readRecordingsAction = new QAction(
323         QIcon(":/icons/network-connect.png"),
324         tr("&Read recordings", "@action"),
325         this);
326     readRecordingsAction->setEnabled(false);
327     addAction(readRecordingsAction);
328     connect(readRecordingsAction, SIGNAL(triggered()),
329             this, SLOT(slotConnectClicked()));
330     connect(ftp, SIGNAL(credentialsChanged(bool)),
331             readRecordingsAction, SLOT(setEnabled(bool)));
332
333
334     QAction * downloadRecordingAction = new QAction(
335         QIcon(":/icons/download.png"),
336         tr("&Download", "@action"),
337         this);
338     downloadRecordingAction->setEnabled(false);
339     addAction(downloadRecordingAction);
340     recodingActions.append(downloadRecordingAction);
341     connect(downloadRecordingAction, SIGNAL(triggered(bool)),
342             this, SLOT(slotDownloadRecording()));
343     connect(lvRecordings, SIGNAL(selectionChanged(bool)),
344             downloadRecordingAction, SLOT(setEnabled(bool)));
345
346
347     QAction * deleteRecordingAction = new QAction(
348         QIcon(":/icons/edit-delete.png"),
349         tr("&Delete recording", "@action"),
350         this);
351     deleteRecordingAction->setEnabled(false);
352     addAction(deleteRecordingAction);
353     recodingActions.append(deleteRecordingAction);
354     connect(deleteRecordingAction, SIGNAL(triggered(bool)),
355             this, SLOT(slotDeleteSelectedRecording()));
356     connect(lvRecordings, SIGNAL(selectionChanged(bool)),
357             deleteRecordingAction, SLOT(setEnabled(bool)));
358
359     QAction * deleteRecordingsAction = new QAction(
360         QIcon(":/icons/edit-clear.png"),
361         tr("Delete &recordings", "@action"),
362         this);
363     deleteRecordingsAction->setEnabled(false);
364     addAction(deleteRecordingsAction);
365     connect(deleteRecordingsAction, SIGNAL(triggered(bool)),
366             this, SLOT(slotDeleteRecordings()));
367     connect(lvRecordings, SIGNAL(listContentsChanged(bool)),
368             deleteRecordingsAction, SLOT(setEnabled(bool)));
369
370     // Create menus
371     QMenu * fileMenu = new QMenu(tr("&File", "@menu"));
372     menuBar()->addMenu(fileMenu);
373     fileMenu->addAction(readRecordingsAction);
374     fileMenu->addAction(deleteRecordingsAction);
375     fileMenu->addSeparator();
376     fileMenu->addActions(recodingActions);
377     fileMenu->addSeparator();
378     fileMenu->addAction(quitAction);
379
380     QMenu * helpMenu = new QMenu(tr("&Help", "@menu"));
381     menuBar()->addMenu(helpMenu);
382     helpMenu->addAction(aboutAction);
383
384     // Create toolbars
385     QToolBar * toolbar = addToolBar(tr("Default", "@label"));
386     toolbar->addAction(readRecordingsAction);
387     toolbar->addAction(deleteRecordingsAction);
388     toolbar->addSeparator();
389     toolbar->addActions(recodingActions);
390
391     // Create context menus
392     lvRecordings->addActions(recodingActions);
393     lvRecordings->setContextMenuPolicy(Qt::ActionsContextMenu);
394 }
395
396
397
398
399 #include "icordviewer.moc"