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() {
221 // systemTrayIcon->hide();
225 void MainWindow::onAlarmTimerTimeout() {
226 // TODO: Show Message if an alarm is set on an event that's starting soon and delete the corresponding alarm.
228 systemTrayIcon->show();
229 qDebug() << systemTrayIcon->geometry(); // gets moved to the correct location only at second usage
230 systemTrayIcon->showMessage("title", "message", QSystemTrayIcon::Information, 1000);
231 QApplication::alert(this);
232 QApplication::beep();
237 void MainWindow::useConference(int conferenceId)
239 if (conferenceId == -1) // in case no conference is active
245 int oldActiveConferenceId = Conference::activeConference();
246 bool switchActiveConference = conferenceId != oldActiveConferenceId;
247 if (switchActiveConference) Conference::getById(oldActiveConferenceId).update("active", 0);
248 Conference activeConference = Conference::getById(conferenceId);
249 if (switchActiveConference) activeConference.update("active",1);
251 // looks like it does not work at n900
252 setWindowTitle(activeConference.title());
255 // dont run initTabs() here
256 // it takes much CPU, making travelling between conferences in ConferenceEditor longer
257 // and is not seen in maemo WM anyway
258 // instead run it explicitly
260 // 2. when ConferenceEditor finished
261 // dont forget to protect the calls by try-catch!
263 // just in case, clear conference selection instead
266 // end of optimization
268 } catch (OrmException& e) {
269 // cannon set an active conference
270 unsetConference(); // TODO: as no active conference is now correctly managed this should be handled as a fatal error
276 void MainWindow::initTabs()
278 int confId = Conference::activeConference();
279 if (confId != -1) // only init tabs if a conference is active
281 Conference active = Conference::getById(confId);
282 QDate startDate = active.start();
283 QDate endDate = active.end();
285 // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
286 dayNavigator->setDates(startDate, endDate);
287 nowAction->trigger();
291 void MainWindow::clearTabs()
293 dayTabContainer->clearModel();
294 tracksTabContainer->clearModel();
295 roomsTabContainer->clearModel();
296 favsTabContainer->clearModel();
297 searchTabContainer->clearModel();
300 void MainWindow::unsetConference()
303 dayNavigator->unsetDates();
304 setWindowTitle(saved_title);
308 void MainWindow::showError(const QString& message) {
309 error_message(message);
313 void MainWindow::on_settingsAction_triggered()
315 SettingsDialog dialog;
316 dialog.loadDialogData();
317 if (dialog.exec() == QDialog::Accepted) {
318 dialog.saveDialogData();
320 AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
321 AppSettings::proxyAddress(),
322 AppSettings::proxyPort(),
325 QNetworkProxy::setApplicationProxy(proxy);
329 /** Create and run ConferenceEditor dialog, making required connections for it.
331 This method manages, which classes actually perform changes in conference list.
333 There are several classes that modify the conferences:
335 deletion and URL update.
336 this, mXmlParser and mNetworkAccessManager:
337 addition and refresh.
339 void MainWindow::on_conferencesAction_triggered()
341 ConferenceEditor dialog(conferenceModel, this);
343 connect(&dialog, SIGNAL(haveConferenceUrl(const QString&, int)), SLOT(importFromNetwork(const QString&, int)));
344 connect(&dialog, SIGNAL(haveConferenceFile(const QString&, int)), SLOT(importFromFile(const QString&, int)));
345 connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
346 connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
347 SLOT(changeConferenceUrl(int, const QString&)));
349 connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
350 connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
352 connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
353 connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
354 connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), &dialog, SLOT(importFinished(int)));
356 connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
360 // optimization, see useConference() code
363 } catch (OrmException) {
368 void MainWindow::networkQueryFinished(QNetworkReply *aReply) {
369 if (aReply->error() != QNetworkReply::NoError) {
370 error_message(QString("Error occured during download: ") + aReply->errorString());
372 QUrl redirectUrl = aReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
373 if (!redirectUrl.isEmpty()) {
374 if (redirectUrl != aReply->request().url()) {
375 importFromNetwork(redirectUrl.toString(), aReply->request().attribute(QNetworkRequest::User).toInt());
376 return; // don't enable controls
378 error_message(QString("Error: Cyclic redirection from %1 to itself.").arg(redirectUrl.toString()));
381 importData(aReply->readAll(), aReply->url().toEncoded(), aReply->request().attribute(QNetworkRequest::User).toInt());
387 void MainWindow::importData(const QByteArray &aData, const QString& url, int conferenceId)
389 mXmlParser->parseData(aData, url, conferenceId);
392 void MainWindow::importFromNetwork(const QString& url, int conferenceId)
394 QNetworkRequest request;
395 request.setUrl(QUrl(url));
396 request.setAttribute(QNetworkRequest::User, conferenceId);
398 mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
399 mNetworkAccessManager->get(request);
402 void MainWindow::importFromFile(const QString& filename, int conferenceId)
404 QFile file(filename);
405 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
406 static const QString format("Cannot read \"%1\": error %2");
407 error_message(format.arg(filename, QString::number(file.error())));
410 importData(file.readAll(), "", conferenceId);
414 void MainWindow::removeConference(int id) {
415 sqlEngine->deleteConference(id);
416 conferenceModel->conferenceRemoved();
417 emit conferenceRemoved();
421 void MainWindow::changeConferenceUrl(int id, const QString& url) {
422 Conference::getById(id).setUrl(url);