source: confclerk_git/src/gui/mainwindow.cpp @ 4d8493b

qt5
Last change on this file since 4d8493b was 4d8493b, checked in by Philipp Spitzer <philipp@…>, 9 years ago

Made sure the mainwindow is destroyed properly and the sql database is closed.

  • Property mode set to 100644
File size: 16.0 KB
Line 
1/*
2 * Copyright (C) 2010 Ixonos Plc.
3 * Copyright (C) 2011-2013 Philipp Spitzer, gregor herrmann, Stefan Stahl
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#include "room.h"
34
35#include "conference.h"
36
37#include <QDialog>
38#include <QMessageBox>
39
40#include "ui_about.h"
41#include "eventdialog.h"
42#include "daynavigatorwidget.h"
43#include "settingsdialog.h"
44#include "conferenceeditor.h"
45#include "schedulexmlparser.h"
46#include "errormessage.h"
47
48#include "tabcontainer.h"
49#include "appsettings.h"
50
51const QString PROXY_USERNAME;
52const QString PROXY_PASSWD;
53
54MainWindow::MainWindow(QWidget* parent): QMainWindow(parent) {
55    setupUi(this);
56
57    // Open database
58    sqlEngine = new SqlEngine(this);
59    searchTabContainer->setSqlEngine(sqlEngine);
60    connect(sqlEngine, SIGNAL(dbError(QString)), this, SLOT(showError(QString)));
61    sqlEngine->open();
62    sqlEngine->createOrUpdateDbSchema();
63
64    conferenceModel = new ConferenceModel(this);
65    mXmlParser = new ScheduleXmlParser(sqlEngine, this);
66    mNetworkAccessManager = new QNetworkAccessManager(this);
67    systemTrayIcon = new QSystemTrayIcon(qApp->windowIcon(), this);
68    alarmTimer = new QTimer(this);
69
70    alarmTimer->setInterval(60000);
71    alarmTimer->start();
72    saved_title = windowTitle();
73
74#ifdef N810
75    tabWidget->setTabText(1,"Favs");
76    //tabWidget->setTabText(2,"Day");
77#endif
78
79    // first time run aplication: -> let's have it direct connection in this case
80    if(!AppSettings::contains("proxyIsDirectConnection"))
81        AppSettings::setDirectConnection(true);
82
83    QNetworkProxy proxy(
84            AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
85            AppSettings::proxyAddress(),
86            AppSettings::proxyPort(),
87            PROXY_USERNAME,
88            PROXY_PASSWD);
89    QNetworkProxy::setApplicationProxy(proxy);
90
91    // event details have changed
92    connect(dayTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
93    connect(favsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
94    connect(tracksTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
95    connect(roomsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
96    connect(searchTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
97
98    // date has changed
99    connect(dayNavigator, SIGNAL(dateChanged(QDate)), dayTabContainer, SLOT(redisplayDate(QDate)));
100    connect(dayNavigator, SIGNAL(dateChanged(QDate)), favsTabContainer, SLOT(redisplayDate(QDate)));
101    connect(dayNavigator, SIGNAL(dateChanged(QDate)), tracksTabContainer, SLOT(redisplayDate(QDate)));
102    connect(dayNavigator, SIGNAL(dateChanged(QDate)), roomsTabContainer, SLOT(redisplayDate(QDate)));
103    connect(dayNavigator, SIGNAL(dateChanged(QDate)), searchTabContainer, SLOT(redisplayDate(QDate)));
104
105    // search result has changed
106    connect(searchTabContainer, SIGNAL(searchResultChanged()), SLOT(onSearchResultChanged()));
107
108    // systm tray icon
109    connect(systemTrayIcon, SIGNAL(messageClicked()), SLOT(onSystemTrayMessageClicked()));
110    connect(systemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(onSystemTrayMessageClicked()));
111
112    // timer
113    connect(alarmTimer, SIGNAL(timeout()), SLOT(onAlarmTimerTimeout()));
114
115    useConference(Conference::activeConference());
116    // optimization, see useConference() code
117    try {
118        initTabs();
119    } catch (const OrmException& e) {
120        qDebug() << "OrmException:" << e.text();
121        clearTabs();
122    }
123
124    connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
125    connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
126    connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), conferenceModel, SLOT(newConferenceEnd(int)));
127}
128
129
130MainWindow::~MainWindow() {
131    sqlEngine->close();
132}
133
134
135void MainWindow::on_aboutAction_triggered()
136{
137    QDialog dialog(this);
138    Ui::AboutDialog ui;
139    ui.setupUi(&dialog);
140    ui.labDescription->setText(ui.labDescription->text().arg(qApp->applicationVersion()));
141#ifdef N810
142    dialog.setFixedWidth(width());
143#endif
144    dialog.exec();
145}
146
147
148void MainWindow::on_reloadAction_triggered() {
149    int confId = Conference::activeConference();
150    if (confId== -1) return;
151    Conference active = Conference::getById(confId);
152    if (active.url().isEmpty()) return;
153    importFromNetwork(active.url(), confId);
154    setEnabled(false);
155}
156
157
158void MainWindow::on_nowAction_triggered() {
159    int confId = Conference::activeConference();
160    if (confId== -1) return;
161    dayNavigator->setCurDate(QDate::currentDate());
162    dayTabContainer->expandTimeGroup(QTime::currentTime(), confId);
163}
164
165
166void MainWindow::on_searchAction_triggered() {
167    if (tabWidget->currentWidget() == searchTab)
168        searchTabContainer->showSearchDialog(!searchTabContainer->searchDialogIsVisible());
169    else {
170        tabWidget->setCurrentWidget(searchTab);
171        searchTabContainer->showSearchDialog();
172    }
173}
174
175
176void MainWindow::on_expandAllAction_triggered() {
177    if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->expandAll();
178    if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->expandAll();
179    if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->expandAll();
180    if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->expandAll();
181    if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->expandAll();
182}
183
184
185void MainWindow::on_collapseAllAction_triggered() {
186    if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->collapseAll();
187    if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->collapseAll();
188    if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->collapseAll();
189    if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->collapseAll();
190    if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->collapseAll();
191}
192
193
194void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
195    dayTabContainer->redisplayEvent(aEventId);
196    if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
197    else favsTabContainer->redisplayEvent(aEventId);
198    tracksTabContainer->redisplayEvent(aEventId);
199    roomsTabContainer->redisplayEvent(aEventId);
200    searchTabContainer->redisplayEvent(aEventId);
201}
202
203
204void MainWindow::onSearchResultChanged() {
205    // Are results found on the current date?
206    QDate date = dayNavigator->curDate();
207    int count = searchTabContainer->searchResultCount(date);
208    if (count > 0) {searchTabContainer->redisplayDate(date); return;}
209
210    // Are results found in the future?
211    for (date = date.addDays(1); date <= dayNavigator->endDate(); date = date.addDays(1)) {
212        int count = searchTabContainer->searchResultCount(date);
213        if (count > 0) {dayNavigator->setCurDate(date); return;}
214    }
215
216    // Are results found in the past?
217    for (date = dayNavigator->startDate(); date < dayNavigator->curDate(); date = date.addDays(1)) {
218        int count = searchTabContainer->searchResultCount(date);
219        if (count > 0) {dayNavigator->setCurDate(date); return;}
220    }
221    // No results were found
222    searchTabContainer->redisplayDate(dayNavigator->curDate());
223}
224
225
226void MainWindow::onSystemTrayMessageClicked() {
227    systemTrayIcon->hide();
228}
229
230
231void MainWindow::onAlarmTimerTimeout() {
232    // determine if an alarm is set on an event that's starting soon
233    QList<Event> events = Event::getImminentAlarmEvents(AppSettings::preEventAlarmSec(), Conference::activeConference());
234    if (events.empty()) return;
235
236    // build a message string
237    Event event;
238    QString title;
239    QString message;
240    if (events.size() == 1) {
241        event = events.first();
242        title = tr("Next event at %1").arg(event.start().toString("HH:mm"));
243        message = tr("\"%1\"\n(%2)").arg(event.title()).arg(event.room()->name());
244    } else {
245        title = tr("%1 upcoming events").arg(events.size());
246        QStringList messages;
247        foreach (event, events) {
248            messages += tr("%1: \"%2\" (%3)").arg(event.start().toString("HH:mm")).arg(event.title()).arg(event.room()->name());
249        }
250        message = messages.join("\n");
251    }
252
253    // and delete the corresponding alarm
254    foreach (event, events) {
255        event.setHasAlarm(false);
256        event.update("alarm");
257        onEventChanged(event.id(), false);
258    }
259
260    // show message
261    systemTrayIcon->show();
262    // The next two lines are to prevent a very strange position of the message box the first time at X11/aweseome (not Win32/XP)
263    systemTrayIcon->showMessage("ConfClerk", "Your upcoming events", QSystemTrayIcon::Information);
264    qApp->processEvents();
265    systemTrayIcon->showMessage(title, message, QSystemTrayIcon::Information, 60*60*24*1000);
266    QApplication::alert(this);
267    QApplication::beep();
268}
269
270
271void MainWindow::useConference(int conferenceId)
272{
273    if (conferenceId == -1)  // in case no conference is active
274    {
275        unsetConference();
276        return;
277    }
278    try {
279        int oldActiveConferenceId = Conference::activeConference();
280        bool switchActiveConference = conferenceId != oldActiveConferenceId;
281        if (switchActiveConference) Conference::getById(oldActiveConferenceId).update("active", 0);
282        Conference activeConference = Conference::getById(conferenceId);
283        if (switchActiveConference) activeConference.update("active",1);
284
285        // looks like it does not work at n900
286        setWindowTitle(activeConference.title());
287
288        // optimization.
289        // dont run initTabs() here
290        // it takes much CPU, making travelling between conferences in ConferenceEditor longer
291        // and is not seen in maemo WM anyway
292        // instead run it explicitly
293        // 1. at startup
294        // 2. when ConferenceEditor finished
295        // dont forget to protect the calls by try-catch!
296
297        // just in case, clear conference selection instead
298        clearTabs();
299
300        // end of optimization
301        // initTabs();
302    } catch (OrmException& e) {
303        // cannon set an active conference
304        unsetConference();   // TODO: as no active conference is now correctly managed this should be handled as a fatal error
305        return;
306    }
307
308}
309
310void MainWindow::initTabs()
311{
312    int confId = Conference::activeConference();
313    if (confId != -1)   // only init tabs if a conference is active
314    {
315        Conference active = Conference::getById(confId);
316        QDate startDate = active.start();
317        QDate endDate = active.end();
318
319        // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
320        dayNavigator->setDates(startDate, endDate);
321        nowAction->trigger();
322    }
323}
324
325void MainWindow::clearTabs()
326{
327    dayTabContainer->clearModel();
328    tracksTabContainer->clearModel();
329    roomsTabContainer->clearModel();
330    favsTabContainer->clearModel();
331    searchTabContainer->clearModel();
332}
333
334void MainWindow::unsetConference()
335{
336    clearTabs();
337    dayNavigator->unsetDates();
338    setWindowTitle(saved_title);
339}
340
341
342void MainWindow::showError(const QString& message) {
343    error_message(message);
344}
345
346
347void MainWindow::on_settingsAction_triggered()
348{
349    SettingsDialog dialog;
350    dialog.loadDialogData();
351    if (dialog.exec() == QDialog::Accepted) {
352        dialog.saveDialogData();
353        QNetworkProxy proxy(
354                AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
355                AppSettings::proxyAddress(),
356                AppSettings::proxyPort(),
357                PROXY_USERNAME,
358                PROXY_PASSWD);
359        QNetworkProxy::setApplicationProxy(proxy);
360    }
361}
362
363/** Create and run ConferenceEditor dialog, making required connections for it.
364
365This method manages, which classes actually perform changes in conference list.
366
367There are several classes that modify the conferences:
368this:
369 deletion and URL update.
370this, mXmlParser and mNetworkAccessManager:
371 addition and refresh.
372*/
373void MainWindow::on_conferencesAction_triggered()
374{
375    ConferenceEditor dialog(conferenceModel, this);
376
377    connect(&dialog, SIGNAL(haveConferenceUrl(const QString&, int)), SLOT(importFromNetwork(const QString&, int)));
378    connect(&dialog, SIGNAL(haveConferenceFile(const QString&, int)), SLOT(importFromFile(const QString&, int)));
379    connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
380    connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
381                    SLOT(changeConferenceUrl(int, const QString&)));
382
383    connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
384    connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
385
386    connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
387    connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
388    connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), &dialog, SLOT(importFinished(int)));
389
390    connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
391
392    dialog.exec();
393
394    // optimization, see useConference() code
395    try {
396        initTabs();
397    } catch (OrmException) {
398        clearTabs();
399    }
400}
401
402void MainWindow::networkQueryFinished(QNetworkReply *aReply) {
403    if (aReply->error() != QNetworkReply::NoError) {
404        error_message(QString("Error occured during download: ") + aReply->errorString());
405    } else {
406        QUrl redirectUrl = aReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
407        if (!redirectUrl.isEmpty()) {
408            if (redirectUrl != aReply->request().url()) {
409                importFromNetwork(redirectUrl.toString(), aReply->request().attribute(QNetworkRequest::User).toInt());
410                return; // don't enable controls
411            } else {
412                error_message(QString("Error: Cyclic redirection from %1 to itself.").arg(redirectUrl.toString()));
413            }
414        } else {
415            importData(aReply->readAll(), aReply->url().toEncoded(), aReply->request().attribute(QNetworkRequest::User).toInt());
416        }
417    }
418    setEnabled(true);
419}
420
421void MainWindow::importData(const QByteArray &aData, const QString& url, int conferenceId)
422{
423    mXmlParser->parseData(aData, url, conferenceId);
424}
425
426void MainWindow::importFromNetwork(const QString& url, int conferenceId)
427{
428    QNetworkRequest request;
429    request.setUrl(QUrl(url));
430    request.setAttribute(QNetworkRequest::User, conferenceId);
431
432    mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
433    mNetworkAccessManager->get(request);
434}
435
436void MainWindow::importFromFile(const QString& filename, int conferenceId)
437{
438    QFile file(filename);
439    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {   
440        static const QString format("Cannot read \"%1\": error %2");
441        error_message(format.arg(filename, QString::number(file.error())));
442    }
443
444    importData(file.readAll(), "", conferenceId);
445}
446
447
448void MainWindow::removeConference(int id) {
449    sqlEngine->deleteConference(id);
450    conferenceModel->conferenceRemoved();
451    emit conferenceRemoved();
452}
453
454
455void MainWindow::changeConferenceUrl(int id, const QString& url) {
456    Conference::getById(id).setUrl(url);
457}
458
Note: See TracBrowser for help on using the repository browser.