diff --git a/COPYING b/COPYING index 9322a0e3..4dbaece3 100644 --- a/COPYING +++ b/COPYING @@ -198,3 +198,9 @@ Files: src/zxcvbn/zxcvbn.* Copyright: 2015, Tony Evans 2016, KeePassXC Team License: BSD 3-clause + +Files: src/gui/KMessageWidget.h + src/gui/KMessageWidget.cpp +Copyright: 2011 Aurélien Gâteau + 2014 Dominik Haumann +License: LGPL-2.1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8c394884..c199cbb9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,9 +86,11 @@ set(keepassx_SOURCES gui/FileDialog.cpp gui/IconModels.cpp gui/KeePass1OpenWidget.cpp + gui/KMessageWidget.cpp gui/LineEdit.cpp gui/MainWindow.cpp gui/MessageBox.cpp + gui/MessageWidget.cpp gui/PasswordEdit.cpp gui/PasswordGeneratorWidget.cpp gui/PasswordComboBox.cpp diff --git a/src/gui/ChangeMasterKeyWidget.cpp b/src/gui/ChangeMasterKeyWidget.cpp index 3e346bc1..37fe9b0d 100644 --- a/src/gui/ChangeMasterKeyWidget.cpp +++ b/src/gui/ChangeMasterKeyWidget.cpp @@ -30,6 +30,8 @@ ChangeMasterKeyWidget::ChangeMasterKeyWidget(QWidget* parent) { m_ui->setupUi(this); + m_ui->messageWidget->setHidden(true); + connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(generateKey())); connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(reject())); m_ui->togglePasswordButton->setIcon(filePath()->onOffIcon("actions", "password-show")); @@ -52,7 +54,7 @@ void ChangeMasterKeyWidget::createKeyFile() QString errorMsg; bool created = FileKey::create(fileName, &errorMsg); if (!created) { - MessageBox::warning(this, tr("Error"), tr("Unable to create Key File : ") + errorMsg); + m_ui->messageWidget->showMessage(tr("Unable to create Key File : ").append(errorMsg), MessageWidget::Error); } else { m_ui->keyFileCombo->setEditText(fileName); @@ -110,7 +112,7 @@ void ChangeMasterKeyWidget::generateKey() m_key.addKey(PasswordKey(m_ui->enterPasswordEdit->text())); } else { - MessageBox::warning(this, tr("Error"), tr("Different passwords supplied.")); + m_ui->messageWidget->showMessage(tr("Different passwords supplied."), MessageWidget::Error); m_ui->enterPasswordEdit->setText(""); m_ui->repeatPasswordEdit->setText(""); return; @@ -119,15 +121,16 @@ void ChangeMasterKeyWidget::generateKey() if (m_ui->keyFileGroup->isChecked()) { FileKey fileKey; QString errorMsg; - if (!fileKey.load(m_ui->keyFileCombo->currentText(), &errorMsg)) { - MessageBox::critical(this, tr("Failed to set key file"), - tr("Failed to set %1 as the Key file:\n%2") - .arg(m_ui->keyFileCombo->currentText(), errorMsg)); + QString fileKeyName = m_ui->keyFileCombo->currentText(); + if (!fileKey.load(fileKeyName, &errorMsg)) { + m_ui->messageWidget->showMessage( + tr("Failed to set %1 as the Key file:\n%2").arg(fileKeyName, errorMsg), MessageWidget::Error); return; } m_key.addKey(fileKey); } + m_ui->messageWidget->hideMessage(); Q_EMIT editFinished(true); } diff --git a/src/gui/ChangeMasterKeyWidget.ui b/src/gui/ChangeMasterKeyWidget.ui index d14941cc..4af497a5 100644 --- a/src/gui/ChangeMasterKeyWidget.ui +++ b/src/gui/ChangeMasterKeyWidget.ui @@ -7,10 +7,13 @@ 0 0 438 - 256 + 342 + + + @@ -151,6 +154,12 @@ QLineEdit
gui/PasswordEdit.h
+ + MessageWidget + QWidget +
gui/MessageWidget.h
+ 1 +
passwordGroup diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 781b836f..c9d266fa 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -35,6 +35,8 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) { m_ui->setupUi(this); + m_ui->messageWidget->setHidden(true); + QFont font = m_ui->labelHeadline->font(); font.setBold(true); font.setPointSize(font.pointSize() + 2); @@ -106,8 +108,8 @@ void DatabaseOpenWidget::openDatabase() QFile file(m_filename); if (!file.open(QIODevice::ReadOnly)) { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") - .append(file.errorString())); + m_ui->messageWidget->showMessage( + tr("Unable to open the database.").append("\n").append(file.errorString()), MessageWidget::Error); return; } if (m_db) { @@ -118,11 +120,14 @@ void DatabaseOpenWidget::openDatabase() QApplication::restoreOverrideCursor(); if (m_db) { + if (m_ui->messageWidget->isVisible()) { + m_ui->messageWidget->animatedHide(); + } Q_EMIT editFinished(true); } else { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") - .append(reader.errorString())); + m_ui->messageWidget->showMessage(tr("Unable to open the database.") + .append("\n").append(reader.errorString()), MessageWidget::Error); m_ui->editPassword->clear(); } } @@ -142,7 +147,8 @@ CompositeKey DatabaseOpenWidget::databaseKey() QString keyFilename = m_ui->comboKeyFile->currentText(); QString errorMsg; if (!key.load(keyFilename, &errorMsg)) { - MessageBox::warning(this, tr("Error"), tr("Can't open key file").append(":\n").append(errorMsg)); + m_ui->messageWidget->showMessage(tr("Can't open key file").append(":\n") + .append(errorMsg), MessageWidget::Error); return CompositeKey(); } masterKey.addKey(key); diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index 4aae5faf..f658d568 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -10,10 +10,13 @@ 250 - + 8 + + + @@ -144,6 +147,12 @@ QLineEdit
gui/PasswordEdit.h
+ + MessageWidget + QWidget +
gui/MessageWidget.h
+ 1 +
checkPassword diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 0c784eb2..8b29cfd0 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -116,7 +116,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, QFileInfo fileInfo(fileName); QString canonicalFilePath = fileInfo.canonicalFilePath(); if (canonicalFilePath.isEmpty()) { - MessageBox::warning(this, tr("Warning"), tr("File not found!")); + Q_EMIT messageGlobal(tr("File not found!"), MessageWidget::Error); return; } @@ -136,8 +136,9 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, QFile file(fileName); if (!file.open(QIODevice::ReadWrite)) { if (!file.open(QIODevice::ReadOnly)) { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") - .append(file.errorString())); + // can't open + Q_EMIT messageGlobal( + tr("Unable to open the database.").append("\n").append(file.errorString()), MessageWidget::Error); return; } else { @@ -184,6 +185,10 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, insertDatabase(db, dbStruct); + if (dbStruct.readOnly) { + Q_EMIT messageTab(tr("File opened in read only mode."), MessageWidget::Warning); + } + updateLastDatabases(dbStruct.filePath); if (!pw.isNull() || !keyFile.isEmpty()) { @@ -192,6 +197,7 @@ void DatabaseTabWidget::openDatabase(const QString& fileName, const QString& pw, else { dbStruct.dbWidget->switchToOpenDatabase(dbStruct.filePath); } + Q_EMIT messageDismissGlobal(); } void DatabaseTabWidget::mergeDatabase() @@ -322,8 +328,8 @@ bool DatabaseTabWidget::saveDatabase(Database* db) // write the database to the file m_writer.writeDatabase(&saveFile, db); if (m_writer.hasError()) { - MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n" - + m_writer.errorString()); + Q_EMIT messageTab(tr("Writing the database failed.").append("\n") + .append(m_writer.errorString()), MessageWidget::Error); return false; } @@ -332,17 +338,18 @@ bool DatabaseTabWidget::saveDatabase(Database* db) dbStruct.modified = false; dbStruct.dbWidget->databaseSaved(); updateTabName(db); + Q_EMIT messageDismissTab(); return true; } else { - MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n" - + saveFile.errorString()); + Q_EMIT messageTab(tr("Writing the database failed.").append("\n") + .append(saveFile.errorString()), MessageWidget::Error); return false; } } else { - MessageBox::critical(this, tr("Error"), tr("Writing the database failed.") + "\n\n" - + saveFile.errorString()); + Q_EMIT messageTab(tr("Writing the database failed.").append("\n") + .append(saveFile.errorString()), MessageWidget::Error); return false; } } @@ -486,8 +493,9 @@ void DatabaseTabWidget::exportToCsv() CsvExporter csvExporter; if (!csvExporter.exportDatabase(fileName, db)) { - MessageBox::critical(this, tr("Error"), tr("Writing the CSV file failed.") + "\n\n" - + csvExporter.errorString()); + Q_EMIT messageGlobal( + tr("Writing the CSV file failed.").append("\n") + .append(csvExporter.errorString()), MessageWidget::Error); } } diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 24bdbde2..8f01a987 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -23,12 +23,14 @@ #include "format/KeePass2Writer.h" #include "gui/DatabaseWidget.h" +#include "gui/MessageWidget.h" class DatabaseWidget; class DatabaseWidgetStateSync; class DatabaseOpenWidget; class QFile; class QLockFile; +class MessageWidget; struct DatabaseManagerStruct { @@ -84,6 +86,10 @@ Q_SIGNALS: void activateDatabaseChanged(DatabaseWidget* dbWidget); void databaseLocked(DatabaseWidget* dbWidget); void databaseUnlocked(DatabaseWidget* dbWidget); + void messageGlobal(const QString&, MessageWidget::MessageType type); + void messageTab(const QString&, MessageWidget::MessageType type); + void messageDismissGlobal(); + void messageDismissTab(); private Q_SLOTS: void updateTabName(Database* db); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 4a1298de..2d65352f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -60,7 +60,14 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) , m_newParent(nullptr) { m_mainWidget = new QWidget(this); - QLayout* layout = new QHBoxLayout(m_mainWidget); + + m_messageWidget = new MessageWidget(this); + m_messageWidget->setHidden(true); + + QVBoxLayout* mainLayout = new QVBoxLayout(); + QLayout* layout = new QHBoxLayout(); + mainLayout->addWidget(m_messageWidget); + mainLayout->addLayout(layout); m_splitter = new QSplitter(m_mainWidget); m_splitter->setChildrenCollapsible(false); @@ -105,7 +112,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_splitter->setStretchFactor(1, 70); layout->addWidget(m_splitter); - m_mainWidget->setLayout(layout); + m_mainWidget->setLayout(mainLayout); m_editEntryWidget = new EditEntryWidget(); m_editEntryWidget->setObjectName("editEntryWidget"); @@ -689,7 +696,7 @@ void DatabaseWidget::updateMasterKey(bool accepted) QApplication::restoreOverrideCursor(); if (!result) { - MessageBox::critical(this, tr("Error"), tr("Unable to calculate master key")); + m_messageWidget->showMessage(tr("Unable to calculate master key"), MessageWidget::Error); return; } } @@ -729,14 +736,14 @@ void DatabaseWidget::mergeDatabase(bool accepted) { if (accepted) { if (!m_db) { - MessageBox::critical(this, tr("Error"), tr("No current database.")); + m_messageWidget->showMessage(tr("No current database."), MessageWidget::Error); return; } Database* srcDb = static_cast(sender())->database(); if (!srcDb) { - MessageBox::critical(this, tr("Error"), tr("No source database, nothing to do.")); + m_messageWidget->showMessage(tr("No source database, nothing to do."), MessageWidget::Error); return; } @@ -1086,15 +1093,15 @@ void DatabaseWidget::reloadDatabaseFile() } else { - MessageBox::critical(this, tr("Autoreload Failed"), - tr("Could not parse or unlock the new database file while attempting" - " to autoreload this database.")); + m_messageWidget->showMessage( + tr("Could not parse or unlock the new database file while attempting" + " to autoreload this database."), MessageWidget::Error); } } else { - MessageBox::critical(this, tr("Autoreload Failed"), - tr("Could not open the new database file while attempting to autoreload" - " this database.")); + m_messageWidget->showMessage( + tr("Could not open the new database file while attempting to autoreload this database."), + MessageWidget::Error); } // Rewatch the database file @@ -1220,3 +1227,15 @@ void DatabaseWidget::closeUnlockDialog() { m_unlockDatabaseDialog->close(); } + +void DatabaseWidget::showMessage(const QString& text, MessageWidget::MessageType type) +{ + m_messageWidget->showMessage(text, type); +} + +void DatabaseWidget::hideMessage() +{ + if (m_messageWidget->isVisible()) { + m_messageWidget->animatedHide(); + } +} diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 79e58cec..4133e52b 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -26,6 +26,7 @@ #include "core/Uuid.h" #include "gui/entry/EntryModel.h" +#include "gui/MessageWidget.h" class ChangeMasterKeyWidget; class DatabaseOpenWidget; @@ -43,9 +44,14 @@ class QMenu; class QSplitter; class QLabel; class UnlockDatabaseWidget; +class MessageWidget; class UnlockDatabaseDialog; class QFileSystemWatcher; +namespace Ui { + class SearchWidget; +} + class DatabaseWidget : public QStackedWidget { Q_OBJECT @@ -145,6 +151,8 @@ public Q_SLOTS: void search(const QString& searchtext); void setSearchCaseSensitive(bool state); void endSearch(); + void showMessage(const QString& text, MessageWidget::MessageType type); + void hideMessage(); private Q_SLOTS: void entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column); @@ -192,6 +200,7 @@ private: QString m_filename; Uuid m_groupBeforeLock; Uuid m_entryBeforeLock; + MessageWidget* m_messageWidget; // Search state QString m_lastSearchText; diff --git a/src/gui/DatabaseWidgetStateSync.cpp b/src/gui/DatabaseWidgetStateSync.cpp index fd5719f5..1510d844 100644 --- a/src/gui/DatabaseWidgetStateSync.cpp +++ b/src/gui/DatabaseWidgetStateSync.cpp @@ -149,3 +149,4 @@ QVariant DatabaseWidgetStateSync::intListToVariant(const QList& list) return result; } + diff --git a/src/gui/EditWidget.cpp b/src/gui/EditWidget.cpp index b3d9842b..ef29f013 100644 --- a/src/gui/EditWidget.cpp +++ b/src/gui/EditWidget.cpp @@ -25,6 +25,8 @@ EditWidget::EditWidget(QWidget* parent) m_ui->setupUi(this); setReadOnly(false); + m_ui->messageWidget->setHidden(true); + QFont headerLabelFont = m_ui->headerLabel->font(); headerLabelFont.setBold(true); headerLabelFont.setPointSize(headerLabelFont.pointSize() + 2); @@ -86,3 +88,15 @@ bool EditWidget::readOnly() const { return m_readOnly; } + +void EditWidget::showMessage(const QString& text, MessageWidget::MessageType type) +{ + m_ui->messageWidget->showMessage(text, type); +} + +void EditWidget::hideMessage() +{ + if (m_ui->messageWidget->isVisible()) { + m_ui->messageWidget->animatedHide(); + } +} diff --git a/src/gui/EditWidget.h b/src/gui/EditWidget.h index c5f507ac..6f2c8f2d 100644 --- a/src/gui/EditWidget.h +++ b/src/gui/EditWidget.h @@ -21,6 +21,7 @@ #include #include "gui/DialogyWidget.h" +#include "gui/MessageWidget.h" class QLabel; @@ -48,6 +49,10 @@ Q_SIGNALS: void accepted(); void rejected(); +protected Q_SLOTS: + void showMessage(const QString& text, MessageWidget::MessageType type); + void hideMessage(); + private: const QScopedPointer m_ui; bool m_readOnly; diff --git a/src/gui/EditWidget.ui b/src/gui/EditWidget.ui index 3891007a..05b06b90 100644 --- a/src/gui/EditWidget.ui +++ b/src/gui/EditWidget.ui @@ -11,6 +11,9 @@
+ + + @@ -58,6 +61,12 @@ + + MessageWidget + QWidget +
gui/MessageWidget.h
+ 1 +
CategoryListWidget QListWidget diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 145957ab..ba395b59 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -158,12 +158,11 @@ void EditWidgetIcons::fetchFavicon(QUrl url) void EditWidgetIcons::fetchFaviconFromGoogle(QString domain) { - if (m_fallbackToGoogle) { + if (m_fallbackToGoogle) { abortFaviconDownload(); m_fallbackToGoogle = false; fetchFavicon(QUrl("http://www.google.com/s2/favicons?domain=" + domain)); - } - else { + } else { abortFaviconDownload(); MessageBox::warning(this, tr("Error"), tr("Unable to fetch favicon.")); } @@ -248,7 +247,7 @@ void EditWidgetIcons::addCustomIcon() m_ui->customIconsView->setCurrentIndex(index); } else { - MessageBox::critical(this, tr("Error"), tr("Can't read icon")); + Q_EMIT messageEditEntry(tr("Can't read icon"), MessageWidget::Error); } } } @@ -302,9 +301,8 @@ void EditWidgetIcons::removeCustomIcon() } } else { - MessageBox::information(this, tr("Can't delete icon!"), - tr("Can't delete icon. Still used by %1 items.") - .arg(iconUsedCount)); + Q_EMIT messageEditEntry( + tr("Can't delete icon. Still used by %1 items.").arg(iconUsedCount), MessageWidget::Error); } } } diff --git a/src/gui/EditWidgetIcons.h b/src/gui/EditWidgetIcons.h index 508c5686..ae5f8695 100644 --- a/src/gui/EditWidgetIcons.h +++ b/src/gui/EditWidgetIcons.h @@ -26,6 +26,7 @@ #include "core/Global.h" #include "core/Uuid.h" +#include "gui/MessageWidget.h" class Database; class DefaultIconModel; @@ -58,6 +59,10 @@ public: public Q_SLOTS: void setUrl(const QString &url); +Q_SIGNALS: + void messageEditEntry(QString, MessageWidget::MessageType); + void messageEditEntryDismiss(); + private Q_SLOTS: void downloadFavicon(); void fetchFavicon(QUrl url); diff --git a/src/gui/KMessageWidget.cpp b/src/gui/KMessageWidget.cpp new file mode 100644 index 00000000..f2c48c25 --- /dev/null +++ b/src/gui/KMessageWidget.cpp @@ -0,0 +1,480 @@ +/* This file is part of the KDE libraries + * + * Copyright (c) 2011 Aurélien Gâteau + * Copyright (c) 2014 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include "KMessageWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------- +// KMessageWidgetPrivate +//--------------------------------------------------------------------- +class KMessageWidgetPrivate +{ +public: + void init(KMessageWidget *); + + KMessageWidget *q; + QFrame *content; + QLabel *iconLabel; + QLabel *textLabel; + QToolButton *closeButton; + QTimeLine *timeLine; + QIcon icon; + + KMessageWidget::MessageType messageType; + bool wordWrap; + QList buttons; + QPixmap contentSnapShot; + + void createLayout(); + void updateSnapShot(); + void updateLayout(); + void slotTimeLineChanged(qreal); + void slotTimeLineFinished(); + + int bestContentHeight() const; +}; + +void KMessageWidgetPrivate::init(KMessageWidget *q_ptr) +{ + q = q_ptr; + + q->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + timeLine = new QTimeLine(500, q); + QObject::connect(timeLine, SIGNAL(valueChanged(qreal)), q, SLOT(slotTimeLineChanged(qreal))); + QObject::connect(timeLine, SIGNAL(finished()), q, SLOT(slotTimeLineFinished())); + + content = new QFrame(q); + content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + wordWrap = false; + + iconLabel = new QLabel(content); + iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + iconLabel->hide(); + + textLabel = new QLabel(content); + textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + QObject::connect(textLabel, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString))); + QObject::connect(textLabel, SIGNAL(linkHovered(QString)), q, SIGNAL(linkHovered(QString))); + + QAction *closeAction = new QAction(q); + closeAction->setText(KMessageWidget::tr("&Close")); + closeAction->setToolTip(KMessageWidget::tr("Close message")); + closeAction->setIcon(q->style()->standardIcon(QStyle::SP_DialogCloseButton)); + + QObject::connect(closeAction, SIGNAL(triggered(bool)), q, SLOT(animatedHide())); + + closeButton = new QToolButton(content); + closeButton->setAutoRaise(true); + closeButton->setDefaultAction(closeAction); + + q->setMessageType(KMessageWidget::Information); +} + +void KMessageWidgetPrivate::createLayout() +{ + delete content->layout(); + + content->resize(q->size()); + + qDeleteAll(buttons); + buttons.clear(); + + Q_FOREACH (QAction *action, q->actions()) { + QToolButton *button = new QToolButton(content); + button->setDefaultAction(action); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + buttons.append(button); + } + + // AutoRaise reduces visual clutter, but we don't want to turn it on if + // there are other buttons, otherwise the close button will look different + // from the others. + closeButton->setAutoRaise(buttons.isEmpty()); + + if (wordWrap) { + QGridLayout *layout = new QGridLayout(content); + // Set alignment to make sure icon does not move down if text wraps + layout->addWidget(iconLabel, 0, 0, 1, 1, Qt::AlignHCenter | Qt::AlignTop); + layout->addWidget(textLabel, 0, 1); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(); + Q_FOREACH (QToolButton *button, buttons) { + // For some reason, calling show() is necessary if wordwrap is true, + // otherwise the buttons do not show up. It is not needed if + // wordwrap is false. + button->show(); + buttonLayout->addWidget(button); + } + buttonLayout->addWidget(closeButton); + layout->addItem(buttonLayout, 1, 0, 1, 2); + } else { + QHBoxLayout *layout = new QHBoxLayout(content); + layout->addWidget(iconLabel); + layout->addWidget(textLabel); + + Q_FOREACH (QToolButton *button, buttons) { + layout->addWidget(button); + } + + layout->addWidget(closeButton); + }; + + if (q->isVisible()) { + q->setFixedHeight(content->sizeHint().height()); + } + q->updateGeometry(); +} + +void KMessageWidgetPrivate::updateLayout() +{ + if (content->layout()) { + createLayout(); + } +} + +void KMessageWidgetPrivate::updateSnapShot() +{ + // Attention: updateSnapShot calls QWidget::render(), which causes the whole + // window layouts to be activated. Calling this method from resizeEvent() + // can lead to infinite recursion, see: + // https://bugs.kde.org/show_bug.cgi?id=311336 + contentSnapShot = QPixmap(content->size() * q->devicePixelRatio()); + contentSnapShot.setDevicePixelRatio(q->devicePixelRatio()); + contentSnapShot.fill(Qt::transparent); + content->render(&contentSnapShot, QPoint(), QRegion(), QWidget::DrawChildren); +} + +void KMessageWidgetPrivate::slotTimeLineChanged(qreal value) +{ + q->setFixedHeight(qMin(value * 2, qreal(1.0)) * content->height()); + q->update(); +} + +void KMessageWidgetPrivate::slotTimeLineFinished() +{ + if (timeLine->direction() == QTimeLine::Forward) { + // Show + // We set the whole geometry here, because it may be wrong if a + // KMessageWidget is shown right when the toplevel window is created. + content->setGeometry(0, 0, q->width(), bestContentHeight()); + + // notify about finished animation + emit q->showAnimationFinished(); + } else { + // hide and notify about finished animation + q->hide(); + emit q->hideAnimationFinished(); + } +} + +int KMessageWidgetPrivate::bestContentHeight() const +{ + int height = content->heightForWidth(q->width()); + if (height == -1) { + height = content->sizeHint().height(); + } + return height; +} + +//--------------------------------------------------------------------- +// KMessageWidget +//--------------------------------------------------------------------- +KMessageWidget::KMessageWidget(QWidget *parent) +: QFrame(parent) +, d(new KMessageWidgetPrivate) +{ + d->init(this); +} + +KMessageWidget::KMessageWidget(const QString &text, QWidget *parent) +: QFrame(parent) +, d(new KMessageWidgetPrivate) +{ + d->init(this); + setText(text); +} + +KMessageWidget::~KMessageWidget() +{ + delete d; +} + +QString KMessageWidget::text() const +{ + return d->textLabel->text(); +} + +void KMessageWidget::setText(const QString &text) +{ + d->textLabel->setText(text); + updateGeometry(); +} + +KMessageWidget::MessageType KMessageWidget::messageType() const +{ + return d->messageType; +} + +static QColor darkShade(QColor c) +{ + qreal contrast = 0.7; // taken from kcolorscheme for the dark shade + + qreal darkAmount; + if (c.lightnessF() < 0.006) { /* too dark */ + darkAmount = 0.02 + 0.40 * contrast; + } else if (c.lightnessF() > 0.93) { /* too bright */ + darkAmount = -0.06 - 0.60 * contrast; + } else { + darkAmount = (-c.lightnessF()) * (0.55 + contrast * 0.35); + } + + qreal v = c.lightnessF() + darkAmount; + v = v > 0.0 ? (v < 1.0 ? v : 1.0) : 0.0; + c.setHsvF(c.hslHueF(), c.hslSaturationF(), v); + return c; +} + +void KMessageWidget::setMessageType(KMessageWidget::MessageType type) +{ + d->messageType = type; + QColor bg0, bg1, bg2, border, fg; + switch (type) { + case Positive: + bg1.setRgb(0, 110, 40); // values taken from kcolorscheme.cpp (Positive) + break; + case Information: + bg1 = palette().highlight().color(); + break; + case Warning: + bg1.setRgb(176, 128, 0); // values taken from kcolorscheme.cpp (Neutral) + break; + case Error: + bg1.setRgb(191, 3, 3); // values taken from kcolorscheme.cpp (Negative) + break; + } + + // Colors + fg = palette().highlightedText().color(); + bg0 = bg1.lighter(110); + bg2 = bg1.darker(110); + border = darkShade(bg1); + + d->content->setStyleSheet( + QString(QLatin1String(".QFrame {" + "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1," + " stop: 0 %1," + " stop: 0.1 %2," + " stop: 1.0 %3);" + "border-radius: 5px;" + "border: 1px solid %4;" + "margin: %5px;" + "}" + ".QLabel { color: %6; }" + )) + .arg(bg0.name()) + .arg(bg1.name()) + .arg(bg2.name()) + .arg(border.name()) + // DefaultFrameWidth returns the size of the external margin + border width. We know our border is 1px, so we subtract this from the frame normal QStyle FrameWidth to get our margin + .arg(style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this) - 1) + .arg(fg.name()) + ); +} + +QSize KMessageWidget::sizeHint() const +{ + ensurePolished(); + return d->content->sizeHint(); +} + +QSize KMessageWidget::minimumSizeHint() const +{ + ensurePolished(); + return d->content->minimumSizeHint(); +} + +bool KMessageWidget::event(QEvent *event) +{ + if (event->type() == QEvent::Polish && !d->content->layout()) { + d->createLayout(); + } + return QFrame::event(event); +} + +void KMessageWidget::resizeEvent(QResizeEvent *event) +{ + QFrame::resizeEvent(event); + + if (d->timeLine->state() == QTimeLine::NotRunning) { + d->content->resize(width(), d->bestContentHeight()); + } +} + +int KMessageWidget::heightForWidth(int width) const +{ + ensurePolished(); + return d->content->heightForWidth(width); +} + +void KMessageWidget::paintEvent(QPaintEvent *event) +{ + QFrame::paintEvent(event); + if (d->timeLine->state() == QTimeLine::Running) { + QPainter painter(this); + painter.setOpacity(d->timeLine->currentValue() * d->timeLine->currentValue()); + painter.drawPixmap(0, 0, d->contentSnapShot); + } +} + +bool KMessageWidget::wordWrap() const +{ + return d->wordWrap; +} + +void KMessageWidget::setWordWrap(bool wordWrap) +{ + d->wordWrap = wordWrap; + d->textLabel->setWordWrap(wordWrap); + QSizePolicy policy = sizePolicy(); + policy.setHeightForWidth(wordWrap); + setSizePolicy(policy); + d->updateLayout(); + // Without this, when user does wordWrap -> !wordWrap -> wordWrap, a minimum + // height is set, causing the widget to be too high. + // Mostly visible in test programs. + if (wordWrap) { + setMinimumHeight(0); + } +} + +bool KMessageWidget::isCloseButtonVisible() const +{ + return d->closeButton->isVisible(); +} + +void KMessageWidget::setCloseButtonVisible(bool show) +{ + d->closeButton->setVisible(show); + updateGeometry(); +} + +void KMessageWidget::addAction(QAction *action) +{ + QFrame::addAction(action); + d->updateLayout(); +} + +void KMessageWidget::removeAction(QAction *action) +{ + QFrame::removeAction(action); + d->updateLayout(); +} + +void KMessageWidget::animatedShow() +{ + if (!style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) { + show(); + emit showAnimationFinished(); + return; + } + + if (isVisible()) { + return; + } + + QFrame::show(); + setFixedHeight(0); + int wantedHeight = d->bestContentHeight(); + d->content->setGeometry(0, -wantedHeight, width(), wantedHeight); + + d->updateSnapShot(); + + d->timeLine->setDirection(QTimeLine::Forward); + if (d->timeLine->state() == QTimeLine::NotRunning) { + d->timeLine->start(); + } +} + +void KMessageWidget::animatedHide() +{ + if (!style()->styleHint(QStyle::SH_Widget_Animate, 0, this)) { + hide(); + emit hideAnimationFinished(); + return; + } + + if (!isVisible()) { + hide(); + return; + } + + d->content->move(0, -d->content->height()); + d->updateSnapShot(); + + d->timeLine->setDirection(QTimeLine::Backward); + if (d->timeLine->state() == QTimeLine::NotRunning) { + d->timeLine->start(); + } +} + +bool KMessageWidget::isHideAnimationRunning() const +{ + return (d->timeLine->direction() == QTimeLine::Backward) + && (d->timeLine->state() == QTimeLine::Running); +} + +bool KMessageWidget::isShowAnimationRunning() const +{ + return (d->timeLine->direction() == QTimeLine::Forward) + && (d->timeLine->state() == QTimeLine::Running); +} + +QIcon KMessageWidget::icon() const +{ + return d->icon; +} + +void KMessageWidget::setIcon(const QIcon &icon) +{ + d->icon = icon; + if (d->icon.isNull()) { + d->iconLabel->hide(); + } else { + const int size = style()->pixelMetric(QStyle::PM_ToolBarIconSize); + d->iconLabel->setPixmap(d->icon.pixmap(size)); + d->iconLabel->show(); + } +} + +#include "moc_KMessageWidget.cpp" diff --git a/src/gui/KMessageWidget.h b/src/gui/KMessageWidget.h new file mode 100644 index 00000000..4398e0f9 --- /dev/null +++ b/src/gui/KMessageWidget.h @@ -0,0 +1,342 @@ +/* This file is part of the KDE libraries + * + * Copyright (c) 2011 Aurélien Gâteau + * Copyright (c) 2014 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef KMESSAGEWIDGET_H +#define KMESSAGEWIDGET_H + +#include + +class KMessageWidgetPrivate; + +/** + * @short A widget to provide feedback or propose opportunistic interactions. + * + * KMessageWidget can be used to provide inline positive or negative + * feedback, or to implement opportunistic interactions. + * + * As a feedback widget, KMessageWidget provides a less intrusive alternative + * to "OK Only" message boxes. If you want to avoid a modal KMessageBox, + * consider using KMessageWidget instead. + * + * Examples of KMessageWidget look as follows, all of them having an icon set + * with setIcon(), and the first three show a close button: + * + * \image html kmessagewidget.png "KMessageWidget with different message types" + * + * Negative feedback + * + * The KMessageWidget can be used as a secondary indicator of failure: the + * first indicator is usually the fact the action the user expected to happen + * did not happen. + * + * Example: User fills a form, clicks "Submit". + * + * @li Expected feedback: form closes + * @li First indicator of failure: form stays there + * @li Second indicator of failure: a KMessageWidget appears on top of the + * form, explaining the error condition + * + * When used to provide negative feedback, KMessageWidget should be placed + * close to its context. In the case of a form, it should appear on top of the + * form entries. + * + * KMessageWidget should get inserted in the existing layout. Space should not + * be reserved for it, otherwise it becomes "dead space", ignored by the user. + * KMessageWidget should also not appear as an overlay to prevent blocking + * access to elements the user needs to interact with to fix the failure. + * + * Positive feedback + * + * KMessageWidget can be used for positive feedback but it shouldn't be + * overused. It is often enough to provide feedback by simply showing the + * results of an action. + * + * Examples of acceptable uses: + * + * @li Confirm success of "critical" transactions + * @li Indicate completion of background tasks + * + * Example of unadapted uses: + * + * @li Indicate successful saving of a file + * @li Indicate a file has been successfully removed + * + * Opportunistic interaction + * + * Opportunistic interaction is the situation where the application suggests to + * the user an action he could be interested in perform, either based on an + * action the user just triggered or an event which the application noticed. + * + * Example of acceptable uses: + * + * @li A browser can propose remembering a recently entered password + * @li A music collection can propose ripping a CD which just got inserted + * @li A chat application may notify the user a "special friend" just connected + * + * @author Aurélien Gâteau + * @since 4.7 + */ +class KMessageWidget : public QFrame +{ + Q_OBJECT + Q_ENUMS(MessageType) + + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(bool wordWrap READ wordWrap WRITE setWordWrap) + Q_PROPERTY(bool closeButtonVisible READ isCloseButtonVisible WRITE setCloseButtonVisible) + Q_PROPERTY(MessageType messageType READ messageType WRITE setMessageType) + Q_PROPERTY(QIcon icon READ icon WRITE setIcon) +public: + + /** + * Available message types. + * The background colors are chosen depending on the message type. + */ + enum MessageType { + Positive, + Information, + Warning, + Error + }; + + /** + * Constructs a KMessageWidget with the specified @p parent. + */ + explicit KMessageWidget(QWidget *parent = 0); + + /** + * Constructs a KMessageWidget with the specified @p parent and + * contents @p text. + */ + explicit KMessageWidget(const QString &text, QWidget *parent = 0); + + /** + * Destructor. + */ + ~KMessageWidget(); + + /** + * Get the text of this message widget. + * @see setText() + */ + QString text() const; + + /** + * Check whether word wrap is enabled. + * + * If word wrap is enabled, the message widget wraps the displayed text + * as required to the available width of the widget. This is useful to + * avoid breaking widget layouts. + * + * @see setWordWrap() + */ + bool wordWrap() const; + + /** + * Check whether the close button is visible. + * + * @see setCloseButtonVisible() + */ + bool isCloseButtonVisible() const; + + /** + * Get the type of this message. + * By default, the type is set to KMessageWidget::Information. + * + * @see KMessageWidget::MessageType, setMessageType() + */ + MessageType messageType() const; + + /** + * Add @p action to the message widget. + * For each action a button is added to the message widget in the + * order the actions were added. + * + * @param action the action to add + * @see removeAction(), QWidget::actions() + */ + void addAction(QAction *action); + + /** + * Remove @p action from the message widget. + * + * @param action the action to remove + * @see KMessageWidget::MessageType, addAction(), setMessageType() + */ + void removeAction(QAction *action); + + /** + * Returns the preferred size of the message widget. + */ + QSize sizeHint() const Q_DECL_OVERRIDE; + + /** + * Returns the minimum size of the message widget. + */ + QSize minimumSizeHint() const Q_DECL_OVERRIDE; + + /** + * Returns the required height for @p width. + * @param width the width in pixels + */ + int heightForWidth(int width) const Q_DECL_OVERRIDE; + + /** + * The icon shown on the left of the text. By default, no icon is shown. + * @since 4.11 + */ + QIcon icon() const; + + /** + * Check whether the hide animation started by calling animatedHide() + * is still running. If animations are disabled, this function always + * returns @e false. + * + * @see animatedHide(), hideAnimationFinished() + * @since 5.0 + */ + bool isHideAnimationRunning() const; + + /** + * Check whether the show animation started by calling animatedShow() + * is still running. If animations are disabled, this function always + * returns @e false. + * + * @see animatedShow(), showAnimationFinished() + * @since 5.0 + */ + bool isShowAnimationRunning() const; + +public Q_SLOTS: + /** + * Set the text of the message widget to @p text. + * If the message widget is already visible, the text changes on the fly. + * + * @param text the text to display, rich text is allowed + * @see text() + */ + void setText(const QString &text); + + /** + * Set word wrap to @p wordWrap. If word wrap is enabled, the text() + * of the message widget is wrapped to fit the available width. + * If word wrap is disabled, the message widget's minimum size is + * such that the entire text fits. + * + * @param wordWrap disable/enable word wrap + * @see wordWrap() + */ + void setWordWrap(bool wordWrap); + + /** + * Set the visibility of the close button. If @p visible is @e true, + * a close button is shown that calls animatedHide() if clicked. + * + * @see closeButtonVisible(), animatedHide() + */ + void setCloseButtonVisible(bool visible); + + /** + * Set the message type to @p type. + * By default, the message type is set to KMessageWidget::Information. + * + * @see messageType(), KMessageWidget::MessageType + */ + void setMessageType(KMessageWidget::MessageType type); + + /** + * Show the widget using an animation. + */ + void animatedShow(); + + /** + * Hide the widget using an animation. + */ + void animatedHide(); + + /** + * Define an icon to be shown on the left of the text + * @since 4.11 + */ + void setIcon(const QIcon &icon); + +Q_SIGNALS: + /** + * This signal is emitted when the user clicks a link in the text label. + * The URL referred to by the href anchor is passed in contents. + * @param contents text of the href anchor + * @see QLabel::linkActivated() + * @since 4.10 + */ + void linkActivated(const QString &contents); + + /** + * This signal is emitted when the user hovers over a link in the text label. + * The URL referred to by the href anchor is passed in contents. + * @param contents text of the href anchor + * @see QLabel::linkHovered() + * @since 4.11 + */ + void linkHovered(const QString &contents); + + /** + * This signal is emitted when the hide animation is finished, started by + * calling animatedHide(). If animations are disabled, this signal is + * emitted immediately after the message widget got hidden. + * + * @note This signal is @e not emitted if the widget was hidden by + * calling hide(), so this signal is only useful in conjunction + * with animatedHide(). + * + * @see animatedHide() + * @since 5.0 + */ + void hideAnimationFinished(); + + /** + * This signal is emitted when the show animation is finished, started by + * calling animatedShow(). If animations are disabled, this signal is + * emitted immediately after the message widget got shown. + * + * @note This signal is @e not emitted if the widget was shown by + * calling show(), so this signal is only useful in conjunction + * with animatedShow(). + * + * @see animatedShow() + * @since 5.0 + */ + void showAnimationFinished(); + +protected: + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; + + bool event(QEvent *event) Q_DECL_OVERRIDE; + + void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; + +private: + KMessageWidgetPrivate *const d; + friend class KMessageWidgetPrivate; + + Q_PRIVATE_SLOT(d, void slotTimeLineChanged(qreal)) + Q_PRIVATE_SLOT(d, void slotTimeLineFinished()) +}; + +#endif /* KMESSAGEWIDGET_H */ diff --git a/src/gui/KeePass1OpenWidget.cpp b/src/gui/KeePass1OpenWidget.cpp index 4f70a978..b63bbc48 100644 --- a/src/gui/KeePass1OpenWidget.cpp +++ b/src/gui/KeePass1OpenWidget.cpp @@ -49,8 +49,8 @@ void KeePass1OpenWidget::openDatabase() QFile file(m_filename); if (!file.open(QIODevice::ReadOnly)) { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") - .append(file.errorString())); + m_ui->messageWidget->showMessage( tr("Unable to open the database.").append("\n") + .append(file.errorString()), MessageWidget::Error); return; } if (m_db) { @@ -65,8 +65,9 @@ void KeePass1OpenWidget::openDatabase() Q_EMIT editFinished(true); } else { - MessageBox::warning(this, tr("Error"), tr("Unable to open the database.").append("\n") - .append(reader.errorString())); + m_ui->messageWidget->showMessage(tr("Unable to open the database.").append("\n") + .append(reader.errorString()), MessageWidget::Error); + m_ui->editPassword->clear(); } } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 819bda5d..b9da4e19 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -104,6 +104,7 @@ MainWindow::MainWindow() #endif setWindowIcon(filePath()->applicationIcon()); + m_ui->globalMessageWidget->setHidden(true); QAction* toggleViewAction = m_ui->toolBar->toggleViewAction(); toggleViewAction->setText(tr("Show toolbar")); m_ui->menuView->addAction(toggleViewAction); @@ -277,6 +278,11 @@ MainWindow::MainWindow() connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog())); + connect(m_ui->tabWidget, SIGNAL(messageGlobal(QString,MessageWidget::MessageType)), this, SLOT(displayGlobalMessage(QString, MessageWidget::MessageType))); + connect(m_ui->tabWidget, SIGNAL(messageDismissGlobal()), this, SLOT(hideGlobalMessage())); + connect(m_ui->tabWidget, SIGNAL(messageTab(QString,MessageWidget::MessageType)), this, SLOT(displayTabMessage(QString, MessageWidget::MessageType))); + connect(m_ui->tabWidget, SIGNAL(messageDismissTab()), this, SLOT(hideTabMessage())); + updateTrayIcon(); } @@ -755,7 +761,7 @@ void MainWindow::repairDatabase() if (fileName.isEmpty()) { return; } - + QScopedPointer dialog(new QDialog(this)); DatabaseRepairWidget* dbRepairWidget = new DatabaseRepairWidget(dialog.data()); connect(dbRepairWidget, SIGNAL(success()), dialog.data(), SLOT(accept())); @@ -770,8 +776,9 @@ void MainWindow::repairDatabase() KeePass2Writer writer; writer.writeDatabase(saveFileName, dbRepairWidget->database()); if (writer.hasError()) { - QMessageBox::critical(this, tr("Error"), - tr("Writing the database failed.").append("\n\n").append(writer.errorString())); + displayGlobalMessage( + tr("Writing the database failed.").append("\n").append(writer.errorString()), + MessageWidget::Error); } } } @@ -787,3 +794,26 @@ bool MainWindow::isTrayIconEnabled() const && QSystemTrayIcon::isSystemTrayAvailable(); #endif } + +void MainWindow::displayGlobalMessage(const QString& text, MessageWidget::MessageType type) +{ + m_ui->globalMessageWidget->showMessage(text, type); +} + +void MainWindow::displayTabMessage(const QString& text, MessageWidget::MessageType type) +{ + m_ui->tabWidget->currentDatabaseWidget()->showMessage(text, type); +} + +void MainWindow::hideGlobalMessage() +{ + m_ui->globalMessageWidget->hideMessage(); +} + +void MainWindow::hideTabMessage() +{ + if (m_ui->stackedWidget->currentIndex() == 0) { + m_ui->tabWidget->currentDatabaseWidget()->hideMessage(); + } +} + diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index ab9924a7..4182140c 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -71,6 +71,10 @@ private Q_SLOTS: void toggleWindow(); void lockDatabasesAfterInactivity(); void repairDatabase(); + void displayGlobalMessage(const QString& text, MessageWidget::MessageType type); + void displayTabMessage(const QString& text, MessageWidget::MessageType type); + void hideGlobalMessage(); + void hideTabMessage(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 188ef158..bb7ff779 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -2,6 +2,9 @@ MainWindow + + true + 0 @@ -14,6 +17,9 @@ KeePassXC + + true + 0 @@ -27,8 +33,24 @@ 0 + + + + + 0 + 0 + + + + + + + 0 + 0 + + 2 @@ -104,7 +126,7 @@ 0 0 800 - 26 + 29 @@ -452,6 +474,12 @@ + + MessageWidget + QWidget +
gui/MessageWidget.h
+ 1 +
DatabaseTabWidget QTabWidget diff --git a/src/gui/MessageWidget.cpp b/src/gui/MessageWidget.cpp new file mode 100644 index 00000000..9360a6e6 --- /dev/null +++ b/src/gui/MessageWidget.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 Pedro Alves + * + * This program 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 or (at your option) + * version 3 of the License. + * + * This program 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, see . + */ + +#include "MessageWidget.h" + +MessageWidget::MessageWidget(QWidget* parent) + :KMessageWidget(parent) +{ + +} + +void MessageWidget::showMessage(const QString& text, MessageWidget::MessageType type) +{ + setMessageType(type); + setText(text); + animatedShow(); +} + +void MessageWidget::hideMessage() +{ + animatedHide(); +} diff --git a/src/gui/MessageWidget.h b/src/gui/MessageWidget.h new file mode 100644 index 00000000..34c06743 --- /dev/null +++ b/src/gui/MessageWidget.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 Pedro Alves + * + * This program 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 or (at your option) + * version 3 of the License. + * + * This program 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, see . + */ + +#ifndef MESSAGEWIDGET_H +#define MESSAGEWIDGET_H + +#include "gui/KMessageWidget.h" + +class MessageWidget : public KMessageWidget +{ + Q_OBJECT + +public: + explicit MessageWidget(QWidget* parent = 0); + +public Q_SLOTS: + void showMessage(const QString& text, MessageWidget::MessageType type); + void hideMessage(); + +}; + +#endif // MESSAGEWIDGET_H diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index f2372a0d..58e2a770 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -77,6 +77,9 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) connect(this, SIGNAL(accepted()), SLOT(saveEntry())); connect(this, SIGNAL(rejected()), SLOT(cancel())); + + connect(m_iconsWidget, SIGNAL(messageEditEntry(QString, MessageWidget::MessageType)), SLOT(showMessage(QString, MessageWidget::MessageType))); + connect(m_iconsWidget, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); } EditEntryWidget::~EditEntryWidget() @@ -395,12 +398,13 @@ void EditEntryWidget::saveEntry() { if (m_history) { clear(); + hideMessage(); Q_EMIT editFinished(false); return; } if (!passwordsEqual()) { - MessageBox::warning(this, tr("Error"), tr("Different passwords supplied.")); + showMessage(tr("Different passwords supplied."), MessageWidget::Error); return; } @@ -474,6 +478,7 @@ void EditEntryWidget::cancel() { if (m_history) { clear(); + hideMessage(); Q_EMIT editFinished(false); return; } @@ -497,6 +502,7 @@ void EditEntryWidget::clear() m_autoTypeAssoc->clear(); m_historyModel->clear(); m_iconsWidget->reset(); + hideMessage(); } bool EditEntryWidget::hasBeenModified() const @@ -630,15 +636,13 @@ void EditEntryWidget::insertAttachment() QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { - MessageBox::warning(this, tr("Error"), - tr("Unable to open file").append(":\n").append(file.errorString())); + showMessage(tr("Unable to open file").append(":\n").append(file.errorString()), MessageWidget::Error); return; } QByteArray data; if (!Tools::readAllFromDevice(&file, data)) { - MessageBox::warning(this, tr("Error"), - tr("Unable to open file").append(":\n").append(file.errorString())); + showMessage(tr("Unable to open file").append(":\n").append(file.errorString()), MessageWidget::Error); return; } @@ -665,13 +669,11 @@ void EditEntryWidget::saveCurrentAttachment() QFile file(savePath); if (!file.open(QIODevice::WriteOnly)) { - MessageBox::warning(this, tr("Error"), - tr("Unable to save the attachment:\n").append(file.errorString())); + showMessage(tr("Unable to save the attachment:\n").append(file.errorString()), MessageWidget::Error); return; } if (file.write(attachmentData) != attachmentData.size()) { - MessageBox::warning(this, tr("Error"), - tr("Unable to save the attachment:\n").append(file.errorString())); + showMessage(tr("Unable to save the attachment:\n").append(file.errorString()), MessageWidget::Error); return; } } @@ -692,20 +694,17 @@ void EditEntryWidget::openAttachment(const QModelIndex& index) QTemporaryFile* file = new QTemporaryFile(tmpFileTemplate, this); if (!file->open()) { - MessageBox::warning(this, tr("Error"), - tr("Unable to save the attachment:\n").append(file->errorString())); + showMessage(tr("Unable to save the attachment:\n").append(file->errorString()), MessageWidget::Error); return; } if (file->write(attachmentData) != attachmentData.size()) { - MessageBox::warning(this, tr("Error"), - tr("Unable to save the attachment:\n").append(file->errorString())); + showMessage(tr("Unable to save the attachment:\n").append(file->errorString()), MessageWidget::Error); return; } if (!file->flush()) { - MessageBox::warning(this, tr("Error"), - tr("Unable to save the attachment:\n").append(file->errorString())); + showMessage(tr("Unable to save the attachment:\n").append(file->errorString()), MessageWidget::Error); return; } diff --git a/src/gui/group/EditGroupWidget.cpp b/src/gui/group/EditGroupWidget.cpp index 177c62bb..5b9dfcbc 100644 --- a/src/gui/group/EditGroupWidget.cpp +++ b/src/gui/group/EditGroupWidget.cpp @@ -43,6 +43,9 @@ EditGroupWidget::EditGroupWidget(QWidget* parent) connect(this, SIGNAL(accepted()), SLOT(save())); connect(this, SIGNAL(rejected()), SLOT(cancel())); + + connect(m_editGroupWidgetIcons, SIGNAL(messageEditEntry(QString, MessageWidget::MessageType)), SLOT(showMessage(QString, MessageWidget::MessageType))); + connect(m_editGroupWidgetIcons, SIGNAL(messageEditEntryDismiss()), SLOT(hideMessage())); } EditGroupWidget::~EditGroupWidget() diff --git a/src/gui/group/EditGroupWidget.h b/src/gui/group/EditGroupWidget.h index 94ad891d..606cc77b 100644 --- a/src/gui/group/EditGroupWidget.h +++ b/src/gui/group/EditGroupWidget.h @@ -45,6 +45,8 @@ public: Q_SIGNALS: void editFinished(bool accepted); + void messageEditEntry(QString, MessageWidget::MessageType); + void messageEditEntryDismiss(); private Q_SLOTS: void save();