From f43ad8d062074fc24e1e38c6259658d5d9857234 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Sat, 26 May 2012 16:20:32 +0200 Subject: [PATCH] Support copying username and password to the clipboard. --- CMakeLists.txt | 7 ++- src/CMakeLists.txt | 6 +++ src/gui/Clipboard.cpp | 82 +++++++++++++++++++++++++++++++++++ src/gui/Clipboard.h | 46 ++++++++++++++++++++ src/gui/DatabaseTabWidget.cpp | 10 +++++ src/gui/DatabaseTabWidget.h | 2 + src/gui/DatabaseWidget.cpp | 33 ++++++++++++++ src/gui/DatabaseWidget.h | 4 ++ src/gui/MainWindow.cpp | 20 +++++++-- src/gui/MainWindow.ui | 18 ++++++++ tests/CMakeLists.txt | 4 ++ 11 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 src/gui/Clipboard.cpp create mode 100644 src/gui/Clipboard.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5313d24f6..76e1aca47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,12 @@ if(WITH_TESTS) enable_testing() endif(WITH_TESTS) -find_package(Qt4 4.6.0 REQUIRED QtCore QtGui QtTest) +set(QT_REQUIRED_MODULES QtCore QtGui QtTest) +if(UNIX AND NOT APPLE) + set(QT_REQUIRED_MODULES ${QT_REQUIRED_MODULES} QtDBus) +endif() + +find_package(Qt4 4.6.0 REQUIRED ${QT_REQUIRED_MODULES}) add_definitions(${QT_DEFINITIONS} -DQT_CORE_LIB -DQT_GUI_LIB) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG QT_DEBUG) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_RELEASE QT_NO_DEBUG) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a616175f4..b12237d04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ set(keepassx_SOURCES gui/AboutDialog.cpp gui/Application.cpp gui/ChangeMasterKeyWidget.cpp + gui/Clipboard.cpp gui/DatabaseOpenDialog.cpp gui/DatabaseSettingsWidget.cpp gui/DatabaseTabWidget.cpp @@ -96,6 +97,7 @@ set(keepassx_MOC gui/AboutDialog.h gui/Application.h gui/ChangeMasterKeyWidget.h + gui/Clipboard.h gui/DatabaseOpenDialog.h gui/DatabaseSettingsWidget.h gui/DatabaseTabWidget.h @@ -154,6 +156,10 @@ target_link_libraries(${PROGNAME} ${GCRYPT_LIBRARIES} ${ZLIB_LIBRARIES}) +if(UNIX AND NOT APPLE) + target_link_libraries(${PROGNAME} ${QT_QTDBUS_LIBRARY}) +endif() + if(APPLE) configure_file(${CMAKE_SOURCE_DIR}/share/macosx/Info.plist.cmake ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) set_target_properties(${PROGNAME} PROPERTIES diff --git a/src/gui/Clipboard.cpp b/src/gui/Clipboard.cpp new file mode 100644 index 000000000..fd0528ae6 --- /dev/null +++ b/src/gui/Clipboard.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 Felix Geyer + * + * 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 "Clipboard.h" + +#include +#include +#include + +#ifdef Q_WS_X11 + #include + #include +#endif + +Clipboard::Clipboard(QObject* parent) + : QObject(parent) + , m_timer(new QTimer(this)) +{ + m_timer->setSingleShot(true); + connect(m_timer, SIGNAL(timeout()), SLOT(clearClipboard())); +} + +Clipboard::~Clipboard() +{ + if (m_timer->isActive()) { + clearClipboard(); + } +} + +void Clipboard::setText(const QString& text, int clearTimeout) +{ + QClipboard* clipboard = QApplication::clipboard(); + + clipboard->setText(text, QClipboard::Clipboard); + if (clipboard->supportsSelection()) { + clipboard->setText(text, QClipboard::Selection); + } + + if (clearTimeout > 0) { + m_timer->start(clearTimeout); + } +} + +void Clipboard::clearClipboard() +{ + QClipboard* clipboard = QApplication::clipboard(); + + clipboard->clear(QClipboard::Clipboard); + if (clipboard->supportsSelection()) { + clipboard->clear(QClipboard::Selection); + } + +#ifdef Q_WS_X11 + QDBusMessage message = QDBusMessage::createMethodCall("org.kde.klipper", "/klipper", "", "clearClipboardHistory"); + QDBusConnection::sessionBus().send(message); +#endif +} + +Clipboard* clipboard() +{ + static Clipboard* instance(0); + + if (!instance) { + instance = new Clipboard(qApp); + } + + return instance; +} diff --git a/src/gui/Clipboard.h b/src/gui/Clipboard.h new file mode 100644 index 000000000..c9c7737f4 --- /dev/null +++ b/src/gui/Clipboard.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Felix Geyer + * + * 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 KEEPASSX_CLIPBOARD_H +#define KEEPASSX_CLIPBOARD_H + +#include + +class QTimer; + +class Clipboard : public QObject +{ + Q_OBJECT + +public: + ~Clipboard(); + void setText(const QString& text, int clearTimeout = 0); + +private: + Clipboard(QObject* parent = 0); + + QTimer* m_timer; + +private Q_SLOTS: + void clearClipboard(); + + friend Clipboard* clipboard(); +}; + +Clipboard* clipboard(); + +#endif // KEEPASSX_CLIPBOARD_H diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index f92284647..50c32fdc7 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -349,6 +349,16 @@ void DatabaseTabWidget::deleteEntry() currentDatabaseWidget()->deleteEntry(); } +void DatabaseTabWidget::copyUsername() +{ + currentDatabaseWidget()->copyUsername(); +} + +void DatabaseTabWidget::copyPassword() +{ + currentDatabaseWidget()->copyPassword(); +} + void DatabaseTabWidget::createGroup() { currentDatabaseWidget()->createGroup(); diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index 5d855d769..740fa38ad 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -66,6 +66,8 @@ public Q_SLOTS: void cloneEntry(); void editEntry(); void deleteEntry(); + void copyUsername(); + void copyPassword(); void createGroup(); void editGroup(); void deleteGroup(); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 80217d04d..495808525 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -31,6 +31,7 @@ #include "core/Metadata.h" #include "core/Tools.h" #include "gui/ChangeMasterKeyWidget.h" +#include "gui/Clipboard.h" #include "gui/DatabaseSettingsWidget.h" #include "gui/entry/EditEntryWidget.h" #include "gui/entry/EntryView.h" @@ -122,6 +123,12 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) m_actionEntryEditView->setEnabled(false); m_actionEntryDelete = m_menuEntry->addAction(tr("Delete entry"), this, SLOT(deleteEntry())); m_actionEntryDelete->setEnabled(false); + m_actionEntryCopyUsername = m_menuEntry->addAction(tr("Copy username to clipboard"), this, + SLOT(copyUsername()), Qt::CTRL + Qt::Key_B); + m_actionEntryCopyUsername->setEnabled(false); + m_actionEntryCopyPassword = m_menuEntry->addAction(tr("Copy password to clipboard"), this, + SLOT(copyPassword()), Qt::CTRL + Qt::Key_C); + m_actionEntryCopyPassword->setEnabled(false); m_actionGroupNew = m_menuGroup->addAction(tr("Add new group"), this, SLOT(createGroup())); m_actionGroupEdit = m_menuGroup->addAction(tr("Edit group"), this, SLOT(switchToGroupEdit())); @@ -238,6 +245,30 @@ void DatabaseWidget::deleteEntry() } } +void DatabaseWidget::copyUsername() +{ + Entry* currentEntry = m_entryView->currentEntry(); + if (!currentEntry) { + Q_ASSERT(false); + return; + } + + // TODO: set clearTimeout + clipboard()->setText(currentEntry->username()); +} + +void DatabaseWidget::copyPassword() +{ + Entry* currentEntry = m_entryView->currentEntry(); + if (!currentEntry) { + Q_ASSERT(false); + return; + } + + // TODO: set clearTimeout + clipboard()->setText(currentEntry->password()); +} + void DatabaseWidget::createGroup() { if (!m_groupView->currentGroup()) { @@ -493,6 +524,8 @@ void DatabaseWidget::updateEntryActions() m_actionEntryClone->setEnabled(singleEntrySelected && !inSearch); m_actionEntryEditView->setEnabled(singleEntrySelected); m_actionEntryDelete->setEnabled(singleEntrySelected); + m_actionEntryCopyUsername->setEnabled(singleEntrySelected); + m_actionEntryCopyPassword->setEnabled(singleEntrySelected); } void DatabaseWidget::showGroupContextMenu(const QPoint& pos) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 79a054258..80b8be23e 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -66,6 +66,8 @@ public Q_SLOTS: void createEntry(); void cloneEntry(); void deleteEntry(); + void copyUsername(); + void copyPassword(); void createGroup(); void deleteGroup(); void switchToEntryEdit(); @@ -123,6 +125,8 @@ private: QAction* m_actionEntryClone; QAction* m_actionEntryEditView; QAction* m_actionEntryDelete; + QAction* m_actionEntryCopyUsername; + QAction* m_actionEntryCopyPassword; }; #endif // KEEPASSX_DATABASEWIDGET_H diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 2e67e4fdc..8e0350984 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -41,6 +41,8 @@ MainWindow::MainWindow() setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W); setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q); setShortcut(m_ui->actionSearch, QKeySequence::Find, Qt::CTRL + Qt::Key_F); + m_ui->actionEntryCopyUsername->setShortcut(Qt::CTRL + Qt::Key_B); + m_ui->actionEntryCopyPassword->setShortcut(Qt::CTRL + Qt::Key_C); connect(m_ui->tabWidget, SIGNAL(entrySelectionChanged(bool)), SLOT(setMenuActionState())); @@ -78,6 +80,10 @@ MainWindow::MainWindow() SLOT(editEntry())); connect(m_ui->actionEntryDelete, SIGNAL(triggered()), m_ui->tabWidget, SLOT(deleteEntry())); + connect(m_ui->actionEntryCopyUsername, SIGNAL(triggered()), m_ui->tabWidget, + SLOT(copyUsername())); + connect(m_ui->actionEntryCopyPassword, SIGNAL(triggered()), m_ui->tabWidget, + SLOT(copyPassword())); connect(m_ui->actionGroupNew, SIGNAL(triggered()), m_ui->tabWidget, SLOT(createGroup())); @@ -121,6 +127,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionEntryClone->setEnabled(!inSearch && singleEntry); m_ui->actionEntryEdit->setEnabled(singleEntry); m_ui->actionEntryDelete->setEnabled(singleEntry); + m_ui->actionEntryCopyUsername->setEnabled(singleEntry); + m_ui->actionEntryCopyPassword->setEnabled(singleEntry); m_ui->actionGroupNew->setEnabled(!inSearch); m_ui->actionGroupEdit->setEnabled(!inSearch); m_ui->actionGroupDelete->setEnabled(!inSearch && dbWidget->canDeleteCurrentGoup()); @@ -134,11 +142,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) } case DatabaseWidget::EditMode: m_ui->actionEntryNew->setEnabled(false); - m_ui->actionGroupNew->setEnabled(false); m_ui->actionEntryClone->setEnabled(false); m_ui->actionEntryEdit->setEnabled(false); - m_ui->actionGroupEdit->setEnabled(false); m_ui->actionEntryDelete->setEnabled(false); + m_ui->actionEntryCopyUsername->setEnabled(false); + m_ui->actionEntryCopyPassword->setEnabled(false); + m_ui->actionGroupNew->setEnabled(false); + m_ui->actionGroupEdit->setEnabled(false); m_ui->actionGroupDelete->setEnabled(false); m_ui->actionSearch->setEnabled(false); m_ui->actionSearch->setChecked(false); @@ -154,11 +164,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) } else { m_ui->actionEntryNew->setEnabled(false); - m_ui->actionGroupNew->setEnabled(false); m_ui->actionEntryClone->setEnabled(false); m_ui->actionEntryEdit->setEnabled(false); - m_ui->actionGroupEdit->setEnabled(false); m_ui->actionEntryDelete->setEnabled(false); + m_ui->actionEntryCopyUsername->setEnabled(false); + m_ui->actionEntryCopyPassword->setEnabled(false); + m_ui->actionGroupNew->setEnabled(false); + m_ui->actionGroupEdit->setEnabled(false); m_ui->actionGroupDelete->setEnabled(false); m_ui->actionSearch->setEnabled(false); m_ui->actionSearch->setChecked(false); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index 024c244f0..b299f558f 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -70,6 +70,8 @@ + + @@ -234,6 +236,22 @@ Find + + + false + + + Copy username to clipboard + + + + + false + + + Copy password to clipboard + + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dee9efe75..5a8350414 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -96,6 +96,10 @@ set(TEST_LIBRARIES ${ZLIB_LIBRARIES} ) +if(UNIX AND NOT APPLE) + set(TEST_LIBRARIES ${TEST_LIBRARIES} ${QT_QTDBUS_LIBRARY}) +endif() + set(modeltest_SOURCRS modeltest.cpp) qt4_wrap_cpp(modeltest_SOURCRS modeltest.h) add_library(modeltest STATIC ${modeltest_SOURCRS})