source: src/gui/mainwindow.cpp

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

Clean up #includes.

(qtcreator is helpful but only partially.)

  • Property mode set to 100644
File size: 17.8 KB
Line 
1/*
2 * Copyright (C) 2010 Ixonos Plc.
3 * Copyright (C) 2011-2021 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#include <QSslConfiguration>
28#include <QDialog>
29#include <QMessageBox>
30
31#include "sqlengine.h"
32#include "track.h"
33#include "eventmodel.h"
34#include "delegate.h"
35#include "room.h"
36#include "conference.h"
37#include "ui_about.h"
38#include "eventdialog.h"
39#include "daynavigatorwidget.h"
40#include "settingsdialog.h"
41#include "conferenceeditor.h"
42#include "schedulexmlparser.h"
43#include "errormessage.h"
44#include "tabcontainer.h"
45#include "appsettings.h"
46#include "application.h"
47
48MainWindow::MainWindow(QWidget* parent): QMainWindow(parent) {
49    setupUi(this);
50
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);
61    systemTrayIcon = new QSystemTrayIcon(qApp->windowIcon(), this);
62    alarmTimer = new QTimer(this);
63
64    alarmTimer->setInterval(60000);
65    alarmTimer->start();
66    saved_title = windowTitle();
67
68#ifdef N810
69    tabWidget->setTabText(1,"Favs");
70    //tabWidget->setTabText(2,"Day");
71#endif
72
73    // first time run aplication: -> let's have it direct connection in this case
74    if(!AppSettings::contains("proxyIsDirectConnection"))
75        AppSettings::setDirectConnection(true);
76
77    QNetworkProxy proxy(
78            AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : (QNetworkProxy::ProxyType)AppSettings::proxyType(),
79            AppSettings::proxyAddress(),
80            AppSettings::proxyPort(),
81            AppSettings::proxyUsername(),
82            AppSettings::proxyPassword());
83    QNetworkProxy::setApplicationProxy(proxy);
84
85    // event details have changed
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)));
91
92    // date has changed
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)));
98
99    // search result has changed
100    connect(searchTabContainer, SIGNAL(searchResultChanged()), SLOT(onSearchResultChanged()));
101
102    // systm tray icon
103    connect(systemTrayIcon, SIGNAL(messageClicked()), SLOT(onSystemTrayMessageClicked()));
104    connect(systemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(onSystemTrayMessageClicked()));
105
106    // timer
107    connect(alarmTimer, SIGNAL(timeout()), SLOT(onAlarmTimerTimeout()));
108
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
114    // make it impossible to hide the toolbar by disallowing its context menu
115    toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
116
117    // open conference
118    useConference(Conference::activeConference());
119    // optimization, see useConference() code
120    try {
121        initTabs();
122    } catch (const OrmException& e) {
123        qDebug() << "OrmException:" << e.text();
124        clearTabs();
125    }
126
127    connect(mNetworkAccessManager, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)), SLOT(sslErrors(QNetworkReply*, QList<QSslError>)));
128    connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
129    connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
130    connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), conferenceModel, SLOT(newConferenceEnd(int)));
131}
132
133
134MainWindow::~MainWindow() {
135    sqlEngine->close();
136}
137
138
139void MainWindow::on_aboutAction_triggered()
140{
141    QDialog dialog(this);
142    Ui::AboutDialog ui;
143    ui.setupUi(&dialog);
144    ui.labDescription->setText(ui.labDescription->text().arg(qApp->applicationVersion()));
145#ifdef N810
146    dialog.setFixedWidth(width());
147#endif
148    dialog.exec();
149}
150
151
152void MainWindow::on_reloadAction_triggered() {
153    int confId = Conference::activeConference();
154    if (confId== -1) return;
155    Conference active = Conference::getById(confId);
156    if (active.url().isEmpty()) return;
157    importFromNetwork(active.url(), confId);
158    setEnabled(false);
159}
160
161
162void MainWindow::on_nowAction_triggered() {
163    int confId = Conference::activeConference();
164    if (confId== -1) return;
165    dayNavigator->setCurDate(QDate::currentDate());
166    dayTabContainer->expandTimeGroup(QTime::currentTime(), confId);
167}
168
169
170void MainWindow::on_searchAction_triggered() {
171    if (tabWidget->currentWidget() == searchTab)
172        searchTabContainer->showSearchDialog(!searchTabContainer->searchDialogIsVisible());
173    else {
174        tabWidget->setCurrentWidget(searchTab);
175        searchTabContainer->showSearchDialog();
176    }
177}
178
179
180void MainWindow::on_expandAllAction_triggered() {
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();
186}
187
188
189void MainWindow::on_collapseAllAction_triggered() {
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();
195}
196
197
198void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
199    dayTabContainer->redisplayEvent(aEventId);
200    if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
201    else favsTabContainer->redisplayEvent(aEventId);
202    tracksTabContainer->redisplayEvent(aEventId);
203    roomsTabContainer->redisplayEvent(aEventId);
204    searchTabContainer->redisplayEvent(aEventId);
205}
206
207
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
230void MainWindow::onSystemTrayMessageClicked() {
231    systemTrayIcon->hide();
232}
233
234
235void MainWindow::onAlarmTimerTimeout() {
236    // determine if an alarm is set on an event that's starting soon
237    int conferenceId = Conference::activeConference();
238    QList<Event> events = Event::getImminentAlarmEvents(AppSettings::preEventAlarmSec(), conferenceId);
239    if (events.empty()) return;
240
241    // build a message string
242    const Conference conference = Conference::getById(conferenceId);
243    Event event;
244    QString title;
245    QString message;
246    if (events.size() == 1) {
247        event = events.first();
248        title = tr("Next event at %1").arg(conference.shiftTime(event.start().time()).toString("HH:mm"));
249        message = tr("\"%1\"\n(%2)").arg(event.title()).arg(event.room()->name());
250    } else {
251        title = tr("%1 upcoming events").arg(events.size());
252        QStringList messages;
253        foreach (event, events) {
254            messages += tr("%1: \"%2\" (%3)").arg(conference.shiftTime(event.start().time()).toString("HH:mm")).arg(event.title()).arg(event.room()->name());
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
267    systemTrayIcon->show();
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)
269    systemTrayIcon->showMessage("ConfClerk", tr("Your upcoming events"), QSystemTrayIcon::Information);
270    qApp->processEvents();
271    systemTrayIcon->showMessage(title, message, QSystemTrayIcon::Information, 60*60*24*1000);
272    QApplication::alert(this);
273    QApplication::beep();
274}
275
276
277void MainWindow::useConference(int conferenceId)
278{
279    if (conferenceId == -1)  // in case no conference is active
280    {
281        unsetConference();
282        return;
283    }
284    try {
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);
290
291        // looks like it does not work at n900
292        setWindowTitle(activeConference.title());
293
294        // optimization.
295        // dont run initTabs() here
296        // it takes much CPU, making travelling between conferences in ConferenceEditor longer
297        // and is not seen in maemo WM anyway
298        // instead run it explicitly
299        // 1. at startup
300        // 2. when ConferenceEditor finished
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();
308    } catch (const OrmException& e) {
309        qDebug() << "OrmException:" << e.text();
310        // cannon set an active conference
311        unsetConference();   // TODO: as no active conference is now correctly managed this should be handled as a fatal error
312        return;
313    }
314
315}
316
317void MainWindow::initTabs()
318{
319    int confId = Conference::activeConference();
320    if (confId != -1)   // only init tabs if a conference is active
321    {
322        Conference active = Conference::getById(confId);
323        ((Application*) qApp)->setActiveConference(active);
324        QDate startDate = active.start();
325        QDate endDate = active.end();
326
327        // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
328        dayNavigator->setDates(startDate, endDate);
329        nowAction->trigger();
330    }
331}
332
333void MainWindow::clearTabs()
334{
335    dayTabContainer->clearModel();
336    tracksTabContainer->clearModel();
337    roomsTabContainer->clearModel();
338    favsTabContainer->clearModel();
339    searchTabContainer->clearModel();
340}
341
342void MainWindow::unsetConference()
343{
344    clearTabs();
345    dayNavigator->unsetDates();
346    setWindowTitle(saved_title);
347    ((Application*) qApp)->unsetActiveConference();
348}
349
350
351void MainWindow::showError(const QString& message) {
352    error_message(message);
353}
354
355
356void MainWindow::on_settingsAction_triggered()
357{
358    SettingsDialog dialog;
359    dialog.loadDialogData();
360    if (dialog.exec() == QDialog::Accepted) {
361        dialog.saveDialogData();
362        QNetworkProxy proxy(
363                AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : (QNetworkProxy::ProxyType)AppSettings::proxyType(),
364                AppSettings::proxyAddress(),
365                AppSettings::proxyPort(),
366                AppSettings::proxyUsername(),
367                AppSettings::proxyPassword());
368        QNetworkProxy::setApplicationProxy(proxy);
369    }
370}
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*/
382void MainWindow::on_conferencesAction_triggered()
383{
384    ConferenceEditor dialog(conferenceModel, this);
385
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)));
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)));
397    connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), &dialog, SLOT(importFinished(int)));
398
399    connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
400
401    dialog.exec();
402
403    // optimization, see useConference() code
404    try {
405        initTabs();
406    } catch (const OrmException& e) {
407        qDebug() << "OrmException:" << e.text();
408        clearTabs();
409    }
410}
411
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,
423                tr("SSL errors"),
424                tr("One or more SSL errors have occurred: %1", 0, errors.size()).arg(errorString),
425                QMessageBox::Ignore | QMessageBox::Cancel) == QMessageBox::Ignore) {
426        aReply->ignoreSslErrors();
427    } else {
428        aReply->abort();
429    }
430}
431
432void MainWindow::networkQueryFinished(QNetworkReply *aReply) {
433    if (aReply->error() != QNetworkReply::NoError) {
434        error_message(tr("Error occurred during download: %1").arg(aReply->errorString()));
435    } else {
436        QUrl redirectUrl = aReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
437        if (!redirectUrl.isEmpty()) {
438            if (redirectUrl != aReply->request().url()) {
439                importFromNetwork(redirectUrl.toString(), aReply->request().attribute(QNetworkRequest::User).toInt());
440                return; // don't enable controls
441            } else {
442                error_message(tr("Error: Cyclic redirection from %1 to itself.").arg(redirectUrl.toString()));
443            }
444        } else {
445            importData(aReply->readAll(), aReply->url().toEncoded(), aReply->request().attribute(QNetworkRequest::User).toInt());
446        }
447    }
448    setEnabled(true);
449}
450
451void MainWindow::importData(const QByteArray &aData, const QString& url, int conferenceId)
452{
453    mXmlParser->parseData(aData, url, conferenceId);
454}
455
456void MainWindow::importFromNetwork(const QString& url, int conferenceId)
457{
458    QNetworkRequest request;
459    QSslConfiguration qSslConfiguration = request.sslConfiguration();
460    qSslConfiguration.setProtocol(QSsl::AnyProtocol);
461    qSslConfiguration.setPeerVerifyMode(QSslSocket::QueryPeer);
462    request.setUrl(QUrl(url));
463    request.setSslConfiguration(qSslConfiguration);
464    request.setAttribute(QNetworkRequest::User, conferenceId);
465
466    mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
467    mNetworkAccessManager->get(request);
468}
469
470void MainWindow::importFromFile(const QString& filename, int conferenceId)
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
478    importData(file.readAll(), "", conferenceId);
479}
480
481
482void MainWindow::removeConference(int id) {
483    sqlEngine->deleteConference(id);
484    conferenceModel->conferenceRemoved();
485    emit conferenceRemoved();
486}
487
488
489void MainWindow::changeConferenceUrl(int id, const QString& url) {
490    Conference::getById(id).setUrl(url);
491}
492
Note: See TracBrowser for help on using the repository browser.