2 * Copyright (C) 2010 Ixonos Plc.
3 * Copyright (C) 2011-2012 Philipp Spitzer, gregor herrmann, Stefan Stahl
5 * This file is part of ConfClerk.
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)
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
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/>.
20 #include "mainwindow.h"
24 #include <QNetworkProxy>
25 #include <QNetworkAccessManager>
26 #include <QNetworkReply>
28 #include "sqlengine.h"
31 #include "eventmodel.h"
34 #include "conference.h"
37 #include <QMessageBox>
40 #include "eventdialog.h"
41 #include "daynavigatorwidget.h"
42 #include "settingsdialog.h"
43 #include "conferenceeditor.h"
44 #include "schedulexmlparser.h"
45 #include "errormessage.h"
47 #include "tabcontainer.h"
48 #include "appsettings.h"
50 const QString PROXY_USERNAME;
51 const QString PROXY_PASSWD;
53 MainWindow::MainWindow(QWidget* parent): QMainWindow(parent) {
57 sqlEngine = new SqlEngine(this);
58 searchTabContainer->setSqlEngine(sqlEngine);
59 connect(sqlEngine, SIGNAL(dbError(QString)), this, SLOT(showError(QString)));
61 sqlEngine->createOrUpdateDbSchema();
63 conferenceModel = new ConferenceModel(this);
64 mXmlParser = new ScheduleXmlParser(sqlEngine, this);
65 mNetworkAccessManager = new QNetworkAccessManager(this);
66 systemTrayIcon = new QSystemTrayIcon(qApp->windowIcon(), this);
67 alarmTimer = new QTimer(this);
69 alarmTimer->setInterval(60000);
71 saved_title = windowTitle();
74 tabWidget->setTabText(1,"Favs");
75 //tabWidget->setTabText(2,"Day");
78 // first time run aplication: -> let's have it direct connection in this case
79 if(!AppSettings::contains("proxyIsDirectConnection"))
80 AppSettings::setDirectConnection(true);
83 AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
84 AppSettings::proxyAddress(),
85 AppSettings::proxyPort(),
88 QNetworkProxy::setApplicationProxy(proxy);
90 // event details have changed
91 connect(dayTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
92 connect(favsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
93 connect(tracksTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
94 connect(roomsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
95 connect(searchTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
98 connect(dayNavigator, SIGNAL(dateChanged(QDate)), dayTabContainer, SLOT(redisplayDate(QDate)));
99 connect(dayNavigator, SIGNAL(dateChanged(QDate)), favsTabContainer, SLOT(redisplayDate(QDate)));
100 connect(dayNavigator, SIGNAL(dateChanged(QDate)), tracksTabContainer, SLOT(redisplayDate(QDate)));
101 connect(dayNavigator, SIGNAL(dateChanged(QDate)), roomsTabContainer, SLOT(redisplayDate(QDate)));
102 connect(dayNavigator, SIGNAL(dateChanged(QDate)), searchTabContainer, SLOT(redisplayDate(QDate)));
104 // search result has changed
105 connect(searchTabContainer, SIGNAL(searchResultChanged()), SLOT(onSearchResultChanged()));
108 connect(systemTrayIcon, SIGNAL(messageClicked()), SLOT(onSystemTrayMessageClicked()));
111 connect(alarmTimer, SIGNAL(timeout()), SLOT(onAlarmTimerTimeout()));
113 useConference(Conference::activeConference());
114 // optimization, see useConference() code
117 } catch (const OrmException& e) {
118 qDebug() << "OrmException:" << e.text();
122 connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
123 connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
124 connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), conferenceModel, SLOT(newConferenceEnd(int)));
128 void MainWindow::on_aboutAction_triggered()
130 QDialog dialog(this);
133 ui.labDescription->setText(ui.labDescription->text().arg(qApp->applicationVersion()));
135 dialog.setFixedWidth(width());
141 void MainWindow::on_reloadAction_triggered() {
142 int confId = Conference::activeConference();
143 if (confId== -1) return;
144 Conference active = Conference::getById(confId);
145 if (active.url().isEmpty()) return;
146 importFromNetwork(active.url(), confId);
151 void MainWindow::on_nowAction_triggered() {
152 int confId = Conference::activeConference();
153 if (confId== -1) return;
154 dayNavigator->setCurDate(QDate::currentDate());
155 dayTabContainer->expandTimeGroup(QTime::currentTime(), confId);
159 void MainWindow::on_searchAction_triggered() {
160 if (tabWidget->currentWidget() == searchTab)
161 searchTabContainer->showSearchDialog(!searchTabContainer->searchDialogIsVisible());
163 tabWidget->setCurrentWidget(searchTab);
164 searchTabContainer->showSearchDialog();
169 void MainWindow::on_expandAllAction_triggered() {
170 if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->expandAll();
171 if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->expandAll();
172 if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->expandAll();
173 if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->expandAll();
174 if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->expandAll();
178 void MainWindow::on_collapseAllAction_triggered() {
179 if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->collapseAll();
180 if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->collapseAll();
181 if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->collapseAll();
182 if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->collapseAll();
183 if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->collapseAll();
187 void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
188 dayTabContainer->redisplayEvent(aEventId);
189 if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
190 else favsTabContainer->redisplayEvent(aEventId);
191 tracksTabContainer->redisplayEvent(aEventId);
192 roomsTabContainer->redisplayEvent(aEventId);
193 searchTabContainer->redisplayEvent(aEventId);
197 void MainWindow::onSearchResultChanged() {
198 // Are results found on the current date?
199 QDate date = dayNavigator->curDate();
200 int count = searchTabContainer->searchResultCount(date);
201 if (count > 0) {searchTabContainer->redisplayDate(date); return;}
203 // Are results found in the future?
204 for (date = date.addDays(1); date <= dayNavigator->endDate(); date = date.addDays(1)) {
205 int count = searchTabContainer->searchResultCount(date);
206 if (count > 0) {dayNavigator->setCurDate(date); return;}
209 // Are results found in the past?
210 for (date = dayNavigator->startDate(); date < dayNavigator->curDate(); date = date.addDays(1)) {
211 int count = searchTabContainer->searchResultCount(date);
212 if (count > 0) {dayNavigator->setCurDate(date); return;}
214 // No results were found
215 searchTabContainer->redisplayDate(dayNavigator->curDate());
219 void MainWindow::onSystemTrayMessageClicked() {
224 void MainWindow::onAlarmTimerTimeout() {
225 // TODO: Show Message if an alarm is set on an event that's starting soon and delete the corresponding alarm.
227 systemTrayIcon->show();
228 systemTrayIcon->showMessage("title", "message", QSystemTrayIcon::Information, 1000);
229 QApplication::alert(this);
230 QApplication::beep();
235 void MainWindow::useConference(int conferenceId)
237 if (conferenceId == -1) // in case no conference is active
243 int oldActiveConferenceId = Conference::activeConference();
244 bool switchActiveConference = conferenceId != oldActiveConferenceId;
245 if (switchActiveConference) Conference::getById(oldActiveConferenceId).update("active", 0);
246 Conference activeConference = Conference::getById(conferenceId);
247 if (switchActiveConference) activeConference.update("active",1);
249 // looks like it does not work at n900
250 setWindowTitle(activeConference.title());
253 // dont run initTabs() here
254 // it takes much CPU, making travelling between conferences in ConferenceEditor longer
255 // and is not seen in maemo WM anyway
256 // instead run it explicitly
258 // 2. when ConferenceEditor finished
259 // dont forget to protect the calls by try-catch!
261 // just in case, clear conference selection instead
264 // end of optimization
266 } catch (OrmException& e) {
267 // cannon set an active conference
268 unsetConference(); // TODO: as no active conference is now correctly managed this should be handled as a fatal error
274 void MainWindow::initTabs()
276 int confId = Conference::activeConference();
277 if (confId != -1) // only init tabs if a conference is active
279 Conference active = Conference::getById(confId);
280 QDate startDate = active.start();
281 QDate endDate = active.end();
283 // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
284 dayNavigator->setDates(startDate, endDate);
285 nowAction->trigger();
289 void MainWindow::clearTabs()
291 dayTabContainer->clearModel();
292 tracksTabContainer->clearModel();
293 roomsTabContainer->clearModel();
294 favsTabContainer->clearModel();
295 searchTabContainer->clearModel();
298 void MainWindow::unsetConference()
301 dayNavigator->unsetDates();
302 setWindowTitle(saved_title);
306 void MainWindow::showError(const QString& message) {
307 error_message(message);
311 void MainWindow::on_settingsAction_triggered()
313 SettingsDialog dialog;
314 dialog.loadDialogData();
315 if (dialog.exec() == QDialog::Accepted) {
316 dialog.saveDialogData();
318 AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
319 AppSettings::proxyAddress(),
320 AppSettings::proxyPort(),
323 QNetworkProxy::setApplicationProxy(proxy);
327 /** Create and run ConferenceEditor dialog, making required connections for it.
329 This method manages, which classes actually perform changes in conference list.
331 There are several classes that modify the conferences:
333 deletion and URL update.
334 this, mXmlParser and mNetworkAccessManager:
335 addition and refresh.
337 void MainWindow::on_conferencesAction_triggered()
339 ConferenceEditor dialog(conferenceModel, this);
341 connect(&dialog, SIGNAL(haveConferenceUrl(const QString&, int)), SLOT(importFromNetwork(const QString&, int)));
342 connect(&dialog, SIGNAL(haveConferenceFile(const QString&, int)), SLOT(importFromFile(const QString&, int)));
343 connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
344 connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
345 SLOT(changeConferenceUrl(int, const QString&)));
347 connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
348 connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
350 connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
351 connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
352 connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), &dialog, SLOT(importFinished(int)));
354 connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
358 // optimization, see useConference() code
361 } catch (OrmException) {
366 void MainWindow::networkQueryFinished(QNetworkReply *aReply) {
367 if (aReply->error() != QNetworkReply::NoError) {
368 error_message(QString("Error occured during download: ") + aReply->errorString());
370 QUrl redirectUrl = aReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
371 if (!redirectUrl.isEmpty()) {
372 if (redirectUrl != aReply->request().url()) {
373 importFromNetwork(redirectUrl.toString(), aReply->request().attribute(QNetworkRequest::User).toInt());
374 return; // don't enable controls
376 error_message(QString("Error: Cyclic redirection from %1 to itself.").arg(redirectUrl.toString()));
379 importData(aReply->readAll(), aReply->url().toEncoded(), aReply->request().attribute(QNetworkRequest::User).toInt());
385 void MainWindow::importData(const QByteArray &aData, const QString& url, int conferenceId)
387 mXmlParser->parseData(aData, url, conferenceId);
390 void MainWindow::importFromNetwork(const QString& url, int conferenceId)
392 QNetworkRequest request;
393 request.setUrl(QUrl(url));
394 request.setAttribute(QNetworkRequest::User, conferenceId);
396 mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
397 mNetworkAccessManager->get(request);
400 void MainWindow::importFromFile(const QString& filename, int conferenceId)
402 QFile file(filename);
403 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
404 static const QString format("Cannot read \"%1\": error %2");
405 error_message(format.arg(filename, QString::number(file.error())));
408 importData(file.readAll(), "", conferenceId);
412 void MainWindow::removeConference(int id) {
413 sqlEngine->deleteConference(id);
414 conferenceModel->conferenceRemoved();
415 emit conferenceRemoved();
419 void MainWindow::changeConferenceUrl(int id, const QString& url) {
420 Conference::getById(id).setUrl(url);