2
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
3
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
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.
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.
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/>.
19
#include "EditWidgetIcons.h"
20
#include "ui_EditWidgetIcons.h"
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"
30
#include "gui/MessageBox.h"
31
#ifdef WITH_XC_NETWORKING
32
#include "gui/IconDownloader.h"
37
IconStruct::IconStruct()
40
, applyTo(ApplyIconToOptions::THIS_ONLY)
44
EditWidgetIcons::EditWidgetIcons(QWidget* parent)
46
, m_ui(new Ui::EditWidgetIcons())
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())
57
m_ui->defaultIconsView->setModel(m_defaultIconModel);
58
m_ui->customIconsView->setModel(m_customIconModel);
60
m_ui->applyIconToPushButton->setMenu(createApplyIconToMenu());
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*)));
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&)));
83
#ifndef WITH_XC_NETWORKING
84
m_ui->faviconButton->setVisible(false);
85
m_ui->faviconURL->setVisible(false);
89
EditWidgetIcons::~EditWidgetIcons() = default;
91
IconStruct EditWidgetIcons::state()
94
Q_ASSERT(!m_currentUuid.isNull());
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();
105
QModelIndex index = m_ui->customIconsView->currentIndex();
106
if (index.isValid()) {
107
iconStruct.uuid = m_customIconModel->uuidFromIndex(m_ui->customIconsView->currentIndex());
109
iconStruct.number = -1;
113
iconStruct.applyTo = m_applyIconTo;
118
void EditWidgetIcons::reset()
121
m_currentUuid = QUuid();
124
void EditWidgetIcons::load(const QUuid& currentUuid,
125
const QSharedPointer<Database>& database,
126
const IconStruct& iconStruct,
130
Q_ASSERT(!currentUuid.isNull());
133
m_currentUuid = currentUuid;
136
m_customIconModel->setIcons(Icons::customIconsPixmaps(database.data(), IconSize::Default),
137
database->metadata()->customIconsOrder());
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);
145
QModelIndex index = m_customIconModel->indexFromUuid(iconUuid);
146
if (index.isValid()) {
147
m_ui->customIconsView->setCurrentIndex(index);
148
m_ui->customIconsRadio->setChecked(true);
150
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
151
m_ui->defaultIconsRadio->setChecked(true);
155
m_applyIconTo = ApplyIconToOptions::THIS_ONLY;
156
m_ui->applyIconToPushButton->menu()->defaultAction()->activate(QAction::Trigger);
159
void EditWidgetIcons::setShowApplyIconToButton(bool state)
161
m_ui->applyIconToPushButton->setVisible(state);
164
QMenu* EditWidgetIcons::createApplyIconToMenu()
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;
180
void EditWidgetIcons::keyPressEvent(QKeyEvent* event)
182
if (m_ui->faviconURL->hasFocus() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) {
183
m_ui->faviconButton->animateClick();
185
QWidget::keyPressEvent(event);
189
void EditWidgetIcons::setUrl(const QString& url)
191
#ifdef WITH_XC_NETWORKING
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);
197
m_ui->faviconURL->setText("");
204
void EditWidgetIcons::downloadFavicon()
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();
215
void EditWidgetIcons::iconReceived(const QString& url, const QImage& icon)
217
#ifdef WITH_XC_NETWORKING
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"));
225
emit messageEditEntry(message, MessageWidget::Error);
229
if (!addCustomIcon(icon)) {
230
emit messageEditEntry(tr("Existing icon selected."), MessageWidget::Information);
238
void EditWidgetIcons::abortRequests()
240
#ifdef WITH_XC_NETWORKING
242
m_downloader->abortDownload();
247
void EditWidgetIcons::addCustomIconFromFile()
253
auto filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), Icons::imageFormatsFilter(), tr("All files"));
255
fileDialog()->getOpenFileNames(this, tr("Select Image(s)"), FileDialog::getLastDir("icons"), filter);
256
if (!filenames.empty()) {
257
QStringList errornames;
259
for (const auto& filename : filenames) {
260
if (!filename.isEmpty()) {
261
auto icon = QImage(filename);
263
errornames << filename;
264
} else if (!addCustomIcon(icon, QFileInfo(filename).baseName())) {
265
// Icon already exists in database
271
// Save last used directory using first image path
272
FileDialog::saveLastDir("icons", filenames[0]);
274
int numloaded = filenames.size() - errornames.size() - numexisting;
278
msg = tr("Successfully loaded %1 of %n icon(s)", "", filenames.size()).arg(numloaded);
280
msg = tr("No icons were loaded");
283
if (numexisting > 0) {
284
msg += "\n" + tr("%n icon(s) already exist in the database", "", numexisting);
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);
296
emit messageEditEntry(msg, MessageWidget::Information);
301
bool EditWidgetIcons::addCustomIcon(const QImage& icon, const QString& name)
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);
311
QByteArray serializedIcon = Icons::saveToBytes(scaledIcon);
312
QUuid uuid = m_db->metadata()->findCustomIcon(serializedIcon);
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());
321
// Select the new or existing icon
322
updateRadioButtonCustomIcons();
323
QModelIndex index = m_customIconModel->indexFromUuid(uuid);
324
m_ui->customIconsView->setCurrentIndex(index);
326
emit widgetUpdated();
332
void EditWidgetIcons::updateWidgetsDefaultIcons(bool check)
335
QModelIndex index = m_ui->defaultIconsView->currentIndex();
336
if (!index.isValid()) {
337
m_ui->defaultIconsView->setCurrentIndex(m_defaultIconModel->index(0, 0));
339
m_ui->defaultIconsView->setCurrentIndex(index);
341
m_ui->customIconsView->selectionModel()->clearSelection();
345
void EditWidgetIcons::updateWidgetsCustomIcons(bool check)
348
QModelIndex index = m_ui->customIconsView->currentIndex();
349
if (!index.isValid()) {
350
m_ui->customIconsView->setCurrentIndex(m_customIconModel->index(0, 0));
352
m_ui->customIconsView->setCurrentIndex(index);
354
m_ui->defaultIconsView->selectionModel()->clearSelection();
358
void EditWidgetIcons::updateRadioButtonDefaultIcons()
360
m_ui->defaultIconsRadio->setChecked(true);
363
void EditWidgetIcons::updateRadioButtonCustomIcons()
365
m_ui->customIconsRadio->setChecked(true);
368
void EditWidgetIcons::confirmApplyIconTo(QAction* action)
370
m_applyIconTo = action->data().value<ApplyIconToOptions>();
371
m_ui->applyIconToPushButton->setText(action->text());