source: src/gui/mainwindow.cpp @ 173399b

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

Added some actions to the mainwindow - otherwise shortcuts don't work on MAEMO (see ticket #28).

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