source: confclerk_git/src/gui/mainwindow.cpp @ 83481c6

qt5
Last change on this file since 83481c6 was 83481c6, checked in by gregor herrmann <gregoa@…>, 9 years ago

bump copyright years

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