source: src/gui/mainwindow.cpp @ 4bf83ee

Last change on this file since 4bf83ee was 4bf83ee, checked in by gregor herrmann <gregor@…>, 5 years ago

Settings (dialog): add username/password options for proxy server

Cf. #59

  • Property mode set to 100644
File size: 17.5 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
41#include "ui_about.h"
42#include "eventdialog.h"
43#include "daynavigatorwidget.h"
44#include "settingsdialog.h"
45#include "conferenceeditor.h"
46#include "schedulexmlparser.h"
47#include "errormessage.h"
48
49#include "tabcontainer.h"
50#include "appsettings.h"
51
52MainWindow::MainWindow(QWidget* parent): QMainWindow(parent) {
53    setupUi(this);
54
55    // Open database
56    sqlEngine = new SqlEngine(this);
57    searchTabContainer->setSqlEngine(sqlEngine);
58    connect(sqlEngine, SIGNAL(dbError(QString)), this, SLOT(showError(QString)));
59    sqlEngine->open();
60    sqlEngine->createOrUpdateDbSchema();
61
62    conferenceModel = new ConferenceModel(this);
63    mXmlParser = new ScheduleXmlParser(sqlEngine, this);
64    mNetworkAccessManager = new QNetworkAccessManager(this);
65    systemTrayIcon = new QSystemTrayIcon(qApp->windowIcon(), this);
66    alarmTimer = new QTimer(this);
67
68    alarmTimer->setInterval(60000);
69    alarmTimer->start();
70    saved_title = windowTitle();
71
72#ifdef N810
73    tabWidget->setTabText(1,"Favs");
74    //tabWidget->setTabText(2,"Day");
75#endif
76
77    // first time run aplication: -> let's have it direct connection in this case
78    if(!AppSettings::contains("proxyIsDirectConnection"))
79        AppSettings::setDirectConnection(true);
80
81    QNetworkProxy proxy(
82            AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : (QNetworkProxy::ProxyType)AppSettings::proxyType(),
83            AppSettings::proxyAddress(),
84            AppSettings::proxyPort(),
85            AppSettings::proxyUsername(),
86            AppSettings::proxyPassword());
87    QNetworkProxy::setApplicationProxy(proxy);
88
89    // event details have changed
90    connect(dayTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
91    connect(favsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
92    connect(tracksTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
93    connect(roomsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
94    connect(searchTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
95
96    // date has changed
97    connect(dayNavigator, SIGNAL(dateChanged(QDate)), dayTabContainer, SLOT(redisplayDate(QDate)));
98    connect(dayNavigator, SIGNAL(dateChanged(QDate)), favsTabContainer, SLOT(redisplayDate(QDate)));
99    connect(dayNavigator, SIGNAL(dateChanged(QDate)), tracksTabContainer, SLOT(redisplayDate(QDate)));
100    connect(dayNavigator, SIGNAL(dateChanged(QDate)), roomsTabContainer, SLOT(redisplayDate(QDate)));
101    connect(dayNavigator, SIGNAL(dateChanged(QDate)), searchTabContainer, SLOT(redisplayDate(QDate)));
102
103    // search result has changed
104    connect(searchTabContainer, SIGNAL(searchResultChanged()), SLOT(onSearchResultChanged()));
105
106    // systm tray icon
107    connect(systemTrayIcon, SIGNAL(messageClicked()), SLOT(onSystemTrayMessageClicked()));
108    connect(systemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(onSystemTrayMessageClicked()));
109
110    // timer
111    connect(alarmTimer, SIGNAL(timeout()), SLOT(onAlarmTimerTimeout()));
112
113    // add the actions from the main menu to the window, otherwise the shortcuts don't work on MAEMO
114    addAction(conferencesAction);
115    addAction(settingsAction);
116    addAction(quitAction);
117
118    // make it impossible to hide the toolbar by disallowing its context menu
119    toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
120
121    // open conference
122    useConference(Conference::activeConference());
123    // optimization, see useConference() code
124    try {
125        initTabs();
126    } catch (const OrmException& e) {
127        qDebug() << "OrmException:" << e.text();
128        clearTabs();
129    }
130
131    connect(mNetworkAccessManager, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)), SLOT(sslErrors(QNetworkReply*, QList<QSslError>)));
132    connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
133    connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
134    connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), conferenceModel, SLOT(newConferenceEnd(int)));
135}
136
137
138MainWindow::~MainWindow() {
139    sqlEngine->close();
140}
141
142
143void MainWindow::on_aboutAction_triggered()
144{
145    QDialog dialog(this);
146    Ui::AboutDialog ui;
147    ui.setupUi(&dialog);
148    ui.labDescription->setText(ui.labDescription->text().arg(qApp->applicationVersion()));
149#ifdef N810
150    dialog.setFixedWidth(width());
151#endif
152    dialog.exec();
153}
154
155
156void MainWindow::on_reloadAction_triggered() {
157    int confId = Conference::activeConference();
158    if (confId== -1) return;
159    Conference active = Conference::getById(confId);
160    if (active.url().isEmpty()) return;
161    importFromNetwork(active.url(), confId);
162    setEnabled(false);
163}
164
165
166void MainWindow::on_nowAction_triggered() {
167    int confId = Conference::activeConference();
168    if (confId== -1) return;
169    dayNavigator->setCurDate(QDate::currentDate());
170    dayTabContainer->expandTimeGroup(QTime::currentTime(), confId);
171}
172
173
174void MainWindow::on_searchAction_triggered() {
175    if (tabWidget->currentWidget() == searchTab)
176        searchTabContainer->showSearchDialog(!searchTabContainer->searchDialogIsVisible());
177    else {
178        tabWidget->setCurrentWidget(searchTab);
179        searchTabContainer->showSearchDialog();
180    }
181}
182
183
184void MainWindow::on_expandAllAction_triggered() {
185    if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->expandAll();
186    if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->expandAll();
187    if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->expandAll();
188    if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->expandAll();
189    if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->expandAll();
190}
191
192
193void MainWindow::on_collapseAllAction_triggered() {
194    if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->collapseAll();
195    if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->collapseAll();
196    if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->collapseAll();
197    if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->collapseAll();
198    if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->collapseAll();
199}
200
201
202void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
203    dayTabContainer->redisplayEvent(aEventId);
204    if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
205    else favsTabContainer->redisplayEvent(aEventId);
206    tracksTabContainer->redisplayEvent(aEventId);
207    roomsTabContainer->redisplayEvent(aEventId);
208    searchTabContainer->redisplayEvent(aEventId);
209}
210
211
212void MainWindow::onSearchResultChanged() {
213    // Are results found on the current date?
214    QDate date = dayNavigator->curDate();
215    int count = searchTabContainer->searchResultCount(date);
216    if (count > 0) {searchTabContainer->redisplayDate(date); return;}
217
218    // Are results found in the future?
219    for (date = date.addDays(1); date <= dayNavigator->endDate(); date = date.addDays(1)) {
220        int count = searchTabContainer->searchResultCount(date);
221        if (count > 0) {dayNavigator->setCurDate(date); return;}
222    }
223
224    // Are results found in the past?
225    for (date = dayNavigator->startDate(); date < dayNavigator->curDate(); date = date.addDays(1)) {
226        int count = searchTabContainer->searchResultCount(date);
227        if (count > 0) {dayNavigator->setCurDate(date); return;}
228    }
229    // No results were found
230    searchTabContainer->redisplayDate(dayNavigator->curDate());
231}
232
233
234void MainWindow::onSystemTrayMessageClicked() {
235    systemTrayIcon->hide();
236}
237
238
239void MainWindow::onAlarmTimerTimeout() {
240    // determine if an alarm is set on an event that's starting soon
241    QList<Event> events = Event::getImminentAlarmEvents(AppSettings::preEventAlarmSec(), Conference::activeConference());
242    if (events.empty()) return;
243
244    // build a message string
245    Event event;
246    QString title;
247    QString message;
248    if (events.size() == 1) {
249        event = events.first();
250        title = tr("Next event at %1").arg(event.start().toString("HH:mm"));
251        message = tr("\"%1\"\n(%2)").arg(event.title()).arg(event.room()->name());
252    } else {
253        title = tr("%1 upcoming events").arg(events.size());
254        QStringList messages;
255        foreach (event, events) {
256            messages += tr("%1: \"%2\" (%3)").arg(event.start().toString("HH:mm")).arg(event.title()).arg(event.room()->name());
257        }
258        message = messages.join("\n");
259    }
260
261    // and delete the corresponding alarm
262    foreach (event, events) {
263        event.setHasAlarm(false);
264        event.update("alarm");
265        onEventChanged(event.id(), false);
266    }
267
268    // show message
269    systemTrayIcon->show();
270    // The next two lines are to prevent a very strange position of the message box the first time at X11/aweseome (not Win32/XP)
271    systemTrayIcon->showMessage("ConfClerk", tr("Your upcoming events"), QSystemTrayIcon::Information);
272    qApp->processEvents();
273    systemTrayIcon->showMessage(title, message, QSystemTrayIcon::Information, 60*60*24*1000);
274    QApplication::alert(this);
275    QApplication::beep();
276}
277
278
279void MainWindow::useConference(int conferenceId)
280{
281    if (conferenceId == -1)  // in case no conference is active
282    {
283        unsetConference();
284        return;
285    }
286    try {
287        int oldActiveConferenceId = Conference::activeConference();
288        bool switchActiveConference = conferenceId != oldActiveConferenceId;
289        if (switchActiveConference) Conference::getById(oldActiveConferenceId).update("active", 0);
290        Conference activeConference = Conference::getById(conferenceId);
291        if (switchActiveConference) activeConference.update("active",1);
292
293        // looks like it does not work at n900
294        setWindowTitle(activeConference.title());
295
296        // optimization.
297        // dont run initTabs() here
298        // it takes much CPU, making travelling between conferences in ConferenceEditor longer
299        // and is not seen in maemo WM anyway
300        // instead run it explicitly
301        // 1. at startup
302        // 2. when ConferenceEditor finished
303        // dont forget to protect the calls by try-catch!
304
305        // just in case, clear conference selection instead
306        clearTabs();
307
308        // end of optimization
309        // initTabs();
310    } catch (const OrmException& e) {
311        qDebug() << "OrmException:" << e.text();
312        // cannon set an active conference
313        unsetConference();   // TODO: as no active conference is now correctly managed this should be handled as a fatal error
314        return;
315    }
316
317}
318
319void MainWindow::initTabs()
320{
321    int confId = Conference::activeConference();
322    if (confId != -1)   // only init tabs if a conference is active
323    {
324        Conference active = Conference::getById(confId);
325        QDate startDate = active.start();
326        QDate endDate = active.end();
327
328        // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
329        dayNavigator->setDates(startDate, endDate);
330        nowAction->trigger();
331    }
332}
333
334void MainWindow::clearTabs()
335{
336    dayTabContainer->clearModel();
337    tracksTabContainer->clearModel();
338    roomsTabContainer->clearModel();
339    favsTabContainer->clearModel();
340    searchTabContainer->clearModel();
341}
342
343void MainWindow::unsetConference()
344{
345    clearTabs();
346    dayNavigator->unsetDates();
347    setWindowTitle(saved_title);
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.