Implemented stub for expand/collape all.
[toast/confclerk.git] / src / gui / mainwindow.cpp
1 /*
2  * Copyright (C) 2010 Ixonos Plc.
3  * Copyright (C) 2011 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     searchTabContainer->showSearchDialog();
165     tabWidget->setCurrentWidget(searchTab);
166 }
167
168
169 void MainWindow::on_expandAllAction_triggered() {
170
171 }
172
173
174 void MainWindow::on_collapseAllAction_triggered() {
175
176 }
177
178
179 void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
180     dayTabContainer->redisplayEvent(aEventId);
181     if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
182     else favsTabContainer->redisplayEvent(aEventId);
183     tracksTabContainer->redisplayEvent(aEventId);
184     roomsTabContainer->redisplayEvent(aEventId);
185     searchTabContainer->redisplayEvent(aEventId);
186 }
187
188
189 void MainWindow::onSearchResultChanged() {
190     // Are results found on the current date?
191     QDate date = dayNavigator->curDate();
192     int count = searchTabContainer->searchResultCount(date);
193     if (count > 0) {searchTabContainer->redisplayDate(date); return;}
194
195     // Are results found in the future?
196     for (date = date.addDays(1); date <= dayNavigator->endDate(); date = date.addDays(1)) {
197         int count = searchTabContainer->searchResultCount(date);
198         if (count > 0) {dayNavigator->setCurDate(date); return;}
199     }
200
201     // Are results found in the past?
202     for (date = dayNavigator->startDate(); date < dayNavigator->curDate(); date = date.addDays(1)) {
203         int count = searchTabContainer->searchResultCount(date);
204         if (count > 0) {dayNavigator->setCurDate(date); return;}
205     }
206     // No results were found
207     searchTabContainer->redisplayDate(dayNavigator->curDate());
208 }
209
210
211 void MainWindow::useConference(int id)
212 {
213     if (id == -1)  // in case no conference is active
214     {
215         unsetConference();
216         return;
217     }
218     try {
219         Conference::getById(Conference::activeConference()).update("active",0);
220         Conference new_active = Conference::getById(id);
221         new_active.update("active",1);
222
223         // looks like it does not work at n900
224         setWindowTitle(new_active.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     }
260 }
261
262 void MainWindow::clearTabs()
263 {
264     dayTabContainer->clearModel();
265     tracksTabContainer->clearModel();
266     roomsTabContainer->clearModel();
267     favsTabContainer->clearModel();
268     searchTabContainer->clearModel();
269 }
270
271 void MainWindow::unsetConference()
272 {
273     clearTabs();
274     dayNavigator->unsetDates();
275     setWindowTitle(saved_title);
276 }
277
278 void MainWindow::on_settingsAction_triggered()
279 {
280     SettingsDialog dialog;
281     dialog.loadDialogData();
282     if (dialog.exec() == QDialog::Accepted) {
283         dialog.saveDialogData();
284         QNetworkProxy proxy(
285                 AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
286                 AppSettings::proxyAddress(),
287                 AppSettings::proxyPort(),
288                 PROXY_USERNAME,
289                 PROXY_PASSWD);
290         QNetworkProxy::setApplicationProxy(proxy);
291     }
292 }
293
294 /** Create and run ConferenceEditor dialog, making required connections for it.
295
296 This method manages, which classes actually perform changes in conference list.
297
298 There are several classes that modify the conferences:
299 this:
300  deletion and URL update.
301 this, mXmlParser and mNetworkAccessManager:
302  addition and refresh.
303 */
304 void MainWindow::on_conferencesAction_triggered()
305 {
306     ConferenceEditor dialog(conferenceModel, this);
307
308     connect(&dialog, SIGNAL(haveConferenceUrl(const QString&)), SLOT(importFromNetwork(const QString&)));
309     connect(&dialog, SIGNAL(haveConferenceFile(const QString&)), SLOT(importFromFile(const QString&)));
310     connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
311     connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
312                     SLOT(changeConferenceUrl(int, const QString&)));
313
314     connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
315     connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
316
317     connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
318     connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
319     connect(mXmlParser, SIGNAL(parsingScheduleEnd(const QString&)), &dialog, SLOT(importFinished(const QString&)));
320
321     connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
322
323     dialog.exec();
324
325     // optimization, see useConference() code
326     try {
327         initTabs();
328     } catch (OrmException) {
329         clearTabs();
330     }
331 }
332
333 void MainWindow::networkQueryFinished(QNetworkReply *aReply)
334 {
335     if ( aReply->error() != QNetworkReply::NoError )
336     {
337         error_message(QString("Error occured during download: ") + aReply->errorString());
338     }
339     else
340     {
341         importData(aReply->readAll(), aReply->url().toEncoded());
342     }
343     setEnabled(true);
344 }
345
346 void MainWindow::importData(const QByteArray &aData, const QString& url)
347 {
348     mXmlParser->parseData(aData, url);
349 }
350
351 void MainWindow::importFromNetwork(const QString& url)
352 {
353     QNetworkRequest request;
354     request.setUrl(QUrl(url));
355
356     mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
357     mNetworkAccessManager->get(request);
358 }
359
360 void MainWindow::importFromFile(const QString& filename)
361 {
362     QFile file(filename);
363     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {    
364         static const QString format("Cannot read \"%1\": error %2");
365         error_message(format.arg(filename, QString::number(file.error())));
366     }
367
368     importData(file.readAll(), "");
369 }
370
371 void MainWindow::removeConference(int id)
372 {
373     Conference::deleteConference(id);
374     conferenceModel->conferenceRemoved();
375
376     emit conferenceRemoved();
377 }
378
379 void MainWindow::changeConferenceUrl(int id, const QString& url)
380 {
381     Conference::getById(id).setUrl(url);
382 }
383