source: src/gui/mainwindow.cpp @ fe5dac7

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

Used tr() for some more GUI strings (there are plenty more that should be treated this way).

  • Property mode set to 100644
File size: 17.3 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
52const QString PROXY_USERNAME;
53const QString PROXY_PASSWD;
54
55MainWindow::MainWindow(QWidget* parent): QMainWindow(parent) {
56    setupUi(this);
57
58    // Open database
59    sqlEngine = new SqlEngine(this);
60    searchTabContainer->setSqlEngine(sqlEngine);
61    connect(sqlEngine, SIGNAL(dbError(QString)), this, SLOT(showError(QString)));
62    sqlEngine->open();
63    sqlEngine->createOrUpdateDbSchema();
64
65    conferenceModel = new ConferenceModel(this);
66    mXmlParser = new ScheduleXmlParser(sqlEngine, this);
67    mNetworkAccessManager = new QNetworkAccessManager(this);
68    systemTrayIcon = new QSystemTrayIcon(qApp->windowIcon(), this);
69    alarmTimer = new QTimer(this);
70
71    alarmTimer->setInterval(60000);
72    alarmTimer->start();
73    saved_title = windowTitle();
74
75#ifdef N810
76    tabWidget->setTabText(1,"Favs");
77    //tabWidget->setTabText(2,"Day");
78#endif
79
80    // first time run aplication: -> let's have it direct connection in this case
81    if(!AppSettings::contains("proxyIsDirectConnection"))
82        AppSettings::setDirectConnection(true);
83
84    QNetworkProxy proxy(
85            AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
86            AppSettings::proxyAddress(),
87            AppSettings::proxyPort(),
88            PROXY_USERNAME,
89            PROXY_PASSWD);
90    QNetworkProxy::setApplicationProxy(proxy);
91
92    // event details have changed
93    connect(dayTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
94    connect(favsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
95    connect(tracksTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
96    connect(roomsTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
97    connect(searchTabContainer, SIGNAL(eventChanged(int,bool)), SLOT(onEventChanged(int,bool)));
98
99    // date has changed
100    connect(dayNavigator, SIGNAL(dateChanged(QDate)), dayTabContainer, SLOT(redisplayDate(QDate)));
101    connect(dayNavigator, SIGNAL(dateChanged(QDate)), favsTabContainer, SLOT(redisplayDate(QDate)));
102    connect(dayNavigator, SIGNAL(dateChanged(QDate)), tracksTabContainer, SLOT(redisplayDate(QDate)));
103    connect(dayNavigator, SIGNAL(dateChanged(QDate)), roomsTabContainer, SLOT(redisplayDate(QDate)));
104    connect(dayNavigator, SIGNAL(dateChanged(QDate)), searchTabContainer, SLOT(redisplayDate(QDate)));
105
106    // search result has changed
107    connect(searchTabContainer, SIGNAL(searchResultChanged()), SLOT(onSearchResultChanged()));
108
109    // systm tray icon
110    connect(systemTrayIcon, SIGNAL(messageClicked()), SLOT(onSystemTrayMessageClicked()));
111    connect(systemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SLOT(onSystemTrayMessageClicked()));
112
113    // timer
114    connect(alarmTimer, SIGNAL(timeout()), SLOT(onAlarmTimerTimeout()));
115
116    // add the actions from the main menu to the window, otherwise the shortcuts don't work on MAEMO
117    addAction(conferencesAction);
118    addAction(settingsAction);
119    addAction(quitAction);
120
121    // make it impossible to hide the toolbar by disallowing its context menu
122    toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
123
124    // open conference
125    useConference(Conference::activeConference());
126    // optimization, see useConference() code
127    try {
128        initTabs();
129    } catch (const OrmException& e) {
130        qDebug() << "OrmException:" << e.text();
131        clearTabs();
132    }
133
134    connect(mNetworkAccessManager, SIGNAL(sslErrors(QNetworkReply*, QList<QSslError>)), SLOT(sslErrors(QNetworkReply*, QList<QSslError>)));
135    connect(mNetworkAccessManager, SIGNAL(finished(QNetworkReply*)), SLOT(networkQueryFinished(QNetworkReply*)));
136    connect(mXmlParser, SIGNAL(parsingScheduleBegin()), conferenceModel, SLOT(newConferenceBegin()));
137    connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), conferenceModel, SLOT(newConferenceEnd(int)));
138}
139
140
141MainWindow::~MainWindow() {
142    sqlEngine->close();
143}
144
145
146void MainWindow::on_aboutAction_triggered()
147{
148    QDialog dialog(this);
149    Ui::AboutDialog ui;
150    ui.setupUi(&dialog);
151    ui.labDescription->setText(ui.labDescription->text().arg(qApp->applicationVersion()));
152#ifdef N810
153    dialog.setFixedWidth(width());
154#endif
155    dialog.exec();
156}
157
158
159void MainWindow::on_reloadAction_triggered() {
160    int confId = Conference::activeConference();
161    if (confId== -1) return;
162    Conference active = Conference::getById(confId);
163    if (active.url().isEmpty()) return;
164    importFromNetwork(active.url(), confId);
165    setEnabled(false);
166}
167
168
169void MainWindow::on_nowAction_triggered() {
170    int confId = Conference::activeConference();
171    if (confId== -1) return;
172    dayNavigator->setCurDate(QDate::currentDate());
173    dayTabContainer->expandTimeGroup(QTime::currentTime(), confId);
174}
175
176
177void MainWindow::on_searchAction_triggered() {
178    if (tabWidget->currentWidget() == searchTab)
179        searchTabContainer->showSearchDialog(!searchTabContainer->searchDialogIsVisible());
180    else {
181        tabWidget->setCurrentWidget(searchTab);
182        searchTabContainer->showSearchDialog();
183    }
184}
185
186
187void MainWindow::on_expandAllAction_triggered() {
188    if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->expandAll();
189    if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->expandAll();
190    if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->expandAll();
191    if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->expandAll();
192    if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->expandAll();
193}
194
195
196void MainWindow::on_collapseAllAction_triggered() {
197    if (tabWidget->currentWidget() == favouritesTab) favsTabContainer->treeView->collapseAll();
198    if (tabWidget->currentWidget() == dayViewTab) dayTabContainer->treeView->collapseAll();
199    if (tabWidget->currentWidget() == tracksTab) tracksTabContainer->treeView->collapseAll();
200    if (tabWidget->currentWidget() == roomsTab) roomsTabContainer->treeView->collapseAll();
201    if (tabWidget->currentWidget() == searchTab) searchTabContainer->treeView->collapseAll();
202}
203
204
205void MainWindow::onEventChanged(int aEventId, bool favouriteChanged) {
206    dayTabContainer->redisplayEvent(aEventId);
207    if (favouriteChanged) favsTabContainer->redisplayDate(dayNavigator->curDate());
208    else favsTabContainer->redisplayEvent(aEventId);
209    tracksTabContainer->redisplayEvent(aEventId);
210    roomsTabContainer->redisplayEvent(aEventId);
211    searchTabContainer->redisplayEvent(aEventId);
212}
213
214
215void MainWindow::onSearchResultChanged() {
216    // Are results found on the current date?
217    QDate date = dayNavigator->curDate();
218    int count = searchTabContainer->searchResultCount(date);
219    if (count > 0) {searchTabContainer->redisplayDate(date); return;}
220
221    // Are results found in the future?
222    for (date = date.addDays(1); date <= dayNavigator->endDate(); date = date.addDays(1)) {
223        int count = searchTabContainer->searchResultCount(date);
224        if (count > 0) {dayNavigator->setCurDate(date); return;}
225    }
226
227    // Are results found in the past?
228    for (date = dayNavigator->startDate(); date < dayNavigator->curDate(); date = date.addDays(1)) {
229        int count = searchTabContainer->searchResultCount(date);
230        if (count > 0) {dayNavigator->setCurDate(date); return;}
231    }
232    // No results were found
233    searchTabContainer->redisplayDate(dayNavigator->curDate());
234}
235
236
237void MainWindow::onSystemTrayMessageClicked() {
238    systemTrayIcon->hide();
239}
240
241
242void MainWindow::onAlarmTimerTimeout() {
243    // determine if an alarm is set on an event that's starting soon
244    QList<Event> events = Event::getImminentAlarmEvents(AppSettings::preEventAlarmSec(), Conference::activeConference());
245    if (events.empty()) return;
246
247    // build a message string
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(event.start().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(event.start().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 (OrmException& e) {
314        // cannon set an active conference
315        unsetConference();   // TODO: as no active conference is now correctly managed this should be handled as a fatal error
316        return;
317    }
318
319}
320
321void MainWindow::initTabs()
322{
323    int confId = Conference::activeConference();
324    if (confId != -1)   // only init tabs if a conference is active
325    {
326        Conference active = Conference::getById(confId);
327        QDate startDate = active.start();
328        QDate endDate = active.end();
329
330        // 'dayNavigator' emits signal 'dateChanged' after setting valid START:END dates
331        dayNavigator->setDates(startDate, endDate);
332        nowAction->trigger();
333    }
334}
335
336void MainWindow::clearTabs()
337{
338    dayTabContainer->clearModel();
339    tracksTabContainer->clearModel();
340    roomsTabContainer->clearModel();
341    favsTabContainer->clearModel();
342    searchTabContainer->clearModel();
343}
344
345void MainWindow::unsetConference()
346{
347    clearTabs();
348    dayNavigator->unsetDates();
349    setWindowTitle(saved_title);
350}
351
352
353void MainWindow::showError(const QString& message) {
354    error_message(message);
355}
356
357
358void MainWindow::on_settingsAction_triggered()
359{
360    SettingsDialog dialog;
361    dialog.loadDialogData();
362    if (dialog.exec() == QDialog::Accepted) {
363        dialog.saveDialogData();
364        QNetworkProxy proxy(
365                AppSettings::isDirectConnection() ? QNetworkProxy::NoProxy : QNetworkProxy::HttpProxy,
366                AppSettings::proxyAddress(),
367                AppSettings::proxyPort(),
368                PROXY_USERNAME,
369                PROXY_PASSWD);
370        QNetworkProxy::setApplicationProxy(proxy);
371    }
372}
373
374/** Create and run ConferenceEditor dialog, making required connections for it.
375
376This method manages, which classes actually perform changes in conference list.
377
378There are several classes that modify the conferences:
379this:
380 deletion and URL update.
381this, mXmlParser and mNetworkAccessManager:
382 addition and refresh.
383*/
384void MainWindow::on_conferencesAction_triggered()
385{
386    ConferenceEditor dialog(conferenceModel, this);
387
388    connect(&dialog, SIGNAL(haveConferenceUrl(const QString&, int)), SLOT(importFromNetwork(const QString&, int)));
389    connect(&dialog, SIGNAL(haveConferenceFile(const QString&, int)), SLOT(importFromFile(const QString&, int)));
390    connect(&dialog, SIGNAL(removeConferenceRequested(int)), SLOT(removeConference(int)));
391    connect(&dialog, SIGNAL(changeUrlRequested(int, const QString&)),
392                    SLOT(changeConferenceUrl(int, const QString&)));
393
394    connect(&dialog, SIGNAL(haveConferenceSelected(int)), SLOT(useConference(int)));
395    connect(&dialog, SIGNAL(noneConferenceSelected()), SLOT(unsetConference()));
396
397    connect(mXmlParser, SIGNAL(parsingScheduleBegin()), &dialog, SLOT(importStarted()));
398    connect(mXmlParser, SIGNAL(progressStatus(int)), &dialog, SLOT(showParsingProgress(int)));
399    connect(mXmlParser, SIGNAL(parsingScheduleEnd(int)), &dialog, SLOT(importFinished(int)));
400
401    connect(this, SIGNAL(conferenceRemoved()), &dialog, SLOT(conferenceRemoved()));
402
403    dialog.exec();
404
405    // optimization, see useConference() code
406    try {
407        initTabs();
408    } catch (OrmException) {
409        clearTabs();
410    }
411}
412
413void MainWindow::sslErrors(QNetworkReply *aReply, const QList<QSslError> &errors) {
414    QString errorString;
415    foreach (const QSslError &error, errors) {
416        if (!errorString.isEmpty()) {
417            errorString += ", ";
418        }
419        errorString += error.errorString();
420    }
421
422    if (QMessageBox::warning(
423                this,
424                tr("SSL errors"),
425                tr("One or more SSL errors have occurred: %1", 0, errors.size()).arg(errorString),
426                QMessageBox::Ignore | QMessageBox::Cancel) == QMessageBox::Ignore) {
427        aReply->ignoreSslErrors();
428    } else {
429        aReply->abort();
430    }
431}
432
433void MainWindow::networkQueryFinished(QNetworkReply *aReply) {
434    if (aReply->error() != QNetworkReply::NoError) {
435        error_message(tr("Error occurred during download: %1").arg(aReply->errorString()));
436    } else {
437        QUrl redirectUrl = aReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
438        if (!redirectUrl.isEmpty()) {
439            if (redirectUrl != aReply->request().url()) {
440                importFromNetwork(redirectUrl.toString(), aReply->request().attribute(QNetworkRequest::User).toInt());
441                return; // don't enable controls
442            } else {
443                error_message(tr("Error: Cyclic redirection from %1 to itself.").arg(redirectUrl.toString()));
444            }
445        } else {
446            importData(aReply->readAll(), aReply->url().toEncoded(), aReply->request().attribute(QNetworkRequest::User).toInt());
447        }
448    }
449    setEnabled(true);
450}
451
452void MainWindow::importData(const QByteArray &aData, const QString& url, int conferenceId)
453{
454    mXmlParser->parseData(aData, url, conferenceId);
455}
456
457void MainWindow::importFromNetwork(const QString& url, int conferenceId)
458{
459    QNetworkRequest request;
460    QSslConfiguration qSslConfiguration = request.sslConfiguration();
461    qSslConfiguration.setProtocol(QSsl::AnyProtocol);
462    qSslConfiguration.setPeerVerifyMode(QSslSocket::QueryPeer);
463    request.setUrl(QUrl(url));
464    request.setSslConfiguration(qSslConfiguration);
465    request.setAttribute(QNetworkRequest::User, conferenceId);
466
467    mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
468    mNetworkAccessManager->get(request);
469}
470
471void MainWindow::importFromFile(const QString& filename, int conferenceId)
472{
473    QFile file(filename);
474    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {   
475        static const QString format("Cannot read \"%1\": error %2");
476        error_message(format.arg(filename, QString::number(file.error())));
477    }
478
479    importData(file.readAll(), "", conferenceId);
480}
481
482
483void MainWindow::removeConference(int id) {
484    sqlEngine->deleteConference(id);
485    conferenceModel->conferenceRemoved();
486    emit conferenceRemoved();
487}
488
489
490void MainWindow::changeConferenceUrl(int id, const QString& url) {
491    Conference::getById(id).setUrl(url);
492}
493
Note: See TracBrowser for help on using the repository browser.