summaryrefslogtreecommitdiff
path: root/src/frontend/bookshelfmanager/installpage/btinstallprogressdialog.cpp
blob: cc862faa02d21c33dffc57fd347ab08fd76a2b7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*********
*
* This file is part of BibleTime's source code, http://www.bibletime.info/.
*
* Copyright 1999-2011 by the BibleTime developers.
* The BibleTime source code is licensed under the GNU General Public License version 2.0.
*
**********/

#include "frontend/bookshelfmanager/installpage/btinstallprogressdialog.h"

#include <QApplication>
#include <QCloseEvent>
#include <QDebug>
#include <QDialog>
#include <QHeaderView>
#include <QMultiMap>
#include <QProgressBar>
#include <QPushButton>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include "backend/managers/cswordbackend.h"
#include "frontend/bookshelfmanager/installpage/btinstallthread.h"


BtInstallProgressDialog::BtInstallProgressDialog(
        const QSet<const CSwordModuleInfo*> &modules,
        const QString &destination, QWidget *parent, Qt::WindowFlags flags)
        : QDialog(parent, flags)
{
    // we want this dialog to be deleted when user closes it or the downloads are completed
    setAttribute(Qt::WA_DeleteOnClose, true);
    setWindowTitle(tr("Install Progress"));
    //create the dialog which shows the status and lets the user stop installation
    m_statusWidget = new QTreeWidget();
    m_statusWidget->setRootIsDecorated(false);
    m_statusWidget->setHeaderLabels(QStringList(tr("Work")) << tr("Progress") << QString::null);
    m_statusWidget->header()->setStretchLastSection(false);
#if QT_VERSION < 0x050000
    m_statusWidget->header()->setResizeMode(1, QHeaderView::Stretch);
    m_statusWidget->header()->setMovable(false);
#else
    m_statusWidget->header()->setSectionResizeMode(1, QHeaderView::Stretch);
    m_statusWidget->header()->setSectionsMovable(false);
#endif
    //m_statusWidget->setColumnWidth(1, util::tool::mWidth(m_statusWidget, 2));

    Q_FOREACH(const CSwordModuleInfo *module, modules) {
        const QString sourceName(module->property("installSourceName").toString());
        // create a thread for this module
        BtInstallThread* thread = new BtInstallThread(module->name(), sourceName, destination);
        m_waitingThreads.insert(sourceName, thread);
        m_threadsByModule.insert(module->name(), thread);
        // progress widget/item
        QPushButton* stopButton = new QPushButton(tr("Stop"), m_statusWidget);
        stopButton->setFixedSize(stopButton->sizeHint());

        // the item
        QTreeWidgetItem* progressItem = new QTreeWidgetItem(m_statusWidget);
        m_statusWidget->setColumnWidth(2, stopButton->sizeHint().width());
        progressItem->setSizeHint(2, stopButton->sizeHint());
        progressItem->setText(0, module->name());
        progressItem->setFlags(Qt::ItemIsEnabled);
        m_statusWidget->setItemWidget(progressItem, 2, stopButton);
        progressItem->setText(1, tr("Waiting for turn..."));

        //connect the signals between the dialog, items and threads
        QObject::connect(stopButton, SIGNAL(clicked()), thread, SLOT(slotStopInstall()), Qt::QueuedConnection);
        QObject::connect(thread, SIGNAL(installStopped(QString, QString)), this, SLOT(slotOneItemStopped(QString, QString)), Qt::QueuedConnection);
        //is this needed or is statusUpdated enough?
        QObject::connect(thread, SIGNAL(installCompleted(QString, QString, int)), this, SLOT(slotOneItemCompleted(QString, QString, int)), Qt::QueuedConnection);
        QObject::connect(thread, SIGNAL(statusUpdated(QString, int)), this, SLOT(slotStatusUpdated(QString, int)), Qt::QueuedConnection);
        QObject::connect(thread, SIGNAL(downloadStarted(QString)), this, SLOT(slotDownloadStarted(QString)), Qt::QueuedConnection);

        QObject::connect(thread, SIGNAL(preparingInstall(QString, QString)), this, SLOT(slotInstallStarted(QString, QString)), Qt::QueuedConnection);
    }

    m_statusWidget->setMinimumWidth(m_statusWidget->size().width());
    QPushButton* stopAllButton = new QPushButton(tr("Stop All"), this);

    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(m_statusWidget);
    layout->addWidget(stopAllButton);

    connect(stopAllButton, SIGNAL(clicked()), SLOT(slotStopInstall()) );

    qApp->processEvents();

    startThreads();

}

void BtInstallProgressDialog::startThreads() {
    // remove all the updated modules from the backend module list at once
    //foreach (QString mName, m_threadsByModule.keys()) {
    //}
    //QList<CSwordModuleInfo*> CSwordBackend::instance()()->takeModulesFromList(m_threadsByModule.keys());
    //loop through the multimap of the waiting threads, start at most 3 threads for each source
    QMultiMap<QString, BtInstallThread*>::iterator threadIterator = m_waitingThreads.end();
// concurrency is disabled for now
//     while (threadIterator != m_waitingThreads.end()) {
//         QString sourceName = threadIterator.key();
//         if (m_runningThreads.values(sourceName).count() < 3) {
//             BtInstallThread* t = threadIterator.value();
//             m_runningThreads.insert(sourceName, t);
//             threadIterator = m_waitingThreads.erase(threadIterator);
//             t->start();
//         }
//         else ++threadIterator;
//     }
    //non-concurrent
    if (threadIterator != m_waitingThreads.begin()) {
        // go to the last item which is actually the first in the visible list
        // because the iterator is reversed compared to insert order
        threadIterator--;
        QString sourceName = threadIterator.key();
        BtInstallThread* t = threadIterator.value();
        m_runningThreads.insert(sourceName, t);
        threadIterator = m_waitingThreads.erase(threadIterator);
        t->start();
    }
}

void BtInstallProgressDialog::slotOneItemCompleted(QString module, QString source, int status) {
    QString message;
    //status comes from the sword installer.
    /// \todo Additionally we should check that there are files really installed.
    if (status != 0) {
        message = tr("Failed");
    }
    else {
        message = tr("Completed");
    }
    oneItemStoppedOrCompleted(module, source, message);
}

void BtInstallProgressDialog::slotOneItemStopped(QString module, QString source) {
    oneItemStoppedOrCompleted(module, source, tr("Cancelled"));
}

void BtInstallProgressDialog::oneItemStoppedOrCompleted(QString module, QString source, QString statusMessage) {
    qDebug() << "\n**********************************\nBtInstallProgressDialog::oneItemStoppedOrCompleted" << module << statusMessage << "\n******************************************";
    // update the list item
    m_statusWidget->setItemWidget(getItem(module), 1, 0);
    getItem(module)->setText(1, statusMessage);
    m_statusWidget->itemWidget(getItem(module), 2)->setEnabled(false);
    getItem(module)->setDisabled(true);

    qDebug() << "remove from threads maps" << source << m_threadsByModule.value(module);
    m_runningThreads.remove(source, m_threadsByModule.value(module));
    m_waitingThreads.remove(source, m_threadsByModule.value(module));

//concurrency is disabled for now
//     //start a waiting thread if there are any
//     QList<BtInstallThread*> threadsForSource = m_waitingThreads.values(source);
//     if (!threadsForSource.isEmpty()) {
//         BtInstallThread* thread = threadsForSource.at(0);
//         m_waitingThreads.remove(source, thread);
//         m_runningThreads.insert(source, thread);
//         thread->start();
//     }

    //non-concurrent
    QMultiMap<QString, BtInstallThread*>::iterator threadIterator = m_waitingThreads.end();
    if (m_runningThreads.isEmpty() && threadIterator != m_waitingThreads.begin()) {
        threadIterator--; // the last item
        QString sourceName = threadIterator.key();
        BtInstallThread* t = threadIterator.value();
        m_runningThreads.insert(sourceName, t);
        threadIterator = m_waitingThreads.erase(threadIterator);
        t->start();
    }

    if (threadsDone()) {
        qDebug() << "close the dialog";
        close();
    }
}

void BtInstallProgressDialog::slotStopInstall() {
    qDebug() << "BtInstallProgressDialog::slotStopInstall";

    // Clear the waiting threads map, stop all running threads.

    m_waitingThreads.clear();
    if (m_runningThreads.count() > 0) {
        foreach(BtInstallThread* thread, m_runningThreads) {
            thread->slotStopInstall();
        }
    }
    else {
        close();
    }
}

void BtInstallProgressDialog::slotStatusUpdated(QString module, int status) {
    // find the progress bar for this module and update the value
    QWidget* itemWidget = m_statusWidget->itemWidget(getItem(module) , 1);
    QProgressBar* bar = dynamic_cast<QProgressBar*>(itemWidget);
    if (bar) bar->setValue(status);
}

void BtInstallProgressDialog::slotInstallStarted(QString module, QString) {
    getItem(module)->setText(1, tr("Preparing install..."));
}

void BtInstallProgressDialog::slotDownloadStarted(QString module) {
    getItem(module)->setText(1, QString::null);
    //m_statusWidget->itemWidget(getItem(module), 1)->setVisible(true);

    QProgressBar* bar = new QProgressBar(m_statusWidget);
    bar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    bar->setValue(0);
    m_statusWidget->setItemWidget(getItem(module), 1, bar);
}

QTreeWidgetItem* BtInstallProgressDialog::getItem(QString moduleName) {
    return m_statusWidget->findItems(moduleName, Qt::MatchExactly).at(0);
}

void BtInstallProgressDialog::closeEvent(QCloseEvent* event) {
    if (event->spontaneous()) {
        event->ignore();
        return;
    }
    // other parts of the UI/engine must be updated
    CSwordBackend::instance()->reloadModules(CSwordBackend::AddedModules);
}

bool BtInstallProgressDialog::threadsDone() {
    return (m_waitingThreads.isEmpty() && m_runningThreads.isEmpty());
}