source: src/gui/mainwindow.cpp @ 5b7fa79

Last change on this file since 5b7fa79 was 5b7fa79, checked in by Philipp Spitzer <philipp@…>, 10 months ago

Adjust shown event time when displayTimeShift is set.

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