source: src/gui/mainwindow.cpp

Last change on this file was a5e1f50, checked in by gregor herrmann <gregor@…>, 6 months ago

Clean up #includes.

(qtcreator is helpful but only partially.)

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