KXmlGui

kshortcutseditordelegate.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
4 SPDX-FileCopyrightText: 1997 Nicolas Hadacek <hadacek@kde.org>
5 SPDX-FileCopyrightText: 1998 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
7 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
8 SPDX-FileCopyrightText: 2007 Roberto Raggi <roberto@kdevelop.org>
9 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
10 SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
11
12 SPDX-License-Identifier: LGPL-2.0-or-later
13*/
14#include "kshortcutsdialog_p.h"
15
16#include <QAction>
17#include <QApplication>
18#include <QHeaderView>
19#include <QKeyEvent>
20#include <QLabel>
21#include <QPainter>
22#include <QTreeWidgetItemIterator>
23
24KShortcutsEditorDelegate::KShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts)
26 , m_allowLetterShortcuts(allowLetterShortcuts)
27{
28 Q_ASSERT(qobject_cast<QAbstractItemView *>(parent));
29
30 const QSize indicatorSize(16, 16);
31 const qreal dpr = parent->devicePixelRatioF();
32 QPixmap pixmap(indicatorSize * dpr);
33
34 pixmap.fill(QColor(Qt::transparent));
35 pixmap.setDevicePixelRatio(dpr);
36 QPainter p(&pixmap);
37 QStyleOption option;
38 option.rect = QRect(QPoint(0, 0), indicatorSize);
39
40 bool isRtl = QApplication::isRightToLeft();
42 p.end();
43 setExtendPixmap(pixmap);
44
45 pixmap.fill(QColor(Qt::transparent));
46 pixmap.setDevicePixelRatio(dpr);
47 p.begin(&pixmap);
49 p.end();
50 setContractPixmap(pixmap);
51
52 parent->installEventFilter(this);
53
54 // Listen to activation signals
55 // connect(parent, SIGNAL(activated(QModelIndex)), this, SLOT(itemActivated(QModelIndex)));
56 connect(parent, &QAbstractItemView::clicked, this, &KShortcutsEditorDelegate::itemActivated);
57
58 // Listen to collapse signals
59 connect(parent, &QTreeView::collapsed, this, &KShortcutsEditorDelegate::itemCollapsed);
60}
61
62void KShortcutsEditorDelegate::stealShortcut(const QKeySequence &seq, QAction *action)
63{
64 QTreeWidget *view = static_cast<QTreeWidget *>(parent());
65
66 // Iterate over all items
68
69 for (; (*it); ++it) {
70 KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it);
71 if (item && item->data(0, ObjectRole).value<QObject *>() == action) {
72 // We found the action, snapshot the current state. Steal the
73 // shortcut. We will save the change later.
74 const QList<QKeySequence> cut = action->shortcuts();
75 const QKeySequence primary = cut.isEmpty() ? QKeySequence() : cut.at(0);
76 const QKeySequence alternate = cut.size() <= 1 ? QKeySequence() : cut.at(1);
77
78 if (primary.matches(seq) != QKeySequence::NoMatch //
79 || seq.matches(primary) != QKeySequence::NoMatch) {
80 item->setKeySequence(LocalPrimary, QKeySequence());
81 }
82
83 if (alternate.matches(seq) != QKeySequence::NoMatch //
84 || seq.matches(alternate) != QKeySequence::NoMatch) {
85 item->setKeySequence(LocalAlternate, QKeySequence());
86 }
87 break;
88 }
89 }
90}
91
92QSize KShortcutsEditorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
93{
94 QSize ret(KExtendableItemDelegate::sizeHint(option, index));
95 ret.rheight() += 4;
96 return ret;
97}
98
99// slot
100void KShortcutsEditorDelegate::itemActivated(const QModelIndex &_index)
101{
102 // As per our constructor our parent *is* a QTreeWidget
103 QTreeWidget *view = static_cast<QTreeWidget *>(parent());
104 QModelIndex index(_index);
105
106 KShortcutsEditorItem *item = KShortcutsEditorPrivate::itemFromIndex(view, index);
107 if (!item) {
108 // that probably was a non-leaf (type() !=ActionItem) item
109 return;
110 }
111
112 int column = index.column();
113 if (column == Name) {
114 // If user click in the name column activate the (Global|Local)Primary
115 // column if possible.
116 if (!view->header()->isSectionHidden(LocalPrimary)) {
117 column = LocalPrimary;
118 } else if (!view->header()->isSectionHidden(GlobalPrimary)) {
119 column = GlobalPrimary;
120 } else {
121 // do nothing.
122 }
123 index = index.sibling(index.row(), column);
125 }
126
127 // Check if the models wants us to edit the item at index
128 if (!index.data(ShowExtensionIndicatorRole).toBool()) {
129 return;
130 }
131
132 if (!isExtended(index)) {
133 // we only want maximum ONE extender open at any time.
134 if (m_editingIndex.isValid()) {
135 KShortcutsEditorItem *oldItem = KShortcutsEditorPrivate::itemFromIndex(view, m_editingIndex);
136 Q_ASSERT(oldItem); // here we really expect nothing but a real KShortcutsEditorItem
137
138 oldItem->setNameBold(false);
139 contractItem(m_editingIndex);
140 }
141
142 m_editingIndex = index;
143 QWidget *viewport = static_cast<QAbstractItemView *>(parent())->viewport();
144
145 if (column >= LocalPrimary && column <= GlobalAlternate) {
146 ShortcutEditWidget *editor = new ShortcutEditWidget(viewport,
147 index.data(DefaultShortcutRole).value<QKeySequence>(),
148 index.data(ShortcutRole).value<QKeySequence>(),
149 m_allowLetterShortcuts);
150 if (column == GlobalPrimary) {
151 QObject *action = index.data(ObjectRole).value<QObject *>();
152 editor->setAction(action);
153 editor->setMultiKeyShortcutsAllowed(false);
154 QString componentName = action->property("componentName").toString();
155 if (componentName.isEmpty()) {
156 componentName = QCoreApplication::applicationName();
157 }
158 editor->setComponentName(componentName);
159 }
160
161 m_editor = editor;
162 // For global shortcuts check against the kde standard shortcuts
163 if (column == GlobalPrimary || column == GlobalAlternate) {
166 }
167
168 editor->setCheckActionCollections(m_checkActionCollections);
169
170 connect(editor, &ShortcutEditWidget::keySequenceChanged, this, &KShortcutsEditorDelegate::keySequenceChanged);
171 connect(editor, &ShortcutEditWidget::stealShortcut, this, &KShortcutsEditorDelegate::stealShortcut);
172
173 } else if (column == RockerGesture) {
174 m_editor = new QLabel(QStringLiteral("A lame placeholder"), viewport);
175
176 } else if (column == ShapeGesture) {
177 m_editor = new QLabel(QStringLiteral("<i>A towel</i>"), viewport);
178
179 } else {
180 return;
181 }
182
183 m_editor->installEventFilter(this);
184 item->setNameBold(true);
185 extendItem(m_editor, index);
186
187 } else {
188 // the item is extended, and clicking on it again closes it
189 item->setNameBold(false);
190 contractItem(index);
192 m_editingIndex = QModelIndex();
193 m_editor = nullptr;
194 }
195}
196
197// slot
198void KShortcutsEditorDelegate::itemCollapsed(const QModelIndex &index)
199{
200 if (!m_editingIndex.isValid()) {
201 return;
202 }
203
204 const QAbstractItemModel *model = index.model();
205 for (int row = 0; row < model->rowCount(index); ++row) {
206 for (int col = 0; col < index.model()->columnCount(index); ++col) {
207 QModelIndex colIndex = model->index(row, col, index);
208
209 if (colIndex == m_editingIndex) {
210 itemActivated(m_editingIndex); // this will *close* the item's editor because it's already open
211 }
212 }
213 }
214}
215
216// slot
217void KShortcutsEditorDelegate::hiddenBySearchLine(QTreeWidgetItem *item, bool hidden)
218{
219 if (!hidden || !item) {
220 return;
221 }
222 QTreeWidget *view = static_cast<QTreeWidget *>(parent());
223 QTreeWidgetItem *editingItem = KShortcutsEditorPrivate::itemFromIndex(view, m_editingIndex);
224 if (editingItem == item) {
225 itemActivated(m_editingIndex); // this will *close* the item's editor because it's already open
226 }
227}
228
229bool KShortcutsEditorDelegate::eventFilter(QObject *o, QEvent *e)
230{
231 if (o == m_editor) {
232 // Prevent clicks in the empty part of the editor widget from closing the editor
233 // because they would propagate to the itemview and be interpreted as a click in
234 // an item's rect. That in turn would lead to an itemActivated() call, closing
235 // the current editor.
236
237 switch (e->type()) {
241 return true;
242 default:
243 return false;
244 }
245 } else if (o == parent()) {
246 // Make left/right cursor keys switch items instead of operate the scroll bar
247 // (subclassing QtreeView/Widget would be cleaner but much more of a hassle)
248 // Note that in our case we know that the selection mode is SingleSelection,
249 // so we don't have to ask QAbstractItemView::selectionCommand() et al.
250
251 if (e->type() != QEvent::KeyPress) {
252 return false;
253 }
254 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
255 QTreeWidget *view = static_cast<QTreeWidget *>(parent());
256 QItemSelectionModel *selection = view->selectionModel();
257 QModelIndex index = selection->currentIndex();
258
259 switch (ke->key()) {
260 case Qt::Key_Space:
261 case Qt::Key_Select:
262 // we are not using the standard "open editor" mechanism of QAbstractItemView,
263 // so let's emulate that here.
264 itemActivated(index);
265 return true;
266 case Qt::Key_Left:
267 index = index.sibling(index.row(), index.column() - 1);
268 break;
269 case Qt::Key_Right:
270 index = index.sibling(index.row(), index.column() + 1);
271 break;
272 default:
273 return false;
274 }
275 // a cursor key was pressed
276 if (index.isValid()) {
278 //### using PositionAtCenter for now;
279 // EnsureVisible has no effect which seems to be a bug.
281 }
282 return true;
283 }
284 return false;
285}
286
287// slot
288void KShortcutsEditorDelegate::keySequenceChanged(const QKeySequence &seq)
289{
290 QVariant ret = QVariant::fromValue(seq);
291 Q_EMIT shortcutChanged(ret, m_editingIndex);
292}
293
294void KShortcutsEditorDelegate::setCheckActionCollections(const QList<KActionCollection *> &checkActionCollections)
295{
296 m_checkActionCollections = checkActionCollections;
297}
298
299#if 0
300//slot
301void KShortcutsEditorDelegate::shapeGestureChanged(const KShapeGesture &gest)
302{
303 //this is somewhat verbose because the gesture types are not "built in" to QVariant
304 QVariant ret = QVariant::fromValue(gest);
305 Q_EMIT shortcutChanged(ret, m_editingIndex);
306}
307#endif
308
309#if 0
310//slot
311void KShortcutsEditorDelegate::rockerGestureChanged(const KRockerGesture &gest)
312{
313 QVariant ret = QVariant::fromValue(gest);
314 Q_EMIT shortcutChanged(ret, m_editingIndex);
315}
316#endif
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
@ GlobalShortcuts
Check against global shortcuts.
@ StandardShortcuts
Check against standard shortcuts.
@ LocalShortcuts
Check with local shortcuts.
const QList< QKeySequence > & cut()
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
void clicked(const QModelIndex &index)
QItemSelectionModel * selectionModel() const const
QList< QKeySequence > shortcuts() const const
QStyle * style()
MouseButtonPress
Type type() const const
bool isRightToLeft()
bool isSectionHidden(int logicalIndex) const const
QModelIndex currentIndex() const const
virtual void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
virtual void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
int key() const const
SequenceMatch matches(const QKeySequence &seq) const const
const_reference at(qsizetype i) const const
bool isEmpty() const const
qsizetype size() const const
int column() const const
QVariant data(int role) const const
bool isValid() const const
const QAbstractItemModel * model() const const
int row() const const
QModelIndex sibling(int row, int column) const const
void installEventFilter(QObject *filterObj)
QVariant property(const char *name) const const
qreal devicePixelRatioF() const const
bool isEmpty() const const
PE_IndicatorArrowLeft
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
transparent
Key_Space
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void collapsed(const QModelIndex &index)
QHeaderView * header() const const
virtual void scrollTo(const QModelIndex &index, ScrollHint hint) override
QVariant fromValue(T &&value)
bool toBool() const const
QString toString() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:19:32 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.