reworked UI for conference editing
[toast/confclerk.git] / src / gui / mainwindow.cpp
1 /*
2  * Copyright (C) 2010 Ixonos Plc.
3  *
4  * This file is part of fosdem-schedule.
5  *
6  * fosdem-schedule is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * fosdem-schedule is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * fosdem-schedule.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "mainwindow.h"
20
21 #include <QTreeView>
22 #include <QFile>
23 #include <QNetworkProxy>
24 #include <QNetworkAccessManager>
25 #include <QNetworkReply>
26
27 #include <sqlengine.h>
28
29 #include <track.h>
30 #include <eventmodel.h>
31 #include <delegate.h>
32
33 #include <conference.h>
34
35 #include <QDialog>
36 #include <QMessageBox>
37
38 #include "ui_about.h"
39 #include <eventdialog.h>
40 #include "daynavigatorwidget.h"
41 #include "mapwindow.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     if(AppSettings::isDirectConnection())
73     {
74         qDebug() << "Setting-up proxy: " << AppSettings::proxyAddress() << ":" << AppSettings::proxyPort();
75     }
76     QNetworkProxy proxy(
77             AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
78             AppSettings::proxyAddress(),
79             AppSettings::proxyPort(),
80             PROXY_USERNAME,
81             PROXY_PASSWD);
82     QNetworkProxy::setApplicationProxy(proxy);
83
84     #if 0
85     // list of conferences must be maintained by ConferenceEditor
86     // here must be one of the signals from the closing ConferenceEditor (or model):
87     // selectedConf(conference), noConf()
88     connect(importScheduleWidget, SIGNAL(scheduleImported(int)), SLOT(scheduleImported(int)));
89     connect(importScheduleWidget, SIGNAL(scheduleDeleted(const QString&)), SLOT(scheduleDeleted(const QString&)));
90     #endif
91
92     // event details have changed
93     connect(dayTabContainer, SIGNAL(eventHasChanged(int,bool)), SLOT(eventHasChanged(int,bool)));
94     connect(favsTabContainer, SIGNAL(eventHasChanged(int,bool)), SLOT(eventHasChanged(int,bool)));
95     connect(tracksTabContainer, SIGNAL(eventHasChanged(int,bool)), SLOT(eventHasChanged(int,bool)));
96     connect(roomsTabContainer, SIGNAL(eventHasChanged(int,bool)), SLOT(eventHasChanged(int,bool)));
97     connect(nowTabContainer, SIGNAL(eventHasChanged(int,bool)), SLOT(eventHasChanged(int,bool)));
98     connect(searchTabContainer, SIGNAL(eventHasChanged(int,bool)), SLOT(eventHasChanged(int,bool)));
99
100     // event conference map button clicked
101     #if 0
102     // TODO: think about it when return to maps
103     connect(showMapButton, SIGNAL(clicked()), SLOT(conferenceMapClicked()));
104     #endif
105
106     connect(aboutAction, SIGNAL(triggered()), SLOT(aboutApp()));
107     connect(settingsAction, SIGNAL(triggered()), SLOT(setup()));
108     connect(conferencesAction, SIGNAL(triggered()), SLOT(showConferences()));
109
110     useConference(Conference::activeConference());
111
112     #if 0
113     // TODO: remove GUI
114     // initialisation of model and pick active conference from there and call conferenceChanged()
115     // selectConference->setDuplicatesEnabled(false);
116     int confCount = Conference::getAll().count();
117     if(confCount)
118     {
119         initTabs();
120         // fillAndShowConferenceHeader();
121         setWindowTitle(Conference::getById(confId).title());
122
123         QList<Conference> confs = Conference::getAll();
124         QListIterator<Conference> i(confs);
125         while(i.hasNext())
126         {
127             Conference conf = i.next();
128             // TODO: remove GUI
129             // selectConference->addItem(conf.title(),conf.id());
130         }
131         // TODO: remove GUI
132         // int idx = selectConference->findText(Conference::getById(Conference::activeConference()).title());
133         // selectConference->setCurrentIndex(idx);
134         // connect(selectConference, SIGNAL(currentIndexChanged(int)), SLOT(conferenceChanged(int)));
135         // conferenceChanged(idx);
136     }
137     else
138     {
139         // TODO: remove GUI
140         // conferenceHeader->hide();
141         // selectConferenceWidget->hide();
142         // // go to the 'conferenceTab', so the user can import the schedule
143         // tabWidget->setCurrentIndex(6); // 6 - conference tab
144     }
145     #endif
146
147     // open dialog for given Event ID
148     // this is used in case Alarm Dialog request application to start
149     if(aEventId)
150     {
151         try
152         {
153             EventDialog dialog(aEventId,this);
154             dialog.exec();
155         }
156         catch(OrmNoObjectException&) {} // just start application
157         catch(...) {} // just start application
158     }
159
160     connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
161
162     connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
163     connect(mXmlParser, SIGNAL(parsingScheduleEnd(const QString&)), conferenceModel, SLOT(newConferenceEnd(const QString&)));
164 }
165
166 void MainWindow::scheduleImported(int aConfId)
167 {
168     Q_UNUSED(aConfId);
169
170     // TODO: this all goes to ConferenceEditor and model of conferences
171     #if 0
172
173     Conference conf = Conference::getById(aConfId);
174     if( selectConference->findText(conf.title()) < 0 ) // item doesn't exist
175     {
176         disconnect(selectConference, SIGNAL(currentIndexChanged(int)), this, SLOT(conferenceChanged(int)));
177         selectConference->addItem(conf.title(),conf.id());
178         connect(selectConference, SIGNAL(currentIndexChanged(int)), SLOT(conferenceChanged(int)));
179     }
180     int confCount = Conference::getAll().count();
181     if(confCount)
182     {
183         int idx = selectConference->findText(conf.title());
184         selectConference->setCurrentIndex(idx);
185
186         selectConferenceWidget->show();
187
188         conferenceChanged(idx);
189     }
190     #endif
191 }
192
193 void MainWindow::scheduleDeleted(const QString& title)
194 {
195     Q_UNUSED(title);
196     // TODO: this all goes to ConferenceEditor and model of conferences
197     #if 0
198     int idx = selectConference->findText(title);
199
200     if (idx == -1) {
201         // should not happen
202         qWarning() << __PRETTY_FUNCTION__ << "removed non-existent item:" << title;
203         // this happens when you remove the only conference (the list is not ptoperly inited in this case)
204         // now make sure that conferencet
205         if (selectConference->count() > 0) {
206             selectConference->setCurrentIndex(0);
207             conferenceChanged(0);
208         } else {
209             conferenceChanged(-1);
210         }
211     } else {
212         // will it signal "changed"?
213         selectConference->removeItem(idx);
214     }
215     #endif
216 }
217
218 void MainWindow::aboutApp()
219 {
220     QDialog dialog(this);
221     Ui::AboutDialog ui;
222     ui.setupUi(&dialog);
223 #ifdef N810
224     dialog.setFixedWidth(width());
225 #endif
226     dialog.exec();
227 }
228
229 void MainWindow::conferenceMapClicked()
230 {
231     QString mapPath = QString(":/maps/campus.png");
232     if(!QFile::exists(mapPath))
233         mapPath = QString(":/maps/rooms/not-available.png");
234
235     QString roomName;
236
237     QPixmap map(mapPath);
238     MapWindow window(map,roomName,this);
239     window.exec();
240 }
241
242 void MainWindow::eventHasChanged(int aEventId, bool aReloadModel)
243 {
244     dayTabContainer->updateTreeViewModel(aEventId);
245     favsTabContainer->updateTreeViewModel(aEventId,aReloadModel);
246     tracksTabContainer->updateTreeViewModel(aEventId);
247     nowTabContainer->updateTreeViewModel(aEventId);
248     roomsTabContainer->updateTreeViewModel(aEventId);
249     searchTabContainer->updateTreeViewModel(aEventId);
250 }
251
252 void MainWindow::useConference(int id)
253 {
254     try {
255         Conference::getById(Conference::activeConference()).update("active",0);
256         Conference::getById(id).update("active",1);
257
258         initTabs();
259     } catch (OrmException& e) {
260         // cannon set an active conference
261         unsetConference();
262         return;
263     }
264
265 }
266
267 void MainWindow::initTabs()
268 {
269     int confId = Conference::activeConference();
270     Conference active = Conference::getById(confId);
271     QDate startDate = active.start();
272     QDate endDate = active.end();
273     setWindowTitle(active.title());
274
275     // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
276     dayTabContainer->setDates(startDate, endDate);
277     tracksTabContainer->setDates(startDate, endDate);
278     roomsTabContainer->setDates(startDate, endDate);
279     favsTabContainer->setDates(startDate, endDate);
280     searchTabContainer->setDates(startDate, endDate);
281     searchTabContainer->searchAgainClicked();
282     nowTabContainer->updateTreeView(QDate::currentDate());
283 }
284
285 void MainWindow::unsetConference()
286 {
287     dayTabContainer->clearModel();
288     tracksTabContainer->clearModel();
289     roomsTabContainer->clearModel();
290     favsTabContainer->clearModel();
291     searchTabContainer->clearModel();
292     searchTabContainer->searchAgainClicked();
293     nowTabContainer->clearModel();
294
295     // TODO:  remove
296     // conferenceHeader->hide();
297     setWindowTitle(saved_title);
298 }
299
300 void MainWindow::setup()
301 {
302     SettingsDialog dialog;
303     dialog.exec();
304
305     qDebug() << "Setting-up proxy: " << AppSettings::proxyAddress() << ":" << AppSettings::proxyPort();
306     QNetworkProxy proxy(
307             AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
308             AppSettings::proxyAddress(),
309             AppSettings::proxyPort(),
310             PROXY_USERNAME,
311             PROXY_PASSWD);
312     QNetworkProxy::setApplicationProxy(proxy);
313 }
314
315 /** Create and run ConferenceEditor dialog, making required connections for it.
316
317 This method manages, which classes actually perform changes in conference list.
318
319 There are several classes that modify the conferences:
320 this:
321  deletion and URL update.
322 this, mXmlParser and mNetworkAccessManager:
323  addition and refresh.
324 */
325 void MainWindow::showConferences()
326 {
327     ConferenceEditor dialog(conferenceModel, this);
328
329     // TODO: connect signals about progress of network and parsing
330
331     connect(&dialog, SIGNAL(haveConferenceUrl(const QString&)), SLOT(importFromNetwork(const QString&)));
332     connect(&dialog, SIGNAL(haveConferenceFile(const QString&)), SLOT(importFromFile(const QString&)));
333     connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
334     connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
335                     SLOT(changeConferenceUrl(int, const QString&)));
336
337     connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
338     connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
339
340     connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
341     connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
342     connect(mXmlParser, SIGNAL(parsingScheduleEnd(const QString&)), &dialog, SLOT(importFinished(const QString&)));
343
344     connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
345
346     dialog.exec();
347 }
348
349 void MainWindow::networkQueryFinished(QNetworkReply *aReply)
350 {
351     if ( aReply->error() != QNetworkReply::NoError )
352     {
353         error_message(QString("Error occured during download: ") + aReply->errorString());
354     }
355     else
356     {
357         qDebug() << __PRETTY_FUNCTION__ << ": have data";
358         importData(aReply->readAll(), aReply->url().toEncoded());
359     }
360 }
361
362 void MainWindow::importData(const QByteArray &aData, const QString& url)
363 {
364     // TODO: remove GUI
365     // instead send signals to the child dialog
366     #if 0
367     browse->hide();
368     online->hide();
369     progressBar->show();
370     // proxySettings->hide();
371     #endif
372
373     int confId = mXmlParser->parseData(aData, url);
374
375     #if 0
376     progressBar->hide();
377     browse->show();
378     online->show();
379     // proxySettings->show();
380     importScheduleLabel->setText("Schedule:");
381
382     #endif
383     if (confId > 0) {
384         emit(scheduleImported(confId));
385     }
386 }
387
388 void MainWindow::importFromNetwork(const QString& url)
389 {
390     qDebug() << __PRETTY_FUNCTION__;
391     QNetworkRequest request;
392     request.setUrl(QUrl(url));
393
394     mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
395     mNetworkAccessManager->get(request);
396 }
397
398 void MainWindow::importFromFile(const QString& filename)
399 {
400     qDebug() << __PRETTY_FUNCTION__;
401     QFile file(filename);
402     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {    
403         static const QString format("Cannot read \"%1\": error %2");
404         error_message(format.arg(filename, QString::number(file.error())));
405     }
406
407     importData(file.readAll(), "");
408 }
409
410 void MainWindow::removeConference(int id)
411 {
412     Conference::deleteConference(id);
413     conferenceModel->conferenceRemoved();
414
415     emit conferenceRemoved();
416 }
417
418 void MainWindow::changeConferenceUrl(int id, const QString& url)
419 {
420     Conference::getById(id).setUrl(url);
421 }
422