verifychecksumsdialog.cpp 12.1 KB
Newer Older
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
/* -*- mode: c++; c-basic-offset:4 -*-
    crypto/gui/verifychecksumsdialog.cpp

    This file is part of Kleopatra, the KDE keymanager
    Copyright (c) 2010 Klarälvdalens Datakonsult AB

    Kleopatra is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Kleopatra is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#include <config-kleopatra.h>

#include "verifychecksumsdialog.h"

37
38
#ifndef QT_NO_DIRMODEL

39
#include <KLocalizedString>
Thomas McGuire's avatar
Thomas McGuire committed
40
#include <KMessageBox>
41
42
43
44
45
46
47
48

#include <QHBoxLayout>
#include <QLabel>
#include <QStringList>
#include <QVBoxLayout>
#include <QHash>
#include <QTreeView>
#include <QSortFilterProxyModel>
Thomas McGuire's avatar
Thomas McGuire committed
49
#include <QDirModel>
50
51
#include <QProgressBar>
#include <QDialogButtonBox>
Thomas McGuire's avatar
Thomas McGuire committed
52
#include <QPushButton>
Allen Winter's avatar
Allen Winter committed
53
#include <QHeaderView>
Laurent Montel's avatar
Laurent Montel committed
54
#include "kleopatra_debug.h"
55
56
57
58
59
60
61

#include <cassert>

using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;

Laurent Montel's avatar
Laurent Montel committed
62
63
namespace
{
64

Laurent Montel's avatar
Laurent Montel committed
65
66
67
68
69
70
static Qt::GlobalColor statusColor[] = {
    Qt::color0,   // Unknown - nothing
    Qt::green,    // OK
    Qt::red,      // Failed
    Qt::darkRed,  // Error
};
Daniel Vrátil's avatar
Daniel Vrátil committed
71
static_assert((sizeof(statusColor) / sizeof(*statusColor)) == VerifyChecksumsDialog::NumStatii, "");
72

Laurent Montel's avatar
Laurent Montel committed
73
74
75
76
class ColorizedFileSystemModel : public QDirModel
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
77
    explicit ColorizedFileSystemModel(QObject *parent = Q_NULLPTR)
Laurent Montel's avatar
Laurent Montel committed
78
79
80
        : QDirModel(parent),
          statusMap()
    {
81

Laurent Montel's avatar
Laurent Montel committed
82
    }
83

Sergio Martins's avatar
Sergio Martins committed
84
    QVariant data(const QModelIndex &mi, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE
Laurent Montel's avatar
Laurent Montel committed
85
86
87
88
89
90
91
92
93
94
95
    {
        if (mi.isValid() && role == Qt::BackgroundRole) {
            const QHash<QString, VerifyChecksumsDialog::Status>::const_iterator
            it = statusMap.find(filePath(mi));
            if (it != statusMap.end())
                if (const Qt::GlobalColor c = statusColor[*it]) {
                    return QColor(c);
                }
        }
        return QDirModel::data(mi, role);
    }
96

Laurent Montel's avatar
Laurent Montel committed
97
98
99
public Q_SLOTS:
    void setStatus(const QString &file, VerifyChecksumsDialog::Status status)
    {
100

Laurent Montel's avatar
Laurent Montel committed
101
102
103
104
105
106
107
108
        if (status >= VerifyChecksumsDialog::NumStatii || file.isEmpty()) {
            return;
        }

        // canonicalize filename:
        const QModelIndex mi = index(file);
        const QString canonical = filePath(mi);
        if (canonical.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
109
            qCDebug(KLEOPATRA_LOG) << "can't locate file " << file;
Laurent Montel's avatar
Laurent Montel committed
110
111
            return;
        }
112

Laurent Montel's avatar
Laurent Montel committed
113
114
        const QHash<QString, VerifyChecksumsDialog::Status>::iterator
        it = statusMap.find(canonical);
115

Laurent Montel's avatar
Laurent Montel committed
116
117
118
119
120
121
122
123
        if (it != statusMap.end())
            if (*it == status) {
                return;    // nothing to do
            } else {
                *it = status;
            }
        else {
            statusMap[canonical] = status;
124
125
        }

Laurent Montel's avatar
Laurent Montel committed
126
127
        emitDataChangedFor(mi);
    }
128

Laurent Montel's avatar
Laurent Montel committed
129
130
131
    void clearStatusInformation()
    {
        using std::swap;
132

Laurent Montel's avatar
Laurent Montel committed
133
134
        QHash<QString, VerifyChecksumsDialog::Status> oldStatusMap;
        swap(statusMap, oldStatusMap);
135

Laurent Montel's avatar
Laurent Montel committed
136
        for (QHash<QString, VerifyChecksumsDialog::Status>::const_iterator it = oldStatusMap.constBegin(), end = oldStatusMap.constEnd(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
137
            emitDataChangedFor(it.key());
138
        }
Laurent Montel's avatar
Laurent Montel committed
139
    }
140

Laurent Montel's avatar
Laurent Montel committed
141
142
143
144
145
146
147
148
private:
    void emitDataChangedFor(const QString &file)
    {
        emitDataChangedFor(index(file));
    }
    void emitDataChangedFor(const QModelIndex &mi)
    {
        const QModelIndex p = parent(mi);
Laurent Montel's avatar
Laurent Montel committed
149
        Q_EMIT dataChanged(index(mi.row(), 0, p), index(mi.row(), columnCount(p) - 1, p));
Laurent Montel's avatar
Laurent Montel committed
150
    }
151

Laurent Montel's avatar
Laurent Montel committed
152
153
154
private:
    QHash<QString, VerifyChecksumsDialog::Status> statusMap;
};
155

Laurent Montel's avatar
Laurent Montel committed
156
157
static int find_layout_item(const QBoxLayout &blay)
{
Laurent Montel's avatar
Laurent Montel committed
158
    for (int i = 0, end = blay.count(); i < end; ++i)
Laurent Montel's avatar
Laurent Montel committed
159
160
161
162
163
164
        if (QLayoutItem *item = blay.itemAt(i))
            if (item->layout()) {
                return i;
            }
    return 0;
}
165

Laurent Montel's avatar
Laurent Montel committed
166
167
168
169
struct BaseWidget {
    QSortFilterProxyModel proxy;
    QLabel label;
    QTreeView view;
170

Laurent Montel's avatar
Laurent Montel committed
171
172
173
174
175
176
177
178
    BaseWidget(QDirModel *model, QWidget *parent, QVBoxLayout *vlay)
        : proxy(),
          label(parent),
          view(parent)
    {
        KDAB_SET_OBJECT_NAME(proxy);
        KDAB_SET_OBJECT_NAME(label);
        KDAB_SET_OBJECT_NAME(view);
179

Laurent Montel's avatar
Laurent Montel committed
180
181
182
        const int row = find_layout_item(*vlay);
        vlay->insertWidget(row,   &label);
        vlay->insertWidget(row + 1, &view, 1);
183

Laurent Montel's avatar
Laurent Montel committed
184
        proxy.setSourceModel(model);
185

Laurent Montel's avatar
Laurent Montel committed
186
        view.setModel(&proxy);
187

Laurent Montel's avatar
Laurent Montel committed
188
189
190
191
        QRect r;
        for (int i = 0; i < proxy.columnCount(); ++i) {
            view.resizeColumnToContents(i);
        }
192

Laurent Montel's avatar
Laurent Montel committed
193
194
195
196
197
        // define some minimum sizes
        view.header()->resizeSection(0, qMax(view.header()->sectionSize(0), 220));
        view.header()->resizeSection(1, qMax(view.header()->sectionSize(1), 75));
        view.header()->resizeSection(2, qMax(view.header()->sectionSize(2), 75));
        view.header()->resizeSection(3, qMax(view.header()->sectionSize(3), 140));
198

Laurent Montel's avatar
Laurent Montel committed
199
200
        for (int i = 0; i < proxy.rowCount(); ++i) {
            r = r.united(view.visualRect(proxy.index(proxy.columnCount() - 1, i)));
201
        }
Laurent Montel's avatar
Laurent Montel committed
202
203
204
        view.setMinimumSize(QSize(qBound(r.width() + 4 * view.frameWidth(), 220 + 75 + 75 + 140 + 4 * view.frameWidth(), 1024), // 100 is the default defaultSectionSize
                                  qBound(r.height(), 220, 512)));
    }
205

Laurent Montel's avatar
Laurent Montel committed
206
207
208
209
210
211
    void setBase(const QString &base)
    {
        label.setText(base);
        if (QDirModel *fsm = qobject_cast<QDirModel *>(proxy.sourceModel())) {
            view.setRootIndex(proxy.mapFromSource(fsm->index(base)));
        } else {
Laurent Montel's avatar
Laurent Montel committed
212
            qCWarning(KLEOPATRA_LOG) << "expect a QDirModel-derived class as proxy.sourceModel(), got ";
Laurent Montel's avatar
Laurent Montel committed
213
            if (!proxy.sourceModel()) {
Laurent Montel's avatar
Laurent Montel committed
214
                qCWarning(KLEOPATRA_LOG) << "a null pointer";
215
            } else {
Laurent Montel's avatar
Laurent Montel committed
216
                qCWarning(KLEOPATRA_LOG) << proxy.sourceModel()->metaObject()->className();
217
            }
218
        }
Laurent Montel's avatar
Laurent Montel committed
219
220
    }
};
221
222
223

} // anon namespace

Laurent Montel's avatar
Laurent Montel committed
224
225
class VerifyChecksumsDialog::Private
{
226
    friend class ::Kleo::Crypto::Gui::VerifyChecksumsDialog;
Laurent Montel's avatar
Laurent Montel committed
227
    VerifyChecksumsDialog *const q;
228
public:
Laurent Montel's avatar
Laurent Montel committed
229
230
    explicit Private(VerifyChecksumsDialog *qq)
        : q(qq),
231
          bases(),
Thomas McGuire's avatar
Thomas McGuire committed
232
          errors(),
233
          model(),
Laurent Montel's avatar
Laurent Montel committed
234
          ui(q)
235
    {
Laurent Montel's avatar
Laurent Montel committed
236
        qRegisterMetaType<Status>("Kleo::Crypto::Gui::VerifyChecksumsDialog::Status");
237
238
    }

Thomas McGuire's avatar
Thomas McGuire committed
239
private:
Laurent Montel's avatar
Laurent Montel committed
240
241
242
243
    void slotErrorButtonClicked()
    {
        KMessageBox::errorList(q, i18n("The following errors and warnings were recorded:"),
                               errors, i18n("Checksum Verification Errors"));
Thomas McGuire's avatar
Thomas McGuire committed
244
245
246
    }

private:
Laurent Montel's avatar
Laurent Montel committed
247
248
    void updateErrors()
    {
Thomas McGuire's avatar
Thomas McGuire committed
249
        const bool active = ui.isProgressBarActive();
Laurent Montel's avatar
Laurent Montel committed
250
251
252
253
254
255
256
257
258
        ui.progressLabel.setVisible(active);
        ui.progressBar.  setVisible(active);
        ui.errorLabel.   setVisible(!active);
        ui.errorButton.  setVisible(!active && !errors.empty());
        if (errors.empty()) {
            ui.errorLabel.setText(i18n("No errors occurred"));
        } else {
            ui.errorLabel.setText(i18np("One error occurred", "%1 errors occurred", errors.size()));
        }
Thomas McGuire's avatar
Thomas McGuire committed
259
260
    }

261
262
private:
    QStringList bases;
Thomas McGuire's avatar
Thomas McGuire committed
263
    QStringList errors;
264
    ColorizedFileSystemModel model;
265

266
    struct UI {
Laurent Montel's avatar
Laurent Montel committed
267
        std::vector<BaseWidget *> baseWidgets;
268
269
        QLabel progressLabel;
        QProgressBar progressBar;
Thomas McGuire's avatar
Thomas McGuire committed
270
271
        QLabel errorLabel;
        QPushButton errorButton;
272
273
        QDialogButtonBox buttonBox;
        QVBoxLayout vlay;
Thomas McGuire's avatar
Thomas McGuire committed
274
        QHBoxLayout hlay[2];
275

Laurent Montel's avatar
Laurent Montel committed
276
        explicit UI(VerifyChecksumsDialog *q)
277
            : baseWidgets(),
Laurent Montel's avatar
Laurent Montel committed
278
279
280
281
282
283
              progressLabel(i18n("Progress:"), q),
              progressBar(q),
              errorLabel(i18n("No errors occurred"), q),
              errorButton(i18nc("Show Errors", "Show"), q),
              buttonBox(QDialogButtonBox::Close, Qt::Horizontal, q),
              vlay(q)
284
        {
Laurent Montel's avatar
Laurent Montel committed
285
286
287
288
289
290
291
292
            KDAB_SET_OBJECT_NAME(progressLabel);
            KDAB_SET_OBJECT_NAME(progressBar);
            KDAB_SET_OBJECT_NAME(errorLabel);
            KDAB_SET_OBJECT_NAME(errorButton);
            KDAB_SET_OBJECT_NAME(buttonBox);
            KDAB_SET_OBJECT_NAME(vlay);
            KDAB_SET_OBJECT_NAME(hlay[0]);
            KDAB_SET_OBJECT_NAME(hlay[1]);
Thomas McGuire's avatar
Thomas McGuire committed
293

Laurent Montel's avatar
Laurent Montel committed
294
            errorButton.setAutoDefault(false);
295

Laurent Montel's avatar
Laurent Montel committed
296
297
            hlay[0].addWidget(&progressLabel);
            hlay[0].addWidget(&progressBar, 1);
298

Laurent Montel's avatar
Laurent Montel committed
299
300
            hlay[1].addWidget(&errorLabel, 1);
            hlay[1].addWidget(&errorButton);
Thomas McGuire's avatar
Thomas McGuire committed
301

Laurent Montel's avatar
Laurent Montel committed
302
303
304
            vlay.addLayout(&hlay[0]);
            vlay.addLayout(&hlay[1]);
            vlay.addWidget(&buttonBox);
305

Thomas McGuire's avatar
Thomas McGuire committed
306
307
308
            errorLabel.hide();
            errorButton.hide();

Laurent Montel's avatar
Laurent Montel committed
309
            QPushButton *close = closeButton();
310

Laurent Montel's avatar
Laurent Montel committed
311
312
            connect(close, &QPushButton::clicked, q, &VerifyChecksumsDialog::canceled);
            connect(close, &QPushButton::clicked, q, &VerifyChecksumsDialog::accept);
Thomas McGuire's avatar
Thomas McGuire committed
313

Laurent Montel's avatar
Laurent Montel committed
314
            connect(&errorButton, SIGNAL(clicked()), q, SLOT(slotErrorButtonClicked()));
315
316
        }

Laurent Montel's avatar
Laurent Montel committed
317
318
319
        ~UI()
        {
            qDeleteAll(baseWidgets);
320
321
        }

Laurent Montel's avatar
Laurent Montel committed
322
323
324
        QPushButton *closeButton() const
        {
            return buttonBox.button(QDialogButtonBox::Close);
325
326
        }

Laurent Montel's avatar
Laurent Montel committed
327
328
        void setBases(const QStringList &bases, QDirModel *model)
        {
329
330

            // create new BaseWidgets:
Laurent Montel's avatar
Laurent Montel committed
331
            for (unsigned int i = baseWidgets.size(), end = bases.size(); i < end; ++i) {
Laurent Montel's avatar
Laurent Montel committed
332
333
                baseWidgets.push_back(new BaseWidget(model, vlay.parentWidget(), &vlay));
            }
334
335

            // shed surplus BaseWidgets:
Laurent Montel's avatar
Laurent Montel committed
336
            for (unsigned int i = bases.size(), end = baseWidgets.size(); i < end; ++i) {
337
338
339
340
                delete baseWidgets.back();
                baseWidgets.pop_back();
            }

Laurent Montel's avatar
Laurent Montel committed
341
            assert(static_cast<unsigned>(bases.size()) == baseWidgets.size());
342
343

            // update bases:
Laurent Montel's avatar
Laurent Montel committed
344
            for (unsigned int i = 0; i < baseWidgets.size(); ++i) {
Laurent Montel's avatar
Laurent Montel committed
345
346
                baseWidgets[i]->setBase(bases[i]);
            }
347
348
349

        }

Laurent Montel's avatar
Laurent Montel committed
350
351
352
353
        void setProgress(int cur, int tot)
        {
            progressBar.setMaximum(tot);
            progressBar.setValue(cur);
Thomas McGuire's avatar
Thomas McGuire committed
354
355
        }

Laurent Montel's avatar
Laurent Montel committed
356
357
        bool isProgressBarActive() const
        {
Thomas McGuire's avatar
Thomas McGuire committed
358
359
            const int tot = progressBar.maximum();
            const int cur = progressBar.value();
Laurent Montel's avatar
Laurent Montel committed
360
            return !tot || cur != tot;
361
362
363
364
365
        }

    } ui;
};

Laurent Montel's avatar
Laurent Montel committed
366
367
VerifyChecksumsDialog::VerifyChecksumsDialog(QWidget *parent)
    : QDialog(parent),
Laurent Montel's avatar
Laurent Montel committed
368
      d(new Private(this))
369
370
371
372
373
374
{

}

VerifyChecksumsDialog::~VerifyChecksumsDialog() {}

Thomas McGuire's avatar
Thomas McGuire committed
375
// slot
Laurent Montel's avatar
Laurent Montel committed
376
377
378
void VerifyChecksumsDialog::setBaseDirectories(const QStringList &bases)
{
    if (d->bases == bases) {
379
        return;
Laurent Montel's avatar
Laurent Montel committed
380
    }
381
    d->bases = bases;
Laurent Montel's avatar
Laurent Montel committed
382
    d->ui.setBases(bases, &d->model);
383
384
}

Thomas McGuire's avatar
Thomas McGuire committed
385
// slot
Laurent Montel's avatar
Laurent Montel committed
386
387
388
void VerifyChecksumsDialog::setErrors(const QStringList &errors)
{
    if (d->errors == errors) {
Thomas McGuire's avatar
Thomas McGuire committed
389
        return;
Laurent Montel's avatar
Laurent Montel committed
390
    }
Thomas McGuire's avatar
Thomas McGuire committed
391
392
393
394
    d->errors = errors;
    d->updateErrors();
}

395
// slot
Laurent Montel's avatar
Laurent Montel committed
396
397
398
void VerifyChecksumsDialog::setProgress(int cur, int tot)
{
    d->ui.setProgress(cur, tot);
Thomas McGuire's avatar
Thomas McGuire committed
399
    d->updateErrors();
400
401
402
}

// slot
Laurent Montel's avatar
Laurent Montel committed
403
404
405
void VerifyChecksumsDialog::setStatus(const QString &file, Status status)
{
    d->model.setStatus(file, status);
406
407
408
}

// slot
Laurent Montel's avatar
Laurent Montel committed
409
410
void VerifyChecksumsDialog::clearStatusInformation()
{
Thomas McGuire's avatar
Thomas McGuire committed
411
412
    d->errors.clear();
    d->updateErrors();
413
414
415
416
417
    d->model.clearStatusInformation();
}

#include "verifychecksumsdialog.moc"
#include "moc_verifychecksumsdialog.cpp"
418
419

#endif // QT_NO_DIRMODEL