mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00
349 lines
12 KiB
C++
349 lines
12 KiB
C++
/* This file is part of the KDE libraries
|
|
Copyright (C) 1998 Mark Donohoe <donohoe@kde.org>
|
|
Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org>
|
|
Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org>
|
|
Copyright (C) 2001 Ellis Whitehead <ellis@kde.org>
|
|
Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
|
|
Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org>
|
|
Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com>
|
|
Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
|
|
#include "kshortcutsdialog_p.h"
|
|
#include "kaction_p.h"
|
|
|
|
#include <QApplication>
|
|
#include <QHeaderView>
|
|
#include <QKeyEvent>
|
|
#include <QLabel>
|
|
#include <QPainter>
|
|
#include <QTreeWidgetItemIterator>
|
|
|
|
#include "kaction.h"
|
|
|
|
#include "kdebug.h"
|
|
|
|
|
|
|
|
|
|
KShortcutsEditorDelegate::KShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts)
|
|
: KExtendableItemDelegate(parent),
|
|
m_allowLetterShortcuts(allowLetterShortcuts),
|
|
m_editor(0)
|
|
{
|
|
Q_ASSERT(qobject_cast<QAbstractItemView *>(parent));
|
|
|
|
QPixmap pixmap( 16, 16 );
|
|
pixmap.fill( QColor( Qt::transparent ) );
|
|
QPainter p( &pixmap );
|
|
QStyleOption option;
|
|
option.rect = pixmap.rect();
|
|
|
|
bool isRtl = QApplication::isRightToLeft();
|
|
QApplication::style()->drawPrimitive( isRtl ? QStyle::PE_IndicatorArrowLeft : QStyle::PE_IndicatorArrowRight, &option, &p );
|
|
p.end();
|
|
setExtendPixmap( pixmap );
|
|
|
|
pixmap.fill( QColor( Qt::transparent ) );
|
|
p.begin( &pixmap );
|
|
QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowDown, &option, &p );
|
|
p.end();
|
|
setContractPixmap( pixmap );
|
|
|
|
parent->installEventFilter(this);
|
|
|
|
// Listen to activation signals
|
|
// connect(parent, SIGNAL(activated(QModelIndex)), this, SLOT(itemActivated(QModelIndex)));
|
|
connect(parent, SIGNAL(clicked(QModelIndex)), this, SLOT(itemActivated(QModelIndex)));
|
|
|
|
// Listen to collapse signals
|
|
connect(parent, SIGNAL(collapsed(QModelIndex)), this, SLOT(itemCollapsed(QModelIndex)));
|
|
}
|
|
|
|
|
|
void KShortcutsEditorDelegate::stealShortcut(
|
|
const QKeySequence &seq,
|
|
KAction *action)
|
|
{
|
|
QTreeWidget *view = static_cast<QTreeWidget *>(parent());
|
|
|
|
// Iterate over all items
|
|
QTreeWidgetItemIterator it(view, QTreeWidgetItemIterator::NoChildren);
|
|
|
|
for (; (*it); ++it) {
|
|
KShortcutsEditorItem* item = dynamic_cast<KShortcutsEditorItem *>(*it);
|
|
if (item && item->data(0, ObjectRole).value<QObject*>() == action) {
|
|
|
|
// We found the action, snapshot the current state. Steal the
|
|
// shortcut. We will save the change later.
|
|
KShortcut cut = action->shortcut();
|
|
if ( cut.primary().matches(seq) != QKeySequence::NoMatch
|
|
|| seq.matches(cut.primary()) != QKeySequence::NoMatch) {
|
|
item->setKeySequence(LocalPrimary, QKeySequence());
|
|
}
|
|
|
|
if ( cut.alternate().matches(seq) != QKeySequence::NoMatch
|
|
|| seq.matches(cut.alternate()) != QKeySequence::NoMatch) {
|
|
item->setKeySequence(LocalAlternate, QKeySequence());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
QSize KShortcutsEditorDelegate::sizeHint(const QStyleOptionViewItem &option,
|
|
const QModelIndex &index) const
|
|
{
|
|
QSize ret(KExtendableItemDelegate::sizeHint(option, index));
|
|
ret.rheight() += 4;
|
|
return ret;
|
|
}
|
|
|
|
|
|
//slot
|
|
void KShortcutsEditorDelegate::itemActivated(QModelIndex index)
|
|
{
|
|
//As per our constructor our parent *is* a QTreeWidget
|
|
QTreeWidget *view = static_cast<QTreeWidget *>(parent());
|
|
|
|
KShortcutsEditorItem *item = KShortcutsEditorPrivate::itemFromIndex(view, index);
|
|
if (!item) {
|
|
//that probably was a non-leaf (type() !=ActionItem) item
|
|
return;
|
|
}
|
|
|
|
int column = index.column();
|
|
if (column == Name) {
|
|
// If user click in the name column activate the (Global|Local)Primary
|
|
// column if possible.
|
|
if (!view->header()->isSectionHidden(LocalPrimary)) {
|
|
column = LocalPrimary;
|
|
} else if (!view->header()->isSectionHidden(GlobalPrimary)) {
|
|
column = GlobalPrimary;
|
|
} else {
|
|
// do nothing.
|
|
}
|
|
index = index.sibling(index.row(), column);
|
|
view->selectionModel()->select(index, QItemSelectionModel::SelectCurrent);
|
|
}
|
|
|
|
// Check if the models wants us to edit the item at index
|
|
if (!index.data(ShowExtensionIndicatorRole).value<bool>()) {
|
|
return;
|
|
}
|
|
|
|
if (!isExtended(index)) {
|
|
//we only want maximum ONE extender open at any time.
|
|
if (m_editingIndex.isValid()) {
|
|
KShortcutsEditorItem *oldItem = KShortcutsEditorPrivate::itemFromIndex(view,
|
|
m_editingIndex);
|
|
Q_ASSERT(oldItem); //here we really expect nothing but a real KShortcutsEditorItem
|
|
|
|
oldItem->setNameBold(false);
|
|
contractItem(m_editingIndex);
|
|
}
|
|
|
|
m_editingIndex = index;
|
|
QWidget *viewport = static_cast<QAbstractItemView*>(parent())->viewport();
|
|
|
|
if (column >= LocalPrimary && column <= GlobalAlternate) {
|
|
ShortcutEditWidget *editor = new ShortcutEditWidget(viewport,
|
|
index.data(DefaultShortcutRole).value<QKeySequence>(),
|
|
index.data(ShortcutRole).value<QKeySequence>(),
|
|
m_allowLetterShortcuts);
|
|
if (column==GlobalPrimary) {
|
|
QObject *action = index.data(ObjectRole).value<QObject*>();
|
|
connect(
|
|
action, SIGNAL(globalShortcutChanged(QKeySequence)),
|
|
editor, SLOT(setKeySequence(QKeySequence)));
|
|
editor->setMultiKeyShortcutsAllowed(false);
|
|
KAction *kaction = qobject_cast<KAction*>(action);
|
|
if (kaction) {
|
|
editor->setComponentName(kaction->d->componentData.componentName());
|
|
}
|
|
}
|
|
|
|
m_editor = editor;
|
|
// For global shortcuts check against the kde standard shortcuts
|
|
if (column == GlobalPrimary || column == GlobalAlternate) {
|
|
editor->setCheckForConflictsAgainst(
|
|
KKeySequenceWidget::LocalShortcuts
|
|
| KKeySequenceWidget::GlobalShortcuts
|
|
| KKeySequenceWidget::StandardShortcuts );
|
|
}
|
|
|
|
editor->setCheckActionCollections(m_checkActionCollections);
|
|
|
|
connect(m_editor, SIGNAL(keySequenceChanged(QKeySequence)),
|
|
this, SLOT(keySequenceChanged(QKeySequence)));
|
|
connect(m_editor, SIGNAL(stealShortcut(QKeySequence,KAction*)),
|
|
this, SLOT(stealShortcut(QKeySequence,KAction*)));
|
|
|
|
} else if (column == RockerGesture) {
|
|
m_editor = new QLabel("A lame placeholder", viewport);
|
|
|
|
} else if (column == ShapeGesture) {
|
|
m_editor = new QLabel("<i>A towel</i>", viewport);
|
|
|
|
} else
|
|
return;
|
|
|
|
m_editor->installEventFilter(this);
|
|
item->setNameBold(true);
|
|
extendItem(m_editor, index);
|
|
|
|
} else {
|
|
//the item is extended, and clicking on it again closes it
|
|
item->setNameBold(false);
|
|
contractItem(index);
|
|
view->selectionModel()->select(index, QItemSelectionModel::Clear);
|
|
m_editingIndex = QModelIndex();
|
|
m_editor = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//slot
|
|
void KShortcutsEditorDelegate::itemCollapsed(QModelIndex index)
|
|
{
|
|
if (!m_editingIndex.isValid()) {
|
|
return;
|
|
}
|
|
|
|
const QAbstractItemModel *model = index.model();
|
|
for (int row = 0; row < model->rowCount(index); ++row) {
|
|
QModelIndex rowIndex = model->index(row, 0, index);
|
|
|
|
for (int col = 0; col < index.model()->columnCount(index); ++col) {
|
|
QModelIndex colIndex = model->index(row, col, index);
|
|
|
|
if (colIndex == m_editingIndex) {
|
|
itemActivated(m_editingIndex); //this will *close* the item's editor because it's already open
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//slot
|
|
void KShortcutsEditorDelegate::hiddenBySearchLine(QTreeWidgetItem *item, bool hidden)
|
|
{
|
|
if (!hidden || !item) {
|
|
return;
|
|
}
|
|
QTreeWidget *view = static_cast<QTreeWidget *>(parent());
|
|
QTreeWidgetItem *editingItem = KShortcutsEditorPrivate::itemFromIndex(view, m_editingIndex);
|
|
if (editingItem == item) {
|
|
itemActivated(m_editingIndex); //this will *close* the item's editor because it's already open
|
|
}
|
|
}
|
|
|
|
|
|
bool KShortcutsEditorDelegate::eventFilter(QObject *o, QEvent *e)
|
|
{
|
|
if (o == m_editor) {
|
|
//Prevent clicks in the empty part of the editor widget from closing the editor
|
|
//because they would propagate to the itemview and be interpreted as a click in
|
|
//an item's rect. That in turn would lead to an itemActivated() call, closing
|
|
//the current editor.
|
|
|
|
switch (e->type()) {
|
|
case QEvent::MouseButtonPress:
|
|
case QEvent::MouseButtonRelease:
|
|
case QEvent::MouseButtonDblClick:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
} else if (o == parent()) {
|
|
// Make left/right cursor keys switch items instead of operate the scroll bar
|
|
// (subclassing QtreeView/Widget would be cleaner but much more of a hassle)
|
|
// Note that in our case we know that the selection mode is SingleSelection,
|
|
// so we don't have to ask QAbstractItemView::selectionCommand() et al.
|
|
|
|
if (e->type() != QEvent::KeyPress) {
|
|
return false;
|
|
}
|
|
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
|
|
QTreeWidget *view = static_cast<QTreeWidget *>(parent());
|
|
QItemSelectionModel *selection = view->selectionModel();
|
|
QModelIndex index = selection->currentIndex();
|
|
|
|
switch (ke->key()) {
|
|
case Qt::Key_Space:
|
|
case Qt::Key_Select:
|
|
// we are not using the standard "open editor" mechanism of QAbstractItemView,
|
|
// so let's emulate that here.
|
|
itemActivated(index);
|
|
return true;
|
|
case Qt::Key_Left:
|
|
index = index.sibling(index.row(), index.column() - 1);
|
|
break;
|
|
case Qt::Key_Right:
|
|
index = index.sibling(index.row(), index.column() + 1);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
// a cursor key was pressed
|
|
if (index.isValid()) {
|
|
selection->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
|
|
//### using PositionAtCenter for now;
|
|
// EnsureVisible has no effect which seems to be a bug.
|
|
view->scrollTo(index, QAbstractItemView::PositionAtCenter);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//slot
|
|
void KShortcutsEditorDelegate::keySequenceChanged(const QKeySequence &seq)
|
|
{
|
|
QVariant ret = QVariant::fromValue(seq);
|
|
emit shortcutChanged(ret, m_editingIndex);
|
|
}
|
|
|
|
|
|
void KShortcutsEditorDelegate::setCheckActionCollections(
|
|
const QList<KActionCollection*> checkActionCollections )
|
|
{
|
|
m_checkActionCollections = checkActionCollections;
|
|
}
|
|
|
|
//slot
|
|
void KShortcutsEditorDelegate::shapeGestureChanged(const KShapeGesture &gest)
|
|
{
|
|
//this is somewhat verbose because the gesture types are not "built in" to QVariant
|
|
QVariant ret = QVariant::fromValue(gest);
|
|
emit shortcutChanged(ret, m_editingIndex);
|
|
}
|
|
|
|
|
|
//slot
|
|
void KShortcutsEditorDelegate::rockerGestureChanged(const KRockerGesture &gest)
|
|
{
|
|
QVariant ret = QVariant::fromValue(gest);
|
|
emit shortcutChanged(ret, m_editingIndex);
|
|
}
|
|
|