source: confclerk_git/src/gui/mainwindow.cpp @ 0584e12

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

Make it impossible to hide the toolbar by disallowing its context menu (fixes #51).

  • Property mode set to 100644
File size: 16.3 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    // add the actions from the main menu to the window, otherwise the shortcuts don't work on MAEMO
116    addAction(conferencesAction);
117    addAction(settingsAction);
118    addAction(quitAction);
119
120    // make it impossible to hide the toolbar by disallowing its context menu
121    toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
122
123    // open conference
124    useConference(Conference::activeConference());
125    // optimization, see useConference() code
126    try {
127        initTabs();
128    } catch (const OrmException& e) {
129        qDebug() << "OrmException:" << e.text();
130        clearTabs();
131    }
132
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    QList<Event> events = Event::getImminentAlarmEvents(AppSettings::preEventAlarmSec(), Conference::activeConference());
243    if (events.empty()) return;
244
245    // build a message string
246    Event event;
247    QString title;
248    QString message;
249    if (events.size() == 1) {
250        event = events.first();
251        title = tr("Next event at %1").arg(event.start().toString("HH:mm"));
252        message = tr("\"%1\"\n(%2)").arg(event.title()).arg(event.room()->name());
253    } else {
254        title = tr("%1 upcoming events").arg(events.size());
255        QStringList messages;
256        foreach (event, events) {
257            messages += tr("%1: \"%2\" (%3)").arg(event.start().toString("HH:mm")).arg(event.title()).arg(event.room()->name());
258        }
259        message = messages.join("\n");
260    }
261
262    // and delete the corresponding alarm
263    foreach (event, events) {
264        event.setHasAlarm(false);
265        event.update("alarm");
266        onEventChanged(event.id(), false);
267    }
268
269    // show message
270    systemTrayIcon->show();
271    // The next two lines are to prevent a very strange position of the message box the first time at X11/aweseome (not Win32/XP)
272    systemTrayIcon->showMessage("ConfClerk", "Your upcoming events", QSystemTrayIcon::Information);
273    qApp->processEvents();
274    systemTrayIcon->showMessage(title, message, QSystemTrayIcon::Information, 60*60*24*1000);
275    QApplication::alert(this);
276    QApplication::beep();
277}
278
279
280void MainWindow::useConference(int conferenceId)
281{
282    if (conferenceId == -1)  // in case no conference is active
283    {
284        unsetConference();
285        return;
286    }
287    try {
288        int oldActiveConferenceId = Conference::activeConference();
289        bool switchActiveConference = conferenceId != oldActiveConferenceId;
290        if (switchActiveConference) Conference::getById(oldActiveConferenceId).update("active", 0);
291        Conference activeConference = Conference::getById(conferenceId);
292        if (switchActiveConference) activeConference.update("active",1);
293
294        // looks like it does not work at n900
295        setWindowTitle(activeConference.title());
296
297        // optimization.
298        // dont run initTabs() here
299        // it takes much CPU, making travelling between conferences in ConferenceEditor longer
300        // and is not seen in maemo WM anyway
301        // instead run it explicitly
302        // 1. at startup
303        // 2. when ConferenceEditor finished
304        // dont forget to protect the calls by try-catch!
305
306        // just in case, clear conference selection instead
307        clearTabs();
308
309        // end of optimization
310        // initTabs();
311    } catch (OrmException& e) {
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::HttpProxy,
364                AppSettings::proxyAddress(),
365                AppSettings::proxyPort(),
366                PROXY_USERNAME,
367                PROXY_PASSWD);
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 (OrmException) {
407        clearTabs();
408    }
409}
410
411void MainWindow::networkQueryFinished(QNetworkReply *aReply) {
412    if (aReply->error() != QNetworkReply::NoError) {
413        error_message(QString("Error occured during download: ") + aReply->errorString());
414    } else {
415        QUrl redirectUrl = aReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
416        if (!redirectUrl.isEmpty()) {
417            if (redirectUrl != aReply->request().url()) {
418                importFromNetwork(redirectUrl.toString(), aReply->request().attribute(QNetworkRequest::User).toInt());
419                return; // don't enable controls
420            } else {
421                error_message(QString("Error: Cyclic redirection from %1 to itself.").arg(redirectUrl.toString()));
422            }
423        } else {
424            importData(aReply->readAll(), aReply->url().toEncoded(), aReply->request().attribute(QNetworkRequest::User).toInt());
425        }
426    }
427    setEnabled(true);
428}
429
430void MainWindow::importData(const QByteArray &aData, const QString& url, int conferenceId)
431{
432    mXmlParser->parseData(aData, url, conferenceId);
433}
434
435void MainWindow::importFromNetwork(const QString& url, int conferenceId)
436{
437    QNetworkRequest request;
438    request.setUrl(QUrl(url));
439    request.setAttribute(QNetworkRequest::User, conferenceId);
440
441    mNetworkAccessManager->setProxy(QNetworkProxy::applicationProxy());
442    mNetworkAccessManager->get(request);
443}
444
445void MainWindow::importFromFile(const QString& filename, int conferenceId)
446{
447    QFile file(filename);
448    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {   
449        static const QString format("Cannot read \"%1\": error %2");
450        error_message(format.arg(filename, QString::number(file.error())));
451    }
452
453    importData(file.readAll(), "", conferenceId);
454}
455
456
457void MainWindow::removeConference(int id) {
458    sqlEngine->deleteConference(id);
459    conferenceModel->conferenceRemoved();
460    emit conferenceRemoved();
461}
462
463
464void MainWindow::changeConferenceUrl(int id, const QString& url) {
465    Conference::getById(id).setUrl(url);
466}
467
Note: See TracBrowser for help on using the repository browser.