You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

284 lines
8.6 KiB

/* This file is part of the KDE project
Copyright (C) 2004 Esben Mose Hansen <kde@mosehansen.dk>
Copyright (C) by Andrew Stanley-Jones <asj@cban.com>
Copyright (C) 2000 by Carsten Pfeiffer <pfeiffer@kde.org>
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 of the License, or (at your option) any later version.
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; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "klipperpopup.h"
#include <QApplication>
#include "klipper_debug.h"
#include <QDesktopWidget>
#include <QKeyEvent>
#include <QWidgetAction>
#include <KHelpMenu>
#include <KLineEdit>
#include <KLocalizedString>
#include <KWindowInfo>
#include "history.h"
#include "klipper.h"
#include "popupproxy.h"
namespace {
static const int TOP_HISTORY_ITEM_INDEX = 2;
}
// #define DEBUG_EVENTS__
#ifdef DEBUG_EVENTS__
kdbgstream& operator<<( kdbgstream& stream, const QKeyEvent& e ) {
stream << "(QKeyEvent(text=" << e.text() << ",key=" << e.key() << ( e.isAccepted()?",accepted":",ignored)" ) << ",count=" << e.count();
if ( e.modifiers() & Qt::AltModifier ) {
stream << ",ALT";
}
if ( e.modifiers() & Qt::ControlModifier ) {
stream << ",CTRL";
}
if ( e.modifiers() & Qt::MetaModifier ) {
stream << ",META";
}
if ( e.modifiers() & Qt::ShiftModifier ) {
stream << ",SHIFT";
}
if ( e.isAutoRepeat() ) {
stream << ",AUTOREPEAT";
}
stream << ")";
return stream;
}
#endif
KlipperPopup::KlipperPopup( History* history )
: m_dirty( true ),
m_textForEmptyHistory( i18n( "<empty clipboard>" ) ),
m_textForNoMatch( i18n( "<no matches>" ) ),
m_history( history ),
m_helpMenu( nullptr ),
m_popupProxy( nullptr ),
m_filterWidget( nullptr ),
m_filterWidgetAction( nullptr ),
m_nHistoryItems( 0 ),
m_showHelp(true),
m_lastEvent(nullptr)
{
ensurePolished();
KWindowInfo windowInfo( winId(), NET::WMGeometry );
QRect geometry = windowInfo.geometry();
QRect screen = qApp->desktop()->screenGeometry(geometry.center());
int menuHeight = ( screen.height() ) * 3/4;
int menuWidth = ( screen.width() ) * 1/3;
m_popupProxy = new PopupProxy( this, menuHeight, menuWidth );
connect(this, &KlipperPopup::aboutToShow, this, &KlipperPopup::slotAboutToShow);
}
KlipperPopup::~KlipperPopup() {
}
void KlipperPopup::slotAboutToShow() {
if ( m_filterWidget ) {
if ( !m_filterWidget->text().isEmpty() ) {
m_dirty = true;
m_filterWidget->clear();
}
}
ensureClean();
}
void KlipperPopup::ensureClean() {
// If the history is unchanged since last menu build, the is no reason
// to rebuild it,
if ( m_dirty ) {
rebuild();
}
}
void KlipperPopup::buildFromScratch() {
addSection(QIcon::fromTheme(QStringLiteral("klipper")), i18n("Klipper - Clipboard Tool"));
m_filterWidget = new KLineEdit(this);
m_filterWidget->setFocusPolicy( Qt::NoFocus );
m_filterWidget->setPlaceholderText(i18n("Search..."));
m_filterWidgetAction = new QWidgetAction(this);
m_filterWidgetAction->setDefaultWidget(m_filterWidget);
addAction(m_filterWidgetAction);
addSeparator();
for (int i = 0; i < m_actions.count(); i++) {
if (i + 1 == m_actions.count() && m_showHelp) {
if (!m_helpMenu) {
m_helpMenu = new KHelpMenu( this, i18n("KDE cut & paste history utility"), false );
}
addMenu(m_helpMenu->menu())->setIcon(QIcon::fromTheme(QStringLiteral("help-contents")));
addSeparator();
}
addAction(m_actions.at(i));
}
}
void KlipperPopup::rebuild( const QString& filter ) {
if (actions().isEmpty()) {
buildFromScratch();
} else {
for ( int i=0; i<m_nHistoryItems; i++ ) {
Q_ASSERT(TOP_HISTORY_ITEM_INDEX < actions().count());
removeAction(actions().at(TOP_HISTORY_ITEM_INDEX));
}
}
// We search case insensitive until one uppercased character appears in the search term
Qt::CaseSensitivity caseSens = (filter.toLower() == filter ? Qt::CaseInsensitive : Qt::CaseSensitive);
QRegExp filterexp( filter, caseSens );
QPalette palette = m_filterWidget->palette();
if ( filterexp.isValid() ) {
palette.setColor( m_filterWidget->foregroundRole(), palette.color(foregroundRole()) );
} else {
palette.setColor( m_filterWidget->foregroundRole(), Qt::red );
}
m_nHistoryItems = m_popupProxy->buildParent( TOP_HISTORY_ITEM_INDEX, filterexp );
if ( m_nHistoryItems == 0 ) {
if ( m_history->empty() ) {
insertAction(actions().at(TOP_HISTORY_ITEM_INDEX), new QAction(m_textForEmptyHistory, this));
} else {
palette.setColor( m_filterWidget->foregroundRole(), Qt::red );
insertAction(actions().at(TOP_HISTORY_ITEM_INDEX), new QAction(m_textForNoMatch, this));
}
m_nHistoryItems++;
} else {
if ( history()->topIsUserSelected() ) {
actions().at(TOP_HISTORY_ITEM_INDEX)->setCheckable(true);
actions().at(TOP_HISTORY_ITEM_INDEX)->setChecked(true);
}
}
m_filterWidget->setPalette( palette );
m_dirty = false;
}
void KlipperPopup::slotTopIsUserSelectedSet() {
if ( !m_dirty && m_nHistoryItems > 0 && history()->topIsUserSelected() ) {
actions().at(TOP_HISTORY_ITEM_INDEX)->setCheckable(true);
actions().at(TOP_HISTORY_ITEM_INDEX)->setChecked(true);
}
}
void KlipperPopup::plugAction( QAction* action ) {
m_actions.append(action);
}
/* virtual */
void KlipperPopup::keyPressEvent( QKeyEvent* e ) {
// Most events are send down directly to the m_filterWidget.
// If the m_filterWidget does not handle the event, it will
// come back to this method. Remembering the last event stops
// the infinite event loop
if (m_lastEvent == e) {
m_lastEvent= nullptr;
return;
}
m_lastEvent= e;
// If alt-something is pressed, select a shortcut
// from the menu. Do this by sending a keyPress
// without the alt-modifier to the superobject.
if ( e->modifiers() & Qt::AltModifier ) {
QKeyEvent ke( QEvent::KeyPress,
e->key(),
e->modifiers() ^ Qt::AltModifier,
e->text(),
e->isAutoRepeat(),
e->count() );
QMenu::keyPressEvent( &ke );
#ifdef DEBUG_EVENTS__
qCDebug(KLIPPER_LOG) << "Passing this event to ancestor (KMenu): " << e << "->" << ke;
#endif
if (ke.isAccepted()) {
e->accept();
return;
} else {
e->ignore();
}
}
// Otherwise, send most events to the search
// widget, except a few used for navigation:
// These go to the superobject.
switch( e->key() ) {
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Right:
case Qt::Key_Left:
case Qt::Key_Tab:
case Qt::Key_Backtab:
case Qt::Key_Escape:
{
#ifdef DEBUG_EVENTS__
qCDebug(KLIPPER_LOG) << "Passing this event to ancestor (KMenu): " << e;
#endif
QMenu::keyPressEvent(e);
break;
}
case Qt::Key_Return:
case Qt::Key_Enter:
{
QMenu::keyPressEvent(e);
this->hide();
if (activeAction() == m_filterWidgetAction)
setActiveAction(actions().at(TOP_HISTORY_ITEM_INDEX));
break;
}
default:
{
#ifdef DEBUG_EVENTS__
qCDebug(KLIPPER_LOG) << "Passing this event down to child (KLineEdit): " << e;
#endif
setActiveAction(actions().at(actions().indexOf(m_filterWidgetAction)));
QString lastString = m_filterWidget->text();
QApplication::sendEvent(m_filterWidget, e);
if (m_filterWidget->text() != lastString) {
m_dirty = true;
rebuild(m_filterWidget->text());
}
break;
} //default:
} //case
m_lastEvent= nullptr;
}
void KlipperPopup::slotSetTopActive()
{
if (actions().size() > TOP_HISTORY_ITEM_INDEX) {
setActiveAction(actions().at(TOP_HISTORY_ITEM_INDEX));
}
}