/* This file is part of the KDE project Copyright (C) 2004 Esben Mose Hansen Copyright (C) by Andrew Stanley-Jones Copyright (C) 2000 by Carsten Pfeiffer 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 #include "klipper_debug.h" #include #include #include #include #include #include #include #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( "" ) ), m_textForNoMatch( i18n( "" ) ), 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; ipalette(); 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)); } }