582f34a2ab6ad4647dce07b9f59be97638d838c5
[toast/confclerk.git] / src / gui / mainwindow.cpp
1 /*
2  * Copyright (C) 2010 Ixonos Plc.
3  * Copyright (C) 2011-2012 Philipp Spitzer, gregor herrmann
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(int aEventId, QWidget *aParent)
54     : QMainWindow(aParent)
55     , conferenceModel(new ConferenceModel(this))
56     , mXmlParser(new ScheduleXmlParser(this))
57     , mNetworkAccessManager(new QNetworkAccessManager(this))
58 {
59     setupUi(this);
60
61     saved_title = windowTitle();
62
63 #ifdef N810
64     tabWidget->setTabText(1,"Favs");
65     //tabWidget->setTabText(2,"Day");
66 #endif
67
68     // first time run aplication: -> let's have it direct connection in this case
69     if(!AppSettings::contains("proxyIsDirectConnection"))
70         AppSettings::setDirectConnection(true);
71
72     /*
73     if(AppSettings::isDirectConnection())
74     {
75         qDebug() << "Setting-up proxy: " << AppSettings::proxyAddress() << ":" << AppSettings::proxyPort();
76     }
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     // open dialog for given Event ID
114     // this is used in case Alarm Dialog request application to start
115     if(aEventId)
116     {
117         try
118         {
119             EventDialog dialog(aEventId,this);
120             dialog.exec();
121         }
122         catch(OrmNoObjectException&) {} // just start application
123         catch(...) {} // just start application
124     }
125
126     connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
127
128     connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
129     connect(mXmlParser, SIGNAL(parsingScheduleEnd(const QString&)), conferenceModel, SLOT(newConferenceEnd(const QString&)));
130 }
131
132 void MainWindow::on_aboutAction_triggered()
133 {
134     QDialog dialog(this);
135     Ui::AboutDialog ui;
136     ui.setupUi(&dialog);
137     ui.labDescription->setText(ui.labDescription->text().arg(qApp->applicationVersion()));
138 #ifdef N810
139     dialog.setFixedWidth(width());
140 #endif
141     dialog.exec();
142 }
143
144
145 void MainWindow::on_reloadAction_triggered() {
146     int confId = Conference::activeConference();
147     if (confId== -1) return;
148     Conference active = Conference::getById(confId);
149     if (active.url().isEmpty()) return;
150     importFromNetwork(active.url());
151     setEnabled(false);
152 }
153
154
155 void MainWindow::on_nowAction_triggered() {
156     int confId = Conference::activeConference();
157     if (confId== -1) return;
158     dayNavigator->setCurDate(QDate::currentDate());
159     dayTabContainer->expandTimeGroup(QTime::currentTime(), confId);
160 }
161
162
163 void MainWindow::on_searchAction_triggered() {
164     if (tabWidget->currentWidget() == searchTab)
165         searchTabContainer->showSearchDialog(!searchTabContainer->searchDialogIsVisible());
166     else {
167         tabWidget->setCurrentWidget(searchTab);
168         searchTabContainer->showSearchDialog();
169     }
170 }
171
172
173 void MainWindow::on_expandAllAction_triggered() {
174
175 }
176
177
178 void MainWindow::on_collapseAllAction_triggered() {
179
180 }
181
182
183 void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
184     dayTabContainer->redisplayEvent(aEventId);
185     if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
186     else favsTabContainer->redisplayEvent(aEventId);
187     tracksTabContainer->redisplayEvent(aEventId);
188     roomsTabContainer->redisplayEvent(aEventId);
189     searchTabContainer->redisplayEvent(aEventId);
190 }
191
192
193 void MainWindow::onSearchResultChanged() {
194     // Are results found on the current date?
195     QDate date = dayNavigator->curDate();
196     int count = searchTabContainer->searchResultCount(date);
197     if (count > 0) {searchTabContainer->redisplayDate(date); return;}
198
199     // Are results found in the future?
200     for (date = date.addDays(1); date <= dayNavigator->endDate(); date = date.addDays(1)) {
201         int count = searchTabContainer->searchResultCount(date);
202         if (count > 0) {dayNavigator->setCurDate(date); return;}
203     }
204
205     // Are results found in the past?
206     for (date = dayNavigator->startDate(); date < dayNavigator->curDate(); date = date.addDays(1)) {
207         int count = searchTabContainer->searchResultCount(date);
208         if (count > 0) {dayNavigator->setCurDate(date); return;}
209     }
210     // No results were found
211     searchTabContainer->redisplayDate(dayNavigator->curDate());
212 }
213
214
215 void MainWindow::useConference(int id)
216 {
217     if (id == -1)  // in case no conference is active
218     {
219         unsetConference();
220         return;
221     }
222     try {
223         Conference::getById(Conference::activeConference()).update("active",0);
224         Conference new_active = Conference::getById(id);
225         new_active.update("active",1);
226
227         // looks like it does not work at n900
228         setWindowTitle(new_active.title());
229
230         // optimization.
231         // dont run initTabs() here
232         // it takes much CPU, making travelling between conferences in ConferenceEditor longer
233         // and is not seen in maemo WM anyway
234         // instead run it explicitly
235         // 1. at startup
236         // 2. when ConferenceEditor finished
237         // dont forget to protect the calls by try-catch!
238
239         // just in case, clear conference selection instead
240         clearTabs();
241
242         // end of optimization
243         // initTabs();
244     } catch (OrmException& e) {
245         // cannon set an active conference
246         unsetConference();   // TODO: as no active conference is now correctly managed this should be handled as a fatal error
247         return;
248     }
249
250 }
251
252 void MainWindow::initTabs()
253 {
254     int confId = Conference::activeConference();
255     if (confId != -1)   // only init tabs if a conference is active
256     {
257         Conference active = Conference::getById(confId);
258         QDate startDate = active.start();
259         QDate endDate = active.end();
260
261         // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
262         dayNavigator->setDates(startDate, endDate);
263     }
264 }
265
266 void MainWindow::clearTabs()
267 {
268     dayTabContainer->clearModel();
269     tracksTabContainer->clearModel();
270     roomsTabContainer->clearModel();
271     favsTabContainer->clearModel();
272     searchTabContainer->clearModel();
273 }
274
275 void MainWindow::unsetConference()
276 {
277     clearTabs();
278     dayNavigator->unsetDates();
279     setWindowTitle(saved_title);
280 }
281
282 void MainWindow::on_settingsAction_triggered()
283 {
284     SettingsDialog dialog;
285     dialog.loadDialogData();
286     if (dialog.exec() == QDialog::Accepted) {
287         dialog.saveDialogData();
288         QNetworkProxy proxy(
289                 AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
290                 AppSettings::proxyAddress(),
291                 AppSettings::proxyPort(),
292                 PROXY_USERNAME,
293                 PROXY_PASSWD);
294         QNetworkProxy::setApplicationProxy(proxy);
295     }
296 }
297
298 /** Create and run ConferenceEditor dialog, making required connections for it.
299
300 This method manages, which classes actually perform changes in conference list.
301
302 There are several classes that modify the conferences:
303 this:
304  deletion and URL update.
305 this, mXmlParser and mNetworkAccessManager:
306  addition and refresh.
307 */
308 void MainWindow::on_conferencesAction_triggered()
309 {
310     ConferenceEditor dialog(conferenceModel, this);
311
312     connect(&dialog, SIGNAL(haveConferenceUrl(const QString&)), SLOT(importFromNetwork(const QString&)));
313     connect(&dialog, SIGNAL(haveConferenceFile(const QString&)), SLOT(importFromFile(const QString&)));
314     connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
315     connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
316                     SLOT(changeConferenceUrl(int, const QString&)));
317
318     connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
319     connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
320
321     connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
322     connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
323     connect(mXmlParser, SIGNAL(parsingScheduleEnd(const QString&)), &dialog, SLOT(importFinished(const QString&)));
324
325     connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
326
327     dialog.exec();
328
329     // optimization, see useConference() code
330     try {
331         initTabs();
332     } catch (OrmException) {
333         clearTabs();
334     }
335 }
336
337 void MainWindow::networkQueryFinished(QNetworkReply *aReply)
338 {
339     if ( aReply->error() != QNetworkReply::NoError )
340     {
341         error_message(QString("Error occured during download: ") + aReply->errorString());
342     }
343     else
344     {
345         importData(aReply->readAll(), aReply->url().toEncoded());
346     }
347     setEnabled(true);
348 }
349
350 void MainWindow::importData(const QByteArray &aData, const QString& url)
351 {
352     mXmlParser->parseData(aData, url);
353 }
354
355 void MainWindow::importFromNetwork(const QString& url)
356 {
357     QNetworkRequest request;
358     request.setUrl(QUrl(url));
359
360     mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
361     mNetworkAccessManager->get(request);
362 }
363
364 void MainWindow::importFromFile(const QString& filename)
365 {
366     QFile file(filename);
367     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {    
368         static const QString format("Cannot read \"%1\": error %2");
369         error_message(format.arg(filename, QString::number(file.error())));
370     }
371
372     importData(file.readAll(), "");
373 }
374
375 void MainWindow::removeConference(int id)
376 {
377     Conference::deleteConference(id);
378     conferenceModel->conferenceRemoved();
379
380     emit conferenceRemoved();
381 }
382
383 void MainWindow::changeConferenceUrl(int id, const QString& url)
384 {
385     Conference::getById(id).setUrl(url);
386 }
387