aheinecke@0: #include "mainwindow.h"
aheinecke@0: 
aheinecke@0: #include <QDebug>
aheinecke@0: #include <QMessageBox>
aheinecke@0: #include <QSystemTrayIcon>
aheinecke@0: #include <QAction>
aheinecke@0: #include <QDialog>
aheinecke@0: #include <QMenu>
aheinecke@2: #include <QApplication>
aheinecke@16: #include <QFile>
aheinecke@19: #include <QTimer>
rrenkert@187: #include <QHBoxLayout>
rrenkert@187: #include <QVBoxLayout>
rrenkert@187: #include <QGroupBox>
rrenkert@187: #include <QPushButton>
rrenkert@189: #include <QSplitter>
rrenkert@205: #include <QLabel>
rrenkert@205: #include <QImage>
rrenkert@250: #include <QCheckBox>
aheinecke@19: 
aheinecke@19: // The amount of time in minutes stay silent if we have
aheinecke@19: // something to say
aheinecke@19: #define NAG_INTERVAL_MINUTES 2
aheinecke@0: 
aheinecke@72: #define SERVER_URL "https://files.kolab.org:443"
aheinecke@72: #define LIST_RESOURCE "/incoming/aheinecke/test"
aheinecke@72: #define SW_RESOURCE "/incoming/aheinecke/test"
aheinecke@72: 
aheinecke@7: #include "certificatelist.h"
aheinecke@10: #include "downloader.h"
aheinecke@71: #include "listupdatedialog.h"
rrenkert@191: #include "helpdialog.h"
rrenkert@200: #include "aboutdialog.h"
rrenkert@212: #include "statusdialog.h"
aheinecke@7: 
aheinecke@0: MainWindow::MainWindow() {
aheinecke@0:     createActions();
aheinecke@0:     createTrayIcon();
rrenkert@155:     createMenuBar();
rrenkert@187:     createContent();
aheinecke@45:     qRegisterMetaType<SSLConnection::ErrorCode>("SSLConnection::ErrorCode");
aheinecke@0: 
aheinecke@0:     connect(mTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
aheinecke@0:             this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
aheinecke@19: 
aheinecke@19:     mMessageTimer = new QTimer(this);
aheinecke@19:     connect(mMessageTimer, SIGNAL(timeout()), this, SLOT(showMessage()));
aheinecke@19:     mMessageTimer->setInterval(NAG_INTERVAL_MINUTES * 60 * 1000);
aheinecke@19:     mMessageTimer->start();
aheinecke@71:     checkUpdates();
aheinecke@0: }
aheinecke@0: 
aheinecke@0: void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
aheinecke@0: {
aheinecke@0:     switch (reason) {
aheinecke@0:     case QSystemTrayIcon::Trigger:
aheinecke@0:     case QSystemTrayIcon::MiddleClick:
aheinecke@0:         showMessage();
aheinecke@0:         break;
aheinecke@16:     case QSystemTrayIcon::DoubleClick:
aheinecke@16:         // TODO show menu
aheinecke@16:         break;
aheinecke@0:     default:
aheinecke@0:         ;
aheinecke@0:     }
aheinecke@0: }
aheinecke@0: 
aheinecke@71: void MainWindow::messageClicked()
aheinecke@71: {
aheinecke@71:     if (mCurState == NewListAvailable) {
aheinecke@71:         ListUpdateDialog *listUpdateDialog = new ListUpdateDialog(this,
aheinecke@71:                 mListToInstall);
aheinecke@71:         listUpdateDialog->show();
aheinecke@71:         qDebug() << "NewListAvailable";
aheinecke@71:     }
aheinecke@71: }
aheinecke@71: 
aheinecke@0: void MainWindow::showMessage()
aheinecke@0: {
aheinecke@16:     if (!mCurMessage.isEmpty()) {
aheinecke@16:         mTrayIcon->showMessage(QApplication::applicationName(), mCurMessage,
aheinecke@16:                                QSystemTrayIcon::Information, 5000);
aheinecke@19:         mMessageTimer->start(); // Restart the timer so that we don't spam
aheinecke@16:     }
aheinecke@0: }
aheinecke@16: 
aheinecke@16: void MainWindow::verifyAvailableData()
aheinecke@0: {
aheinecke@16:     QString listFileName = mSettings.value("List/available").toString();
aheinecke@16:     QString swFileName = mSettings.value("Software/available").toString();
aheinecke@16: 
aheinecke@16:     if (!listFileName.isEmpty()) {
aheinecke@71:         mListToInstall.readList(listFileName.toLocal8Bit().constData());
aheinecke@71:         if (!mListToInstall.isValid()) {
aheinecke@82:             mCurState = TransferError;
aheinecke@16:             // Probably a bug when Qt fileName is encoded and cFileName
aheinecke@16:             // fails because of this. This needs a unit test!
aheinecke@16:             // Maybe check that the file is in our data directory
aheinecke@16:             QFile::remove(listFileName);
aheinecke@16:             mSettings.remove("List/available");
aheinecke@16:             mSettings.remove("List/availableDate");
aheinecke@16:         }
aheinecke@16:     } else {
aheinecke@16:         // Make sure the available notation is also removed
aheinecke@16:         mSettings.remove("List/available");
aheinecke@16:         mSettings.remove("List/availableDate");
aheinecke@16:     }
aheinecke@16: 
aheinecke@16:     if (!swFileName.isEmpty()) {
aheinecke@16:         // TODO
aheinecke@16:     } else {
aheinecke@16:         mSettings.remove("Software/available");
aheinecke@16:         mSettings.remove("Software/availableDate");
aheinecke@16:     }
aheinecke@16: }
aheinecke@16: 
aheinecke@16: void MainWindow::handleNewList(const QString& fileName, const QDateTime& modDate) {
aheinecke@71:     mSettings.setValue("List/available", fileName);
aheinecke@71:     mSettings.setValue("List/availableDate", modDate);
aheinecke@16: 
aheinecke@71:     verifyAvailableData();
aheinecke@71:     if (!mListToInstall.isValid()) {
aheinecke@71:         /* Downloader provided invalid files */
aheinecke@71:         /* TODO: Error count. Error handling. Otherwise
aheinecke@71:          * we can go into an endless loop here */
aheinecke@71: 
aheinecke@71:         /* Retry the download again in 10 - 20 minutes */
aheinecke@71:         QTimer::singleShot(600000 + (qrand() % 60000), this, SLOT(checkUpdates()));
aheinecke@82:     } else {
aheinecke@82:         mCurMessage = tr("An updated certificate list is available. Click here to install.");
aheinecke@82:         setState(NewListAvailable);
aheinecke@82:         showMessage();
rrenkert@189:         loadCertificateList();
aheinecke@71:     }
aheinecke@16: }
aheinecke@16: 
aheinecke@16: void MainWindow::handleNewSW(const QString& fileName, const QDateTime& modDate) {
aheinecke@16:     mCurMessage = tr("An update for %1 is available. Click here to install.").arg(
aheinecke@16:                 QApplication::applicationName());
aheinecke@16:     setState(NewSoftwareAvailable);
aheinecke@16:     mSettings.setValue("Software/available", fileName);
aheinecke@16:     mSettings.setValue("Software/availableDate", modDate);
aheinecke@16: 
aheinecke@16:     mSettings.sync();
aheinecke@16:     showMessage();
aheinecke@16: }
aheinecke@16: 
aheinecke@16: void MainWindow::checkUpdates()
aheinecke@16: {
aheinecke@16:     verifyAvailableData();
aheinecke@16: 
aheinecke@17:     QDateTime listInstalledLastMod = mSettings.value("List/installedDate").toDateTime();
aheinecke@17:     QDateTime swInstalledLastMod = mSettings.value("Software/installedDate").toDateTime();
aheinecke@16: 
aheinecke@72:     Downloader* downloader = new Downloader(this,
aheinecke@72:                                             QString::fromLatin1(SERVER_URL),
aheinecke@72:                                             QByteArray(),
aheinecke@72:                                             QDateTime::currentDateTime(),
aheinecke@72: //                                            swInstalledLastMod,
aheinecke@72:                                             listInstalledLastMod,
aheinecke@72:                                             QString::fromLatin1(SW_RESOURCE),
aheinecke@72:                                             QString::fromLatin1(LIST_RESOURCE));
aheinecke@17: 
aheinecke@16:     connect(downloader, SIGNAL(newListAvailable(const QString&, const QDateTime&)),
aheinecke@16:             this, SLOT(handleNewList(const QString&, const QDateTime&)));
aheinecke@16:     connect(downloader, SIGNAL(newSoftwareAvailable(const QString&, const QDateTime&)),
aheinecke@16:             this, SLOT(handleNewSW(const QString&, const QDateTime&)));
aheinecke@16:     connect(downloader, SIGNAL(finished()), downloader, SLOT(deleteLater()));
aheinecke@45:     connect(downloader, SIGNAL(error(const QString &, SSLConnection::ErrorCode)),
aheinecke@45:             this, SLOT(downloaderError(const QString &, SSLConnection::ErrorCode)));
aheinecke@10:     downloader->start();
aheinecke@0: }
aheinecke@0: 
aheinecke@16: 
aheinecke@45: void MainWindow::downloaderError(const QString &message, SSLConnection::ErrorCode error)
aheinecke@16: {
aheinecke@16:     mCurMessage = message;
andre@27:     showMessage();
aheinecke@16: }
aheinecke@16: 
aheinecke@16: 
aheinecke@0: void MainWindow::createActions()
aheinecke@0: {
aheinecke@0:     mCheckUpdates = new QAction(tr("Check for Updates"), this);
aheinecke@16:     connect(mCheckUpdates, SIGNAL(triggered()), this, SLOT(checkUpdates()));
aheinecke@2:     mQuitAction = new QAction(tr("Quit"), this);
aheinecke@2:     connect(mQuitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
aheinecke@0: }
aheinecke@0: 
aheinecke@0: void MainWindow::createTrayIcon()
aheinecke@0: {
aheinecke@11:     QIcon trayImg(":/img/tray_22.png");
aheinecke@0: 
aheinecke@0:     mTrayMenu = new QMenu(this);
aheinecke@0:     mTrayMenu->addAction(mCheckUpdates);
aheinecke@2:     mTrayMenu->addAction(mQuitAction);
aheinecke@0: 
aheinecke@0:     mTrayIcon = new QSystemTrayIcon(this);
aheinecke@0:     mTrayIcon->setContextMenu(mTrayMenu);
aheinecke@0: 
aheinecke@0:     mTrayIcon->setIcon(trayImg);
aheinecke@0:     setWindowIcon(trayImg);
aheinecke@0:     mTrayIcon->show();
aheinecke@0:     mTrayIcon->setToolTip(tr("m13ui"));
aheinecke@71: 
aheinecke@71:     connect(mTrayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked()));
aheinecke@0: }
rrenkert@155: 
rrenkert@155: void MainWindow::createMenuBar()
rrenkert@155: {
rrenkert@155:     mMenuBar = new QMenuBar(this);
rrenkert@155:     QMenu *mMenu = new QMenu(tr("Menu"), mMenuBar);
rrenkert@155:     mMenuBar->addMenu(mMenu);
rrenkert@155:     QAction *update = mMenu->addAction(tr("Force Update"));
rrenkert@155:     QAction *settings = mMenu->addAction(tr("Settings"));
rrenkert@212:     QAction *status = mMenu->addAction(tr("Statusdialog"));
rrenkert@155:     mMenu->addSeparator();
rrenkert@155:     QAction *help = mMenu->addAction(tr("Help"));
rrenkert@155:     QAction *about = mMenu->addAction(tr("About"));
rrenkert@155:     mMenu->addSeparator();
rrenkert@155:     QAction *quit = mMenu->addAction(tr("Quit"));
rrenkert@155:     connect(update, SIGNAL(triggered()), this, SLOT(checkUpdates()));
rrenkert@155:     connect(settings, SIGNAL(triggered()), this, SLOT(showSettings()));
rrenkert@212:     connect(status, SIGNAL(triggered()), this, SLOT(showStatus()));
rrenkert@155:     connect(help, SIGNAL(triggered()), this, SLOT(showHelp()));
rrenkert@155:     connect(about, SIGNAL(triggered()), this, SLOT(showAbout()));
rrenkert@155:     connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
rrenkert@155:     setMenuBar(mMenuBar);
rrenkert@155: }
rrenkert@155: 
rrenkert@187: void MainWindow::createContent()
rrenkert@187: {
rrenkert@187:     // Create a central widget containing the main layout.
rrenkert@187:     QWidget *base = new QWidget;
rrenkert@187: 
rrenkert@250:     // Layouts and Container
rrenkert@250:     QHBoxLayout *mainLayout = new QHBoxLayout;
rrenkert@250:     QVBoxLayout *infoLayout = new QVBoxLayout;
rrenkert@250:     QVBoxLayout *certLayout = new QVBoxLayout;
rrenkert@205:     QHBoxLayout *headerLayout = new QHBoxLayout;
rrenkert@205:     QVBoxLayout *headerTextLayout = new QVBoxLayout;
rrenkert@250:     QVBoxLayout *toolLayout = new QVBoxLayout;
rrenkert@250:     QHBoxLayout *toolButtonLayout = new QHBoxLayout;
rrenkert@187: 
rrenkert@250:     // The certificate list
rrenkert@250:     QGroupBox *certBox = new QGroupBox(tr("Managed Certificates"));
rrenkert@250:     certificateList = new QListWidget;
rrenkert@250:     connect(certificateList, SIGNAL(itemClicked(QListWidgetItem*)),
rrenkert@250:         this, SLOT(showDetails(QListWidgetItem*)));
rrenkert@250:     certLayout->addWidget(certificateList);
rrenkert@250:     certBox->setLayout(certLayout);
rrenkert@250: 
rrenkert@250:     // The header (icon, about text)
rrenkert@205:     QImage *logoImage = new QImage(":/img/logo.png");
rrenkert@205:     QLabel *logo = new QLabel;
rrenkert@205:     logo->setBackgroundRole(QPalette::Base);
rrenkert@205:     logo->setPixmap(QPixmap::fromImage(*logoImage));
rrenkert@210:     QLabel *title = new QLabel("<h2>" + tr("Certificate Installer") + "</h2>");
rrenkert@205:     QLabel *subTitle = new QLabel("This Software installs and removes Certificates");
rrenkert@205:     headerTextLayout->addWidget(title);
rrenkert@205:     headerTextLayout->addWidget(subTitle);
rrenkert@205:     headerLayout->addWidget(logo);
rrenkert@205:     headerLayout->addLayout(headerTextLayout);
rrenkert@210:     headerLayout->setStretch(0, 0);
rrenkert@210:     headerLayout->setStretch(1, 10);
rrenkert@205: 
rrenkert@250:     // The tools to update the certificate list/software and install
rrenkert@250:     // certificates.
rrenkert@250:     QCheckBox *autoUpdateOption = new QCheckBox(tr("autoupdate"));
rrenkert@250:     toolLayout->addWidget(autoUpdateOption);
rrenkert@250: //    connect(autoUpdateOption, SIGNAL(stateChanged()), this, SLOT(setAutoUpdate()));
rrenkert@250:     QPushButton *update = new QPushButton(tr("Search for Updates"));
rrenkert@250:     connect(update, SIGNAL(clicked()), this, SLOT(checkUpdates()));
rrenkert@250:     QPushButton *install = new QPushButton(tr("Install selected"));
rrenkert@250: //    connect(update, SIGNAL(clicked()), this, SLOT(installCertificates()));
rrenkert@250:     toolButtonLayout->addWidget(update);
rrenkert@250:     toolButtonLayout->addWidget(install);
rrenkert@250:     toolButtonLayout->insertStretch(2, 10);
rrenkert@250:     toolLayout->addLayout(toolButtonLayout);
rrenkert@250: 
rrenkert@250:     // The certificate details
rrenkert@189:     certificateDetails = new QTextEdit;
rrenkert@189:     certificateDetails->setReadOnly(true);
rrenkert@187: 
rrenkert@250:     infoLayout->addSpacing(20);
rrenkert@250:     infoLayout->addLayout(headerLayout);
rrenkert@250:     infoLayout->addLayout(toolLayout);
rrenkert@250:     infoLayout->addWidget(certificateDetails);
rrenkert@250: 
rrenkert@187:     mainLayout->addWidget(certBox);
rrenkert@250:     mainLayout->addLayout(infoLayout);
rrenkert@250: 
rrenkert@187: 
rrenkert@187:     // QMainWindow allready has a layout. All child layouts and widgets are
rrenkert@187:     // managed in the central widget.
rrenkert@187:     base->setLayout(mainLayout);
rrenkert@187:     setCentralWidget(base);
rrenkert@187: }
rrenkert@187: 
rrenkert@189: void MainWindow::loadCertificateList()
rrenkert@189: {
rrenkert@189:     qDebug() << "display certificates";
rrenkert@189:     certificateList->clear();
aheinecke@249:     foreach (const Certificate &cert, mListToInstall.getCertificates()) {
rrenkert@189:         if (!cert.isValid()) {
rrenkert@189:             qWarning() << "Invalid certificate in list";
rrenkert@189:             continue;
rrenkert@189:         }
rrenkert@189:         QListWidgetItem* item = new QListWidgetItem(cert.shortDescription());
rrenkert@189:         item->setData(Qt::UserRole, cert.details());
aheinecke@249:         QIcon *certIcon = cert.isInstallCert() ? new QIcon(":/img/list-add.png"):
aheinecke@249:                                                  new QIcon(":/img/list-remove.png");
rrenkert@189:         item->setIcon(*certIcon);
rrenkert@189:         certificateList->addItem(item);
rrenkert@189:     }
rrenkert@189: }
rrenkert@189: 
rrenkert@189: void MainWindow::showSettings()
rrenkert@189: {
rrenkert@155:     qDebug() << "show settingsdialog";
rrenkert@155: }
rrenkert@155: 
rrenkert@212: void MainWindow::showStatus()
rrenkert@212: {
rrenkert@212:     qDebug() << "show settingsdialog";
rrenkert@212:     StatusDialog *status = new StatusDialog(this);
rrenkert@212:     status->show();
rrenkert@212: }
rrenkert@212: 
rrenkert@155: void MainWindow::showHelp()
rrenkert@155: {
rrenkert@155:     qDebug() << "show helpdialog";
rrenkert@191:     HelpDialog *help = new HelpDialog(this);
rrenkert@191:     help->show();
rrenkert@155: }
rrenkert@155: 
rrenkert@155: void MainWindow::showAbout()
rrenkert@155: {
rrenkert@155:     qDebug() << "show aboutdialog";
rrenkert@200:     AboutDialog *about = new AboutDialog(this);
rrenkert@200:     about->show();
rrenkert@155: }
rrenkert@187: 
rrenkert@189: void MainWindow::showDetails(QListWidgetItem *item)
rrenkert@189: {
rrenkert@189:     qDebug() << "show details";
rrenkert@189:     QString details = item->data(Qt::UserRole).toString();
rrenkert@210:     details.append("\rInstalled: 1.1.1971\rRemoved: 1.1.1971");
rrenkert@189:     certificateDetails->setPlainText(details);
rrenkert@189: }