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
15#include "config-xmlgui.h"
16
17#include "kshortcutseditor.h"
18
19// The following is needed for KShortcutsEditorPrivate
20#include "debug.h"
21#include "kshortcutsdialog_p.h"
22
23#include <QAction>
24#include <QHeaderView>
25#include <QList>
26#include <QObject>
27#include <QPrintDialog>
28#include <QPrinter>
29#include <QTextCursor>
30#include <QTextDocument>
31#include <QTextTable>
32#include <QTextTableFormat>
33#include <QTimer>
34
35#include <KConfig>
36#include <KConfigGroup>
37#if HAVE_GLOBALACCEL
38#include <KGlobalAccel>
39#endif
40#include "kactioncategory.h"
41#include "kactioncollection.h"
42#include <KTreeWidgetSearchLine>
43
44//---------------------------------------------------------------------
45// KShortcutsEditor
46//---------------------------------------------------------------------
47
48KShortcutsEditor::KShortcutsEditor(KActionCollection *collection, QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts)
49 : QWidget(parent)
50 , d(new KShortcutsEditorPrivate(this))
51{
52 d->initGUI(actionTypes: actionType, allowLetterShortcuts);
53 addCollection(collection);
54}
55
56KShortcutsEditor::KShortcutsEditor(QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts)
57 : QWidget(parent)
58 , d(new KShortcutsEditorPrivate(this))
59{
60 d->initGUI(actionTypes: actionType, allowLetterShortcuts);
61}
62
63KShortcutsEditor::~KShortcutsEditor()
64{
65 // reset all pending changes
66 undo();
67}
68
69bool KShortcutsEditor::isModified() const
70{
71 // Iterate over all items
72 QTreeWidgetItemIterator it(d->ui.list, QTreeWidgetItemIterator::NoChildren);
73
74 for (; (*it); ++it) {
75 KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it);
76 if (item && item->isModified()) {
77 return true;
78 }
79 }
80 return false;
81}
82
83void KShortcutsEditor::clearCollections()
84{
85 d->delegate->contractAll();
86 d->ui.list->clear();
87 d->actionCollections.clear();
88 QTimer::singleShot(interval: 0, receiver: this, slot: &KShortcutsEditor::resizeColumns);
89}
90
91void KShortcutsEditor::addCollection(KActionCollection *collection, const QString &title)
92{
93 // KXmlGui add action collections unconditionally. If some plugin doesn't
94 // provide actions we don't want to create empty subgroups.
95 if (collection->isEmpty()) {
96 return;
97 }
98
99 // We add a bunch of items. Prevent the treewidget from permanently
100 // updating.
101 setUpdatesEnabled(false);
102
103 d->actionCollections.append(t: collection);
104 // Forward our actionCollections to the delegate which does the conflict
105 // checking.
106 d->delegate->setCheckActionCollections(d->actionCollections);
107 QString displayTitle = title;
108 if (displayTitle.isEmpty()) {
109 // Use the programName (Translated).
110 displayTitle = collection->componentDisplayName();
111 }
112
113 KShortcutsEditorPrivate::HierarchyInfo hierarchy;
114 hierarchy.root = d->ui.list->invisibleRootItem();
115 hierarchy.program = d->findOrMakeItem(parent: hierarchy.root, name: displayTitle);
116 hierarchy.action = nullptr;
117
118 // Set to remember which actions we have seen.
119 QSet<QAction *> actionsSeen;
120
121 // Add all categories in their own subtree below the collections root node
122 const QList<KActionCategory *> categories = collection->findChildren<KActionCategory *>();
123 for (KActionCategory *category : categories) {
124 hierarchy.action = d->findOrMakeItem(parent: hierarchy.program, name: category->text());
125 const auto categoryActions = category->actions();
126 for (QAction *action : categoryActions) {
127 // Set a marker that we have seen this action
128 actionsSeen.insert(value: action);
129 d->addAction(action, parent: hierarchy.action);
130 }
131 }
132
133 // The rest of the shortcuts are added as direct children of the action
134 // collections root node
135 const auto collectionActions = collection->actions();
136 for (QAction *action : collectionActions) {
137 if (actionsSeen.contains(value: action)) {
138 continue;
139 }
140
141 d->addAction(action, parent: hierarchy.program);
142 }
143
144 // sort the list
145 d->ui.list->sortItems(column: Name, order: Qt::AscendingOrder);
146
147 // Hide Global shortcuts columns if there are no global shortcuts
148 d->setGlobalColumnsHidden(!d->m_hasAnyGlobalShortcuts);
149
150 // ...And hide local shortcuts columns if there are no local shortcuts
151 d->setLocalColumnsHidden(!d->m_hasAnyLocalShortcuts);
152
153 // re-enable updating
154 setUpdatesEnabled(true);
155
156 QTimer::singleShot(interval: 0, receiver: this, slot: &KShortcutsEditor::resizeColumns);
157}
158
159void KShortcutsEditor::importConfiguration(KConfigBase *config)
160{
161 d->importConfiguration(config);
162}
163
164void KShortcutsEditor::exportConfiguration(KConfigBase *config) const
165{
166 Q_ASSERT(config);
167 if (!config) {
168 return;
169 }
170
171 if (d->actionTypes & KShortcutsEditor::GlobalAction) {
172 QString groupName(QStringLiteral("Global Shortcuts"));
173 KConfigGroup group(config, groupName);
174 for (KActionCollection *collection : std::as_const(t&: d->actionCollections)) {
175 collection->exportGlobalShortcuts(config: &group, writeDefaults: true);
176 }
177 }
178 if (d->actionTypes & ~KShortcutsEditor::GlobalAction) {
179 QString groupName(QStringLiteral("Shortcuts"));
180 KConfigGroup group(config, groupName);
181 for (KActionCollection *collection : std::as_const(t&: d->actionCollections)) {
182 collection->writeSettings(config: &group, writeDefaults: true);
183 }
184 }
185}
186
187void KShortcutsEditor::writeConfiguration(KConfigGroup *config) const
188{
189 for (KActionCollection *collection : std::as_const(t&: d->actionCollections)) {
190 collection->writeSettings(config);
191 }
192}
193
194// slot
195void KShortcutsEditor::resizeColumns()
196{
197 // skip Name column as its section resize mode will take care of resizing to contents
198 for (int i = Name + 1; i < d->ui.list->columnCount(); i++) {
199 if (d->ui.list->isColumnHidden(column: i)) {
200 continue;
201 }
202 d->ui.list->resizeColumnToContents(column: i);
203 }
204}
205
206void KShortcutsEditor::save()
207{
208 writeConfiguration();
209 // we have to call commit. If we wouldn't do that the changes would be
210 // undone on deletion! That would lead to weird problems. Changes to
211 // Global Shortcuts would vanish completely. Changes to local shortcuts
212 // would vanish for this session.
213 for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
214 if (KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it)) {
215 item->commit();
216 }
217 }
218}
219
220void KShortcutsEditor::undo()
221{
222 // This function used to crash sometimes when invoked by clicking on "cancel"
223 // with Qt 4.2.something. Apparently items were deleted too early by Qt.
224 // It seems to work with 4.3-ish Qt versions. Keep an eye on this.
225 for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
226 if (KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it)) {
227 item->undo();
228 }
229 }
230}
231
232// We ask the user here if there are any conflicts, as opposed to undo().
233// They don't do the same thing anyway, this just not to confuse any readers.
234// slot
235void KShortcutsEditor::allDefault()
236{
237 d->allDefault();
238}
239
240void KShortcutsEditor::printShortcuts() const
241{
242 d->printShortcuts();
243}
244
245KShortcutsEditor::ActionTypes KShortcutsEditor::actionTypes() const
246{
247 return d->actionTypes;
248}
249
250void KShortcutsEditor::setActionTypes(ActionTypes actionTypes)
251{
252 d->setActionTypes(actionTypes);
253}
254
255//---------------------------------------------------------------------
256// KShortcutsEditorPrivate
257//---------------------------------------------------------------------
258
259KShortcutsEditorPrivate::KShortcutsEditorPrivate(KShortcutsEditor *qq)
260 : q(qq)
261 , delegate(nullptr)
262{
263}
264
265void KShortcutsEditorPrivate::initGUI(KShortcutsEditor::ActionTypes types, KShortcutsEditor::LetterShortcuts allowLetterShortcuts)
266{
267 actionTypes = types;
268
269 ui.setupUi(q);
270 q->layout()->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
271 ui.searchFilter->searchLine()->setTreeWidget(ui.list); // Plug into search line
272 ui.list->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
273 ui.list->header()->hideSection(alogicalIndex: ShapeGesture); // mouse gestures didn't make it in time...
274 ui.list->header()->hideSection(alogicalIndex: RockerGesture);
275
276#if HAVE_GLOBALACCEL
277 const bool hideGlobals = !(actionTypes & KShortcutsEditor::GlobalAction);
278#else
279 const bool hideGlobals = true;
280#endif
281
282 if (hideGlobals) {
283 setGlobalColumnsHidden(true);
284 } else if (!(actionTypes & ~KShortcutsEditor::GlobalAction)) {
285 setLocalColumnsHidden(true);
286 }
287
288 // Create the Delegate. It is responsible for the KKeySeqeunceWidgets that
289 // really change the shortcuts.
290 delegate = new KShortcutsEditorDelegate(ui.list, allowLetterShortcuts == KShortcutsEditor::LetterShortcutsAllowed);
291
292 ui.list->setItemDelegate(delegate);
293 ui.list->setSelectionBehavior(QAbstractItemView::SelectItems);
294 ui.list->setSelectionMode(QAbstractItemView::SingleSelection);
295 // we have our own editing mechanism
296 ui.list->setEditTriggers(QAbstractItemView::NoEditTriggers);
297 ui.list->setAlternatingRowColors(true);
298
299 // TODO listen to changes to global shortcuts
300 QObject::connect(sender: delegate, signal: &KShortcutsEditorDelegate::shortcutChanged, context: q, slot: [this](const QVariant &newShortcut, const QModelIndex &index) {
301 capturedShortcut(newShortcut, index);
302 });
303 // hide the editor widget chen its item becomes hidden
304 QObject::connect(sender: ui.searchFilter->searchLine(), signal: &KTreeWidgetSearchLine::hiddenChanged, context: delegate, slot: &KShortcutsEditorDelegate::hiddenBySearchLine);
305
306 ui.searchFilter->setFocus();
307}
308
309void KShortcutsEditorPrivate::setGlobalColumnsHidden(bool hide)
310{
311 QHeaderView *header = ui.list->header();
312 header->setSectionHidden(logicalIndex: GlobalPrimary, hide);
313 header->setSectionHidden(logicalIndex: GlobalAlternate, hide);
314}
315
316void KShortcutsEditorPrivate::setLocalColumnsHidden(bool hide)
317{
318 QHeaderView *header = ui.list->header();
319 header->setSectionHidden(logicalIndex: LocalPrimary, hide);
320 header->setSectionHidden(logicalIndex: LocalAlternate, hide);
321}
322
323void KShortcutsEditorPrivate::setActionTypes(KShortcutsEditor::ActionTypes types)
324{
325 if (actionTypes == types) {
326 return;
327 }
328 actionTypes = types;
329
330 // Show/hide columns based on the newly set action types
331 setGlobalColumnsHidden(!(actionTypes & KShortcutsEditor::GlobalAction));
332 setLocalColumnsHidden(!(actionTypes & ~KShortcutsEditor::GlobalAction));
333}
334
335bool KShortcutsEditorPrivate::addAction(QAction *action, QTreeWidgetItem *parent)
336{
337 // If the action name starts with unnamed- spit out a warning and ignore
338 // it. That name will change at will and will break loading and writing
339 QString actionName = action->objectName();
340 if (actionName.isEmpty() || actionName.startsWith(s: QLatin1String("unnamed-"))) {
341 qCCritical(DEBUG_KXMLGUI) << "Skipping action without name " << action->text() << "," << actionName << "!";
342 return false;
343 }
344
345 const QVariant value = action->property(name: "isShortcutConfigurable");
346 if (!value.isValid() || value.toBool()) {
347 new KShortcutsEditorItem(parent, action);
348
349#if HAVE_GLOBALACCEL
350 if (!m_hasAnyGlobalShortcuts) { // If one global action was found, skip
351 m_hasAnyGlobalShortcuts = KGlobalAccel::self()->hasShortcut(action);
352 }
353
354 if (!m_hasAnyLocalShortcuts) { // If one local action was found, skip
355 m_hasAnyLocalShortcuts = !KGlobalAccel::self()->hasShortcut(action);
356 }
357#else
358 m_hasAnyLocalShortcuts = true;
359#endif
360
361 return true;
362 }
363
364 return false;
365}
366
367void KShortcutsEditorPrivate::allDefault()
368{
369 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
370 if (!(*it)->parent() || (*it)->type() != ActionItem) {
371 continue;
372 }
373
374 KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
375 QAction *act = item->m_action;
376
377 QList<QKeySequence> defaultShortcuts = act->property(name: "defaultShortcuts").value<QList<QKeySequence>>();
378 if (act->shortcuts() != defaultShortcuts) {
379 QKeySequence primary = defaultShortcuts.isEmpty() ? QKeySequence() : defaultShortcuts.at(i: 0);
380 QKeySequence alternate = defaultShortcuts.size() <= 1 ? QKeySequence() : defaultShortcuts.at(i: 1);
381 changeKeyShortcut(item, column: LocalPrimary, capture: primary);
382 changeKeyShortcut(item, column: LocalAlternate, capture: alternate);
383 }
384
385#if HAVE_GLOBALACCEL
386 if (KGlobalAccel::self()->shortcut(action: act) != KGlobalAccel::self()->defaultShortcut(action: act)) {
387 QList<QKeySequence> defaultShortcut = KGlobalAccel::self()->defaultShortcut(action: act);
388 changeKeyShortcut(item, column: GlobalPrimary, capture: primarySequence(sequences: defaultShortcut));
389 changeKeyShortcut(item, column: GlobalAlternate, capture: alternateSequence(sequences: defaultShortcut));
390 }
391#endif
392 }
393}
394
395// static
396KShortcutsEditorItem *KShortcutsEditorPrivate::itemFromIndex(QTreeWidget *const w, const QModelIndex &index)
397{
398 QTreeWidgetItem *item = w->itemFromIndex(index);
399 if (item && item->type() == ActionItem) {
400 return static_cast<KShortcutsEditorItem *>(item);
401 }
402 return nullptr;
403}
404
405QTreeWidgetItem *KShortcutsEditorPrivate::findOrMakeItem(QTreeWidgetItem *parent, const QString &name)
406{
407 for (int i = 0; i < parent->childCount(); i++) {
408 QTreeWidgetItem *child = parent->child(index: i);
409 if (child->text(column: 0) == name) {
410 return child;
411 }
412 }
413 QTreeWidgetItem *ret = new QTreeWidgetItem(parent, NonActionItem);
414 ret->setText(column: 0, atext: name);
415 ui.list->expandItem(item: ret);
416 ret->setFlags(ret->flags() & ~Qt::ItemIsSelectable);
417 return ret;
418}
419
420// private slot
421void KShortcutsEditorPrivate::capturedShortcut(const QVariant &newShortcut, const QModelIndex &index)
422{
423 // dispatch to the right handler
424 if (!index.isValid()) {
425 return;
426 }
427 int column = index.column();
428 KShortcutsEditorItem *item = itemFromIndex(w: ui.list, index);
429 Q_ASSERT(item);
430
431 if (column >= LocalPrimary && column <= GlobalAlternate) {
432 changeKeyShortcut(item, column, capture: newShortcut.value<QKeySequence>());
433 }
434}
435
436void KShortcutsEditorPrivate::changeKeyShortcut(KShortcutsEditorItem *item, uint column, const QKeySequence &capture)
437{
438 // The keySequence we get is cleared by KKeySequenceWidget. No conflicts.
439 if (capture == item->keySequence(column)) {
440 return;
441 }
442
443 item->setKeySequence(column, seq: capture);
444 Q_EMIT q->keyChange();
445 // force view update
446 item->setText(column, atext: capture.toString(format: QKeySequence::NativeText));
447}
448
449void KShortcutsEditorPrivate::importConfiguration(KConfigBase *config)
450{
451 Q_ASSERT(config);
452 if (!config) {
453 return;
454 }
455
456 KConfigGroup globalShortcutsGroup(config, QStringLiteral("Global Shortcuts"));
457 if ((actionTypes & KShortcutsEditor::GlobalAction) && globalShortcutsGroup.exists()) {
458 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
459 if (!(*it)->parent()) {
460 continue;
461 }
462
463 KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
464 const QString actionId = item->data(column: Id).toString();
465 if (!globalShortcutsGroup.hasKey(key: actionId)) {
466 continue;
467 }
468
469 QList<QKeySequence> sc = QKeySequence::listFromString(str: globalShortcutsGroup.readEntry(key: actionId, aDefault: QString()));
470 changeKeyShortcut(item, column: GlobalPrimary, capture: primarySequence(sequences: sc));
471 changeKeyShortcut(item, column: GlobalAlternate, capture: alternateSequence(sequences: sc));
472 }
473 }
474
475 if (actionTypes & ~KShortcutsEditor::GlobalAction) {
476 const KConfigGroup localShortcutsGroup(config, QStringLiteral("Shortcuts"));
477 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
478 if (!(*it)->parent()) {
479 continue;
480 }
481 KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
482 const QString actionId = item->data(column: Id).toString();
483 if (!localShortcutsGroup.hasKey(key: actionId)) {
484 continue;
485 }
486
487 QList<QKeySequence> sc = QKeySequence::listFromString(str: localShortcutsGroup.readEntry(key: actionId, aDefault: QString()));
488 changeKeyShortcut(item, column: LocalPrimary, capture: primarySequence(sequences: sc));
489 changeKeyShortcut(item, column: LocalAlternate, capture: alternateSequence(sequences: sc));
490 }
491 }
492}
493
494/*TODO for the printShortcuts function
495Nice to have features (which I'm not sure I can do before may due to
496more important things):
497
498- adjust the general page borders, IMHO they're too wide
499
500- add a custom printer options page that allows to filter out all
501 actions that don't have a shortcut set to reduce this list. IMHO this
502 should be optional as people might want to simply print all and when
503 they find a new action that they assign a shortcut they can simply use
504 a pen to fill out the empty space
505
506- find a way to align the Main/Alternate/Global entries in the shortcuts
507 column without adding borders. I first did this without a nested table
508 but instead simply added 3 rows and merged the 3 cells in the Action
509 name and description column, but unfortunately I didn't find a way to
510 remove the borders between the 6 shortcut cells.
511*/
512void KShortcutsEditorPrivate::printShortcuts() const
513{
514// One can't print on wince
515#ifndef _WIN32_WCE
516 QTreeWidgetItem *root = ui.list->invisibleRootItem();
517 QTextDocument doc;
518
519 doc.setDefaultFont(QFontDatabase::systemFont(type: QFontDatabase::GeneralFont));
520
521 QTextCursor cursor(&doc);
522 cursor.beginEditBlock();
523 QTextCharFormat headerFormat;
524 headerFormat.setProperty(propertyId: QTextFormat::FontSizeAdjustment, value: 3);
525 headerFormat.setFontWeight(QFont::Bold);
526 cursor.insertText(i18nc("header for an applications shortcut list", "Shortcuts for %1", QGuiApplication::applicationDisplayName()), format: headerFormat);
527 QTextCharFormat componentFormat;
528 componentFormat.setProperty(propertyId: QTextFormat::FontSizeAdjustment, value: 2);
529 componentFormat.setFontWeight(QFont::Bold);
530 QTextBlockFormat componentBlockFormat = cursor.blockFormat();
531 componentBlockFormat.setTopMargin(16);
532 componentBlockFormat.setBottomMargin(16);
533
534 QTextTableFormat tableformat;
535 tableformat.setHeaderRowCount(1);
536 tableformat.setCellPadding(4.0);
537 tableformat.setCellSpacing(0);
538 tableformat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
539 tableformat.setBorder(0.5);
540
541 struct ColumnInfo {
542 QString title;
543 ColumnDesignation column;
544 };
545 const ColumnInfo shortcutTitleToColumnMap[] = {
546 {i18n("Main:"), .column: LocalPrimary},
547 {i18n("Alternate:"), .column: LocalAlternate},
548 {i18n("Global:"), .column: GlobalPrimary},
549 {i18n("Global alternate:"), .column: GlobalAlternate},
550 };
551
552 for (int i = 0; i < root->childCount(); i++) {
553 QTreeWidgetItem *item = root->child(index: i);
554 cursor.insertBlock(format: componentBlockFormat, charFormat: componentFormat);
555 cursor.insertText(text: item->text(column: 0));
556
557 QTextTable *table = cursor.insertTable(rows: 1, cols: 3);
558 table->setFormat(tableformat);
559 int currow = 0;
560
561 QTextTableCell cell = table->cellAt(row: currow, col: 0);
562 QTextCharFormat format = cell.format();
563 format.setFontWeight(QFont::Bold);
564 cell.setFormat(format);
565 cell.firstCursorPosition().insertText(i18n("Action Name"));
566
567 cell = table->cellAt(row: currow, col: 1);
568 cell.setFormat(format);
569 cell.firstCursorPosition().insertText(i18n("Shortcuts"));
570
571 cell = table->cellAt(row: currow, col: 2);
572 cell.setFormat(format);
573 cell.firstCursorPosition().insertText(i18n("Description"));
574 currow++;
575
576 for (QTreeWidgetItemIterator it(item); *it; ++it) {
577 if ((*it)->type() != ActionItem) {
578 continue;
579 }
580
581 KShortcutsEditorItem *editoritem = static_cast<KShortcutsEditorItem *>(*it);
582 table->insertRows(pos: table->rows(), num: 1);
583 QVariant data = editoritem->data(column: Name, role: Qt::DisplayRole);
584 table->cellAt(row: currow, col: 0).firstCursorPosition().insertText(text: data.toString());
585
586 QTextTable *shortcutTable = nullptr;
587 for (const auto &[title, column] : shortcutTitleToColumnMap) {
588 data = editoritem->data(column, role: Qt::DisplayRole);
589 QString key = data.value<QKeySequence>().toString();
590
591 if (!key.isEmpty()) {
592 if (!shortcutTable) {
593 shortcutTable = table->cellAt(row: currow, col: 1).firstCursorPosition().insertTable(rows: 1, cols: 2);
594 QTextTableFormat shortcutTableFormat = tableformat;
595 shortcutTableFormat.setCellSpacing(0.0);
596 shortcutTableFormat.setHeaderRowCount(0);
597 shortcutTableFormat.setBorder(0.0);
598 shortcutTable->setFormat(shortcutTableFormat);
599 } else {
600 shortcutTable->insertRows(pos: shortcutTable->rows(), num: 1);
601 }
602 shortcutTable->cellAt(row: shortcutTable->rows() - 1, col: 0).firstCursorPosition().insertText(text: title);
603 shortcutTable->cellAt(row: shortcutTable->rows() - 1, col: 1).firstCursorPosition().insertText(text: key);
604 }
605 }
606
607 QAction *action = editoritem->m_action;
608 cell = table->cellAt(row: currow, col: 2);
609 format = cell.format();
610 format.setProperty(propertyId: QTextFormat::FontSizeAdjustment, value: -1);
611 cell.setFormat(format);
612 cell.firstCursorPosition().insertHtml(html: action->whatsThis());
613
614 currow++;
615 }
616 cursor.movePosition(op: QTextCursor::End);
617 }
618 cursor.endEditBlock();
619
620 QPrinter printer;
621 QPrintDialog *dlg = new QPrintDialog(&printer, q);
622 if (dlg->exec() == QDialog::Accepted) {
623 doc.print(printer: &printer);
624 }
625 delete dlg;
626#endif
627}
628
629#include "moc_kshortcutseditor.cpp"
630

source code of kxmlgui/src/kshortcutseditor.cpp