keepassxc

Форк
0
/
EditWidgetIcons.cpp 
372 строки · 12.2 Кб
1
/*
2
 *  Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
3
 *  Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation, either version 2 or (at your option)
8
 *  version 3 of the License.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18

19
#include "EditWidgetIcons.h"
20
#include "ui_EditWidgetIcons.h"
21

22
#include "core/Clock.h"
23
#include "core/Config.h"
24
#include "core/Database.h"
25
#include "core/Metadata.h"
26
#include "core/Tools.h"
27
#include "gui/FileDialog.h"
28
#include "gui/IconModels.h"
29
#include "gui/Icons.h"
30
#include "gui/MessageBox.h"
31
#ifdef WITH_XC_NETWORKING
32
#include "gui/IconDownloader.h"
33
#endif
34

35
#include <QKeyEvent>
36

37
IconStruct::IconStruct()
38
    : uuid(QUuid())
39
    , number(0)
40
    , applyTo(ApplyIconToOptions::THIS_ONLY)
41
{
42
}
43

44
EditWidgetIcons::EditWidgetIcons(QWidget* parent)
45
    : QWidget(parent)
46
    , m_ui(new Ui::EditWidgetIcons())
47
    , m_db(nullptr)
48
    , m_applyIconTo(ApplyIconToOptions::THIS_ONLY)
49
    , m_defaultIconModel(new DefaultIconModel(this))
50
    , m_customIconModel(new CustomIconModel(this))
51
#ifdef WITH_XC_NETWORKING
52
    , m_downloader(new IconDownloader())
53
#endif
54
{
55
    m_ui->setupUi(this);
56

57
    m_ui->defaultIconsView->setModel(m_defaultIconModel);
58
    m_ui->customIconsView->setModel(m_customIconModel);
59

60
    m_ui->applyIconToPushButton->setMenu(createApplyIconToMenu());
61

62
    // clang-format off
63
    connect(m_ui->defaultIconsView, SIGNAL(clicked(QModelIndex)), this, SLOT(updateRadioButtonDefaultIcons()));
64
    connect(m_ui->customIconsView, SIGNAL(clicked(QModelIndex)), this, SLOT(updateRadioButtonCustomIcons()));
65
    connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SLOT(updateWidgetsDefaultIcons(bool)));
66
    connect(m_ui->customIconsRadio, SIGNAL(toggled(bool)), this, SLOT(updateWidgetsCustomIcons(bool)));
67
    connect(m_ui->addButton, SIGNAL(clicked()), SLOT(addCustomIconFromFile()));
68
    connect(m_ui->faviconButton, SIGNAL(clicked()), SLOT(downloadFavicon()));
69
    connect(m_ui->applyIconToPushButton->menu(), SIGNAL(triggered(QAction*)), SLOT(confirmApplyIconTo(QAction*)));
70

71
    connect(m_ui->defaultIconsRadio, SIGNAL(toggled(bool)), this, SIGNAL(widgetUpdated()));
72
    connect(m_ui->defaultIconsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
73
            this, SIGNAL(widgetUpdated()));
74
    connect(m_ui->customIconsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
75
            this, SIGNAL(widgetUpdated()));
76
#ifdef WITH_XC_NETWORKING
77
    connect(m_downloader.data(),
78
            SIGNAL(finished(const QString&, const QImage&)),
79
            SLOT(iconReceived(const QString&, const QImage&)));
80
#endif
81
    // clang-format on
82

83
#ifndef WITH_XC_NETWORKING
84
    m_ui->faviconButton->setVisible(false);
85
    m_ui->faviconURL->setVisible(false);
86
#endif
87
}
88

89
EditWidgetIcons::~EditWidgetIcons() = default;
90

91
IconStruct EditWidgetIcons::state()
92
{
93
    Q_ASSERT(m_db);
94
    Q_ASSERT(!m_currentUuid.isNull());
95

96
    IconStruct iconStruct;
97
    if (m_ui->defaultIconsRadio->isChecked()) {
98
        QModelIndex index = m_ui->defaultIconsView->currentIndex();
99
        if (index.isValid()) {
100
            iconStruct.number = index.row();
101
        } else {
102
            Q_ASSERT(false);
103
        }
104
    } else {
105
        QModelIndex index = m_ui->customIconsView->currentIndex();
106
        if (index.isValid()) {
107
            iconStruct.uuid = m_customIconModel->uuidFromIndex(m_ui->customIconsView->currentIndex());
108
        } else {
109
            iconStruct.number = -1;
110
        }
111
    }
112

113
    iconStruct.applyTo = m_applyIconTo;
114

115
    return iconStruct;
116
}
117

118
void EditWidgetIcons::reset()
119
{
120
    m_db.reset();
121
    m_currentUuid = QUuid();
122
}
123

124
void EditWidgetIcons::load(const QUuid& currentUuid,
125
                           const QSharedPointer<Database>& database,
126
                           const IconStruct& iconStruct,
127
                           const QString& url)
128
{
129
    Q_ASSERT(database);
130
    Q_ASSERT(!currentUuid.isNull());
131

132
    m_db = database;
133
    m_currentUuid = currentUuid;
134
    setUrl(url);
135

136
    m_customIconModel->setIcons(Icons::customIconsPixmaps(database.data(), IconSize::Default),
137
                                database->metadata()->customIconsOrder());
138

139
    QUuid iconUuid = iconStruct.uuid;
140
    if (iconUuid.isNull()) {
141
        int iconNumber = iconStruct.number;
142
        m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(iconNumber, 0));
143
        m_ui->defaultIconsRadio->setChecked(true);
144
    } else {
145
        QModelIndex index = m_customIconModel->indexFromUuid(iconUuid);
146
        if (index.isValid()) {
147
            m_ui->customIconsView->setCurrentIndex(index);
148
            m_ui->customIconsRadio->setChecked(true);
149
        } else {
150
            m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
151
            m_ui->defaultIconsRadio->setChecked(true);
152
        }
153
    }
154

155
    m_applyIconTo = ApplyIconToOptions::THIS_ONLY;
156
    m_ui->applyIconToPushButton->menu()->defaultAction()->activate(QAction::Trigger);
157
}
158

159
void EditWidgetIcons::setShowApplyIconToButton(bool state)
160
{
161
    m_ui->applyIconToPushButton->setVisible(state);
162
}
163

164
QMenu* EditWidgetIcons::createApplyIconToMenu()
165
{
166
    auto* applyIconToMenu = new QMenu(this);
167
    QAction* defaultAction = applyIconToMenu->addAction(tr("Apply to this group only"));
168
    defaultAction->setData(QVariant::fromValue(ApplyIconToOptions::THIS_ONLY));
169
    applyIconToMenu->setDefaultAction(defaultAction);
170
    applyIconToMenu->addSeparator();
171
    applyIconToMenu->addAction(tr("Also apply to child groups"))
172
        ->setData(QVariant::fromValue(ApplyIconToOptions::CHILD_GROUPS));
173
    applyIconToMenu->addAction(tr("Also apply to child entries"))
174
        ->setData(QVariant::fromValue(ApplyIconToOptions::CHILD_ENTRIES));
175
    applyIconToMenu->addAction(tr("Also apply to all children"))
176
        ->setData(QVariant::fromValue(ApplyIconToOptions::ALL_CHILDREN));
177
    return applyIconToMenu;
178
}
179

180
void EditWidgetIcons::keyPressEvent(QKeyEvent* event)
181
{
182
    if (m_ui->faviconURL->hasFocus() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) {
183
        m_ui->faviconButton->animateClick();
184
    } else {
185
        QWidget::keyPressEvent(event);
186
    }
187
}
188

189
void EditWidgetIcons::setUrl(const QString& url)
190
{
191
#ifdef WITH_XC_NETWORKING
192
    QUrl urlCheck(url);
193
    if (urlCheck.scheme().startsWith("http")) {
194
        m_ui->faviconURL->setText(urlCheck.url(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment));
195
        m_ui->faviconURL->setCursorPosition(0);
196
    } else {
197
        m_ui->faviconURL->setText("");
198
    }
199
#else
200
    Q_UNUSED(url);
201
#endif
202
}
203

204
void EditWidgetIcons::downloadFavicon()
205
{
206
#ifdef WITH_XC_NETWORKING
207
    auto url = m_ui->faviconURL->text();
208
    if (!url.isEmpty()) {
209
        m_downloader->setUrl(url);
210
        m_downloader->download();
211
    }
212
#endif
213
}
214

215
void EditWidgetIcons::iconReceived(const QString& url, const QImage& icon)
216
{
217
#ifdef WITH_XC_NETWORKING
218
    Q_UNUSED(url);
219
    if (icon.isNull()) {
220
        QString message(tr("Unable to fetch favicon."));
221
        if (!config()->get(Config::Security_IconDownloadFallback).toBool()) {
222
            message.append("\n").append(
223
                tr("You can enable the DuckDuckGo website icon service under Application Settings -> Security"));
224
        }
225
        emit messageEditEntry(message, MessageWidget::Error);
226
        return;
227
    }
228

229
    if (!addCustomIcon(icon)) {
230
        emit messageEditEntry(tr("Existing icon selected."), MessageWidget::Information);
231
    }
232
#else
233
    Q_UNUSED(url);
234
    Q_UNUSED(icon);
235
#endif
236
}
237

238
void EditWidgetIcons::abortRequests()
239
{
240
#ifdef WITH_XC_NETWORKING
241
    if (m_downloader) {
242
        m_downloader->abortDownload();
243
    }
244
#endif
245
}
246

247
void EditWidgetIcons::addCustomIconFromFile()
248
{
249
    if (!m_db) {
250
        return;
251
    }
252

253
    auto filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), Icons::imageFormatsFilter(), tr("All files"));
254
    auto filenames =
255
        fileDialog()->getOpenFileNames(this, tr("Select Image(s)"), FileDialog::getLastDir("icons"), filter);
256
    if (!filenames.empty()) {
257
        QStringList errornames;
258
        int numexisting = 0;
259
        for (const auto& filename : filenames) {
260
            if (!filename.isEmpty()) {
261
                auto icon = QImage(filename);
262
                if (icon.isNull()) {
263
                    errornames << filename;
264
                } else if (!addCustomIcon(icon, QFileInfo(filename).baseName())) {
265
                    // Icon already exists in database
266
                    ++numexisting;
267
                }
268
            }
269
        }
270

271
        // Save last used directory using first image path
272
        FileDialog::saveLastDir("icons", filenames[0]);
273

274
        int numloaded = filenames.size() - errornames.size() - numexisting;
275
        QString msg;
276

277
        if (numloaded > 0) {
278
            msg = tr("Successfully loaded %1 of %n icon(s)", "", filenames.size()).arg(numloaded);
279
        } else {
280
            msg = tr("No icons were loaded");
281
        }
282

283
        if (numexisting > 0) {
284
            msg += "\n" + tr("%n icon(s) already exist in the database", "", numexisting);
285
        }
286

287
        if (!errornames.empty()) {
288
            // Show the first 8 icons that failed to load
289
            errornames = errornames.mid(0, 8);
290
            emit messageEditEntry(msg + "\n" + tr("The following icon(s) failed:", "", errornames.size()) + "\n"
291
                                      + errornames.join("\n"),
292
                                  MessageWidget::Error);
293
        } else if (numloaded > 0) {
294
            emit messageEditEntry(msg, MessageWidget::Positive);
295
        } else {
296
            emit messageEditEntry(msg, MessageWidget::Information);
297
        }
298
    }
299
}
300

301
bool EditWidgetIcons::addCustomIcon(const QImage& icon, const QString& name)
302
{
303
    bool added = false;
304
    if (m_db) {
305
        // Don't add an icon larger than 128x128, but retain original size if smaller
306
        auto scaledIcon = icon;
307
        if (icon.width() > 128 || icon.height() > 128) {
308
            scaledIcon = icon.scaled(128, 128);
309
        }
310

311
        QByteArray serializedIcon = Icons::saveToBytes(scaledIcon);
312
        QUuid uuid = m_db->metadata()->findCustomIcon(serializedIcon);
313
        if (uuid.isNull()) {
314
            uuid = QUuid::createUuid();
315
            m_db->metadata()->addCustomIcon(uuid, serializedIcon, name, Clock::currentDateTimeUtc());
316
            m_customIconModel->setIcons(Icons::customIconsPixmaps(m_db.data(), IconSize::Default),
317
                                        m_db->metadata()->customIconsOrder());
318
            added = true;
319
        }
320

321
        // Select the new or existing icon
322
        updateRadioButtonCustomIcons();
323
        QModelIndex index = m_customIconModel->indexFromUuid(uuid);
324
        m_ui->customIconsView->setCurrentIndex(index);
325

326
        emit widgetUpdated();
327
    }
328

329
    return added;
330
}
331

332
void EditWidgetIcons::updateWidgetsDefaultIcons(bool check)
333
{
334
    if (check) {
335
        QModelIndex index = m_ui->defaultIconsView->currentIndex();
336
        if (!index.isValid()) {
337
            m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
338
        } else {
339
            m_ui->defaultIconsView->setCurrentIndex(index);
340
        }
341
        m_ui->customIconsView->selectionModel()->clearSelection();
342
    }
343
}
344

345
void EditWidgetIcons::updateWidgetsCustomIcons(bool check)
346
{
347
    if (check) {
348
        QModelIndex index = m_ui->customIconsView->currentIndex();
349
        if (!index.isValid()) {
350
            m_ui->customIconsView->setCurrentIndex(m_customIconModel->index(0, 0));
351
        } else {
352
            m_ui->customIconsView->setCurrentIndex(index);
353
        }
354
        m_ui->defaultIconsView->selectionModel()->clearSelection();
355
    }
356
}
357

358
void EditWidgetIcons::updateRadioButtonDefaultIcons()
359
{
360
    m_ui->defaultIconsRadio->setChecked(true);
361
}
362

363
void EditWidgetIcons::updateRadioButtonCustomIcons()
364
{
365
    m_ui->customIconsRadio->setChecked(true);
366
}
367

368
void EditWidgetIcons::confirmApplyIconTo(QAction* action)
369
{
370
    m_applyIconTo = action->data().value<ApplyIconToOptions>();
371
    m_ui->applyIconToPushButton->setText(action->text());
372
}
373

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.