]> ToastFreeware Gitweb - toast/confclerk.git/blob - src/gui/mainwindow.cpp
e055b6a573aaaa5cf4b9c4808a419b40cd09f266
[toast/confclerk.git] / src / gui / mainwindow.cpp
1 /*
2  * Copyright (C) 2010 Ixonos Plc.
3  * Copyright (C) 2011-2012 Philipp Spitzer, gregor herrmann, Stefan Stahl
4  *
5  * This file is part of ConfClerk.
6  *
7  * ConfClerk is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation, either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * ConfClerk is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * ConfClerk.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 #include "mainwindow.h"
21
22 #include <QTreeView>
23 #include <QFile>
24 #include <QNetworkProxy>
25 #include <QNetworkAccessManager>
26 #include <QNetworkReply>
27
28 #include "sqlengine.h"
29
30 #include "track.h"
31 #include "eventmodel.h"
32 #include "delegate.h"
33
34 #include "conference.h"
35
36 #include <QDialog>
37 #include <QMessageBox>
38
39 #include "ui_about.h"
40 #include "eventdialog.h"
41 #include "daynavigatorwidget.h"
42 #include "settingsdialog.h"
43 #include "conferenceeditor.h"
44 #include "schedulexmlparser.h"
45 #include "errormessage.h"
46
47 #include "tabcontainer.h"
48 #include "appsettings.h"
49
50 const QString PROXY_USERNAME;
51 const QString PROXY_PASSWD;
52
53 MainWindow::MainWindow(QWidget* parent): QMainWindow(parent) {
54     setupUi(this);
55
56     // Open database
57     sqlEngine = new SqlEngine(this);
58     searchTabContainer->setSqlEngine(sqlEngine);
59     connect(sqlEngine, SIGNAL(dbError(QString)), this, SLOT(showError(QString)));
60     sqlEngine->open();
61     sqlEngine->createOrUpdateDbSchema();
62
63     conferenceModel = new ConferenceModel(this);
64     mXmlParser = new ScheduleXmlParser(sqlEngine, this);
65     mNetworkAccessManager = new QNetworkAccessManager(this);
66
67     saved_title = windowTitle();
68
69 #ifdef N810
70     tabWidget->setTabText(1,"Favs");
71     //tabWidget->setTabText(2,"Day");
72 #endif
73
74     // first time run aplication: -> let's have it direct connection in this case
75     if(!AppSettings::contains("proxyIsDirectConnection"))
76         AppSettings::setDirectConnection(true);
77
78     QNetworkProxy proxy(
79             AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
80             AppSettings::proxyAddress(),
81             AppSettings::proxyPort(),
82             PROXY_USERNAME,
83             PROXY_PASSWD);
84     QNetworkProxy::setApplicationProxy(proxy);
85
86     // event details have changed
87     connect(dayTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
88     connect(favsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
89     connect(tracksTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
90     connect(roomsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
91     connect(searchTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
92
93     // date has changed
94     connect(dayNavigator, SIGNAL(dateChanged(QDate)), dayTabContainer, SLOT(redisplayDate(QDate)));
95     connect(dayNavigator, SIGNAL(dateChanged(QDate)), favsTabContainer, SLOT(redisplayDate(QDate)));
96     connect(dayNavigator, SIGNAL(dateChanged(QDate)), tracksTabContainer, SLOT(redisplayDate(QDate)));
97     connect(dayNavigator, SIGNAL(dateChanged(QDate)), roomsTabContainer, SLOT(redisplayDate(QDate)));
98     connect(dayNavigator, SIGNAL(dateChanged(QDate)), searchTabContainer, SLOT(redisplayDate(QDate)));
99
100     // search result has changed
101     connect(searchTabContainer, SIGNAL(searchResultChanged()), SLOT(onSearchResultChanged()));
102
103
104     useConference(Conference::activeConference());
105     // optimization, see useConference() code
106     try {
107         initTabs();
108     } catch (const OrmException& e) {
109         qDebug() << "OrmException:" << e.text();
110         clearTabs();
111     }
112
113     connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
114     connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
115     connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), conferenceModel, SLOT(newConferenceEnd(int)));
116 }
117
118 void MainWindow::on_aboutAction_triggered()
119 {
120     QDialog dialog(this);
121     Ui::AboutDialog ui;
122     ui.setupUi(&dialog);
123     ui.labDescription->setText(ui.labDescription->text().arg(qApp->applicationVersion()));
124 #ifdef N810
125     dialog.setFixedWidth(width());
126 #endif
127     dialog.exec();
128 }
129
130
131 void MainWindow::on_reloadAction_triggered() {
132     int confId = Conference::activeConference();
133     if (confId== -1) return;
134     Conference active = Conference::getById(confId);
135     if (active.url().isEmpty()) return;
136     importFromNetwork(active.url(), confId);
137     setEnabled(false);
138 }
139
140
141 void MainWindow::on_nowAction_triggered() {
142     int confId = Conference::activeConference();
143     if (confId== -1) return;
144     dayNavigator->setCurDate(QDate::currentDate());
145     dayTabContainer->expandTimeGroup(QTime::currentTime(), confId);
146 }
147
148
149 void MainWindow::on_searchAction_triggered() {
150     if (tabWidget->currentWidget() == searchTab)
151         searchTabContainer->showSearchDialog(!searchTabContainer->searchDialogIsVisible());
152     else {
153         tabWidget->setCurrentWidget(searchTab);
154         searchTabContainer->showSearchDialog();
155     }
156 }
157
158
159 void MainWindow::on_expandAllAction_triggered() {
160     if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->expandAll();
161     if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->expandAll();
162     if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->expandAll();
163     if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->expandAll();
164     if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->expandAll();
165 }
166
167
168 void MainWindow::on_collapseAllAction_triggered() {
169     if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->collapseAll();
170     if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->collapseAll();
171     if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->collapseAll();
172     if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->collapseAll();
173     if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->collapseAll();
174 }
175
176
177 void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
178     dayTabContainer->redisplayEvent(aEventId);
179     if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
180     else favsTabContainer->redisplayEvent(aEventId);
181     tracksTabContainer->redisplayEvent(aEventId);
182     roomsTabContainer->redisplayEvent(aEventId);
183     searchTabContainer->redisplayEvent(aEventId);
184 }
185
186
187 void MainWindow::onSearchResultChanged() {
188     // Are results found on the current date?
189     QDate date = dayNavigator->curDate();
190     int count = searchTabContainer->searchResultCount(date);
191     if (count > 0) {searchTabContainer->redisplayDate(date); return;}
192
193     // Are results found in the future?
194     for (date = date.addDays(1); date <= dayNavigator->endDate(); date = date.addDays(1)) {
195         int count = searchTabContainer->searchResultCount(date);
196         if (count > 0) {dayNavigator->setCurDate(date); return;}
197     }
198
199     // Are results found in the past?
200     for (date = dayNavigator->startDate(); date < dayNavigator->curDate(); date = date.addDays(1)) {
201         int count = searchTabContainer->searchResultCount(date);
202         if (count > 0) {dayNavigator->setCurDate(date); return;}
203     }
204     // No results were found
205     searchTabContainer->redisplayDate(dayNavigator->curDate());
206 }
207
208
209 void MainWindow::useConference(int conferenceId)
210 {
211     if (conferenceId == -1)  // in case no conference is active
212     {
213         unsetConference();
214         return;
215     }
216     try {
217         int oldActiveConferenceId = Conference::activeConference();
218         bool switchActiveConference = conferenceId != oldActiveConferenceId;
219         if (switchActiveConference) Conference::getById(oldActiveConferenceId).update("active", 0);
220         Conference activeConference = Conference::getById(conferenceId);
221         if (switchActiveConference) activeConference.update("active",1);
222
223         // looks like it does not work at n900
224         setWindowTitle(activeConference.title());
225
226         // optimization.
227         // dont run initTabs() here
228         // it takes much CPU, making travelling between conferences in ConferenceEditor longer
229         // and is not seen in maemo WM anyway
230         // instead run it explicitly
231         // 1. at startup
232         // 2. when ConferenceEditor finished
233         // dont forget to protect the calls by try-catch!
234
235         // just in case, clear conference selection instead
236         clearTabs();
237
238         // end of optimization
239         // initTabs();
240     } catch (OrmException& e) {
241         // cannon set an active conference
242         unsetConference();   // TODO: as no active conference is now correctly managed this should be handled as a fatal error
243         return;
244     }
245
246 }
247
248 void MainWindow::initTabs()
249 {
250     int confId = Conference::activeConference();
251     if (confId != -1)   // only init tabs if a conference is active
252     {
253         Conference active = Conference::getById(confId);
254         QDate startDate = active.start();
255         QDate endDate = active.end();
256
257         // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
258         dayNavigator->setDates(startDate, endDate);
259         nowAction->trigger();
260     }
261 }
262
263 void MainWindow::clearTabs()
264 {
265     dayTabContainer->clearModel();
266     tracksTabContainer->clearModel();
267     roomsTabContainer->clearModel();
268     favsTabContainer->clearModel();
269     searchTabContainer->clearModel();
270 }
271
272 void MainWindow::unsetConference()
273 {
274     clearTabs();
275     dayNavigator->unsetDates();
276     setWindowTitle(saved_title);
277 }
278
279
280 void MainWindow::showError(const QString& message) {
281     error_message(message);
282 }
283
284
285 void MainWindow::on_settingsAction_triggered()
286 {
287     SettingsDialog dialog;
288     dialog.loadDialogData();
289     if (dialog.exec() == QDialog::Accepted) {
290         dialog.saveDialogData();
291         QNetworkProxy proxy(
292                 AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
293                 AppSettings::proxyAddress(),
294                 AppSettings::proxyPort(),
295                 PROXY_USERNAME,
296                 PROXY_PASSWD);
297         QNetworkProxy::setApplicationProxy(proxy);
298     }
299 }
300
301 /** Create and run ConferenceEditor dialog, making required connections for it.
302
303 This method manages, which classes actually perform changes in conference list.
304
305 There are several classes that modify the conferences:
306 this:
307  deletion and URL update.
308 this, mXmlParser and mNetworkAccessManager:
309  addition and refresh.
310 */
311 void MainWindow::on_conferencesAction_triggered()
312 {
313     ConferenceEditor dialog(conferenceModel, this);
314
315     connect(&dialog, SIGNAL(haveConferenceUrl(const QString&, int)), SLOT(importFromNetwork(const QString&, int)));
316     connect(&dialog, SIGNAL(haveConferenceFile(const QString&, int)), SLOT(importFromFile(const QString&, int)));
317     connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
318     connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
319                     SLOT(changeConferenceUrl(int, const QString&)));
320
321     connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
322     connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
323
324     connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
325     connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
326     connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), &dialog, SLOT(importFinished(int)));
327
328     connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
329
330     dialog.exec();
331
332     // optimization, see useConference() code
333     try {
334         initTabs();
335     } catch (OrmException) {
336         clearTabs();
337     }
338 }
339
340 void MainWindow::networkQueryFinished(QNetworkReply *aReply) {
341     if (aReply->error() != QNetworkReply::NoError) {
342         error_message(QString("Error occured during download: ") + aReply->errorString());
343     } else {
344         QUrl redirectUrl = aReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
345         if (!redirectUrl.isEmpty()) {
346             if (redirectUrl != aReply->request().url()) {
347                 importFromNetwork(redirectUrl.toString(), aReply->request().attribute(QNetworkRequest::User).toInt());
348                 return; // don't enable controls
349             } else {
350                 error_message(QString("Error: Cyclic redirection from %1 to itself.").arg(redirectUrl.toString()));
351             }
352         } else {
353             importData(aReply->readAll(), aReply->url().toEncoded(), aReply->request().attribute(QNetworkRequest::User).toInt());
354         }
355     }
356     setEnabled(true);
357 }
358
359 void MainWindow::importData(const QByteArray &aData, const QString& url, int conferenceId)
360 {
361     mXmlParser->parseData(aData, url, conferenceId);
362 }
363
364 void MainWindow::importFromNetwork(const QString& url, int conferenceId)
365 {
366     QNetworkRequest request;
367     request.setUrl(QUrl(url));
368     request.setAttribute(QNetworkRequest::User, conferenceId);
369
370     mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
371     mNetworkAccessManager->get(request);
372 }
373
374 void MainWindow::importFromFile(const QString& filename, int conferenceId)
375 {
376     QFile file(filename);
377     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {    
378         static const QString format("Cannot read \"%1\": error %2");
379         error_message(format.arg(filename, QString::number(file.error())));
380     }
381
382     importData(file.readAll(), "", conferenceId);
383 }
384
385
386 void MainWindow::removeConference(int id) {
387     sqlEngine->deleteConference(id);
388     conferenceModel->conferenceRemoved();
389     emit conferenceRemoved();
390 }
391
392
393 void MainWindow::changeConferenceUrl(int id, const QString& url) {
394     Conference::getById(id).setUrl(url);
395 }
396