File indexing completed on 2024-04-28 11:47:23

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
0004     SPDX-FileCopyrightText: 1997 Nicolas Hadacek <hadacek@kde.org>
0005     SPDX-FileCopyrightText: 1998 Matthias Ettrich <ettrich@kde.org>
0006     SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
0007     SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
0008     SPDX-FileCopyrightText: 2007 Roberto Raggi <roberto@kdevelop.org>
0009     SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
0010     SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
0011 
0012     SPDX-License-Identifier: LGPL-2.0-or-later
0013 */
0014 
0015 #include "config-xmlgui.h"
0016 
0017 #include "kshortcutseditor.h"
0018 
0019 // The following is needed for KShortcutsEditorPrivate and QTreeWidgetHack
0020 #include "debug.h"
0021 #include "kshortcutsdialog_p.h"
0022 
0023 #include <QAction>
0024 #include <QHeaderView>
0025 #include <QList>
0026 #include <QObject>
0027 #include <QPrintDialog>
0028 #include <QPrinter>
0029 #include <QTextCursor>
0030 #include <QTextDocument>
0031 #include <QTextTable>
0032 #include <QTextTableFormat>
0033 #include <QTimer>
0034 
0035 #include <KConfig>
0036 #include <KConfigGroup>
0037 #if HAVE_GLOBALACCEL
0038 #include <KGlobalAccel>
0039 #endif
0040 #include "kactioncategory.h"
0041 #include "kactioncollection.h"
0042 #include <KTreeWidgetSearchLine>
0043 
0044 //---------------------------------------------------------------------
0045 // KShortcutsEditor
0046 //---------------------------------------------------------------------
0047 
0048 KShortcutsEditor::KShortcutsEditor(KActionCollection *collection, QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts)
0049     : QWidget(parent)
0050     , d(new KShortcutsEditorPrivate(this))
0051 {
0052     d->initGUI(actionType, allowLetterShortcuts);
0053     addCollection(collection);
0054 }
0055 
0056 KShortcutsEditor::KShortcutsEditor(QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts)
0057     : QWidget(parent)
0058     , d(new KShortcutsEditorPrivate(this))
0059 {
0060     d->initGUI(actionType, allowLetterShortcuts);
0061 }
0062 
0063 KShortcutsEditor::~KShortcutsEditor() = default;
0064 
0065 bool KShortcutsEditor::isModified() const
0066 {
0067     // Iterate over all items
0068     QTreeWidgetItemIterator it(d->ui.list, QTreeWidgetItemIterator::NoChildren);
0069 
0070     for (; (*it); ++it) {
0071         KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it);
0072         if (item && item->isModified()) {
0073             return true;
0074         }
0075     }
0076     return false;
0077 }
0078 
0079 void KShortcutsEditor::clearCollections()
0080 {
0081     d->delegate->contractAll();
0082     d->ui.list->clear();
0083     d->actionCollections.clear();
0084     QTimer::singleShot(0, this, &KShortcutsEditor::resizeColumns);
0085 }
0086 
0087 void KShortcutsEditor::addCollection(KActionCollection *collection, const QString &title)
0088 {
0089     // KXmlGui add action collections unconditionally. If some plugin doesn't
0090     // provide actions we don't want to create empty subgroups.
0091     if (collection->isEmpty()) {
0092         return;
0093     }
0094 
0095     // We add a bunch of items. Prevent the treewidget from permanently
0096     // updating.
0097     setUpdatesEnabled(false);
0098 
0099     d->actionCollections.append(collection);
0100     // Forward our actionCollections to the delegate which does the conflict
0101     // checking.
0102     d->delegate->setCheckActionCollections(d->actionCollections);
0103     QString displayTitle = title;
0104     if (displayTitle.isEmpty()) {
0105         // Use the programName (Translated).
0106         displayTitle = collection->componentDisplayName();
0107     }
0108 
0109     KShortcutsEditorPrivate::HierarchyInfo hierarchy;
0110     hierarchy.root = d->ui.list->invisibleRootItem();
0111     hierarchy.program = d->findOrMakeItem(hierarchy.root, displayTitle);
0112     hierarchy.action = nullptr;
0113 
0114     // Set to remember which actions we have seen.
0115     QSet<QAction *> actionsSeen;
0116 
0117     // Add all categories in their own subtree below the collections root node
0118     const QList<KActionCategory *> categories = collection->findChildren<KActionCategory *>();
0119     for (KActionCategory *category : categories) {
0120         hierarchy.action = d->findOrMakeItem(hierarchy.program, category->text());
0121         const auto categoryActions = category->actions();
0122         for (QAction *action : categoryActions) {
0123             // Set a marker that we have seen this action
0124             actionsSeen.insert(action);
0125             d->addAction(action, hierarchy.action);
0126         }
0127     }
0128 
0129     // The rest of the shortcuts are added as direct children of the action
0130     // collections root node
0131     const auto collectionActions = collection->actions();
0132     for (QAction *action : collectionActions) {
0133         if (actionsSeen.contains(action)) {
0134             continue;
0135         }
0136 
0137         d->addAction(action, hierarchy.program);
0138     }
0139 
0140     // sort the list
0141     d->ui.list->sortItems(Name, Qt::AscendingOrder);
0142 
0143     // Hide Global shortcuts columns if there are no global shortcuts
0144     d->setGlobalColumnsHidden(!d->m_hasAnyGlobalShortcuts);
0145 
0146     // re-enable updating
0147     setUpdatesEnabled(true);
0148 
0149     QTimer::singleShot(0, this, &KShortcutsEditor::resizeColumns);
0150 }
0151 
0152 void KShortcutsEditor::clearConfiguration()
0153 {
0154     d->clearConfiguration();
0155 }
0156 
0157 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0158 void KShortcutsEditor::importConfiguration(KConfig *config)
0159 {
0160     d->importConfiguration(config);
0161 }
0162 #endif
0163 
0164 void KShortcutsEditor::importConfiguration(KConfigBase *config)
0165 {
0166     d->importConfiguration(config);
0167 }
0168 
0169 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 0)
0170 void KShortcutsEditor::exportConfiguration(KConfig *config) const
0171 {
0172     exportConfiguration(static_cast<KConfigBase *>(config));
0173 }
0174 #endif
0175 
0176 void KShortcutsEditor::exportConfiguration(KConfigBase *config) const
0177 {
0178     Q_ASSERT(config);
0179     if (!config) {
0180         return;
0181     }
0182 
0183     if (d->actionTypes & KShortcutsEditor::GlobalAction) {
0184         QString groupName(QStringLiteral("Global Shortcuts"));
0185         KConfigGroup group(config, groupName);
0186         for (KActionCollection *collection : std::as_const(d->actionCollections)) {
0187             collection->exportGlobalShortcuts(&group, true);
0188         }
0189     }
0190     if (d->actionTypes & ~KShortcutsEditor::GlobalAction) {
0191         QString groupName(QStringLiteral("Shortcuts"));
0192         KConfigGroup group(config, groupName);
0193         for (KActionCollection *collection : std::as_const(d->actionCollections)) {
0194             collection->writeSettings(&group, true);
0195         }
0196     }
0197 }
0198 
0199 void KShortcutsEditor::writeConfiguration(KConfigGroup *config) const
0200 {
0201     for (KActionCollection *collection : std::as_const(d->actionCollections)) {
0202         collection->writeSettings(config);
0203     }
0204 }
0205 
0206 // slot
0207 void KShortcutsEditor::resizeColumns()
0208 {
0209     for (int i = 0; i < d->ui.list->columnCount(); i++) {
0210         d->ui.list->resizeColumnToContents(i);
0211     }
0212 }
0213 
0214 void KShortcutsEditor::commit()
0215 {
0216     for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
0217         if (KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it)) {
0218             item->commit();
0219         }
0220     }
0221 }
0222 
0223 void KShortcutsEditor::save()
0224 {
0225     writeConfiguration();
0226     // we have to call commit. If we wouldn't do that the changes would be
0227     // undone on deletion! That would lead to weird problems. Changes to
0228     // Global Shortcuts would vanish completely. Changes to local shortcuts
0229     // would vanish for this session.
0230     commit();
0231 }
0232 
0233 #if KXMLGUI_BUILD_DEPRECATED_SINCE(5, 75)
0234 void KShortcutsEditor::undoChanges()
0235 {
0236     undo();
0237 }
0238 #endif
0239 
0240 void KShortcutsEditor::undo()
0241 {
0242     // This function used to crash sometimes when invoked by clicking on "cancel"
0243     // with Qt 4.2.something. Apparently items were deleted too early by Qt.
0244     // It seems to work with 4.3-ish Qt versions. Keep an eye on this.
0245     for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
0246         if (KShortcutsEditorItem *item = dynamic_cast<KShortcutsEditorItem *>(*it)) {
0247             item->undo();
0248         }
0249     }
0250 }
0251 
0252 // We ask the user here if there are any conflicts, as opposed to undo().
0253 // They don't do the same thing anyway, this just not to confuse any readers.
0254 // slot
0255 void KShortcutsEditor::allDefault()
0256 {
0257     d->allDefault();
0258 }
0259 
0260 void KShortcutsEditor::printShortcuts() const
0261 {
0262     d->printShortcuts();
0263 }
0264 
0265 KShortcutsEditor::ActionTypes KShortcutsEditor::actionTypes() const
0266 {
0267     return d->actionTypes;
0268 }
0269 
0270 void KShortcutsEditor::setActionTypes(ActionTypes actionTypes)
0271 {
0272     d->setActionTypes(actionTypes);
0273 }
0274 
0275 //---------------------------------------------------------------------
0276 // KShortcutsEditorPrivate
0277 //---------------------------------------------------------------------
0278 
0279 KShortcutsEditorPrivate::KShortcutsEditorPrivate(KShortcutsEditor *qq)
0280     : q(qq)
0281     , delegate(nullptr)
0282 {
0283 }
0284 
0285 void KShortcutsEditorPrivate::initGUI(KShortcutsEditor::ActionTypes types, KShortcutsEditor::LetterShortcuts allowLetterShortcuts)
0286 {
0287     actionTypes = types;
0288 
0289     ui.setupUi(q);
0290     q->layout()->setContentsMargins(0, 0, 0, 0);
0291     ui.searchFilter->searchLine()->setTreeWidget(ui.list); // Plug into search line
0292     ui.list->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
0293     ui.list->header()->hideSection(ShapeGesture); // mouse gestures didn't make it in time...
0294     ui.list->header()->hideSection(RockerGesture);
0295 
0296 #if HAVE_GLOBALACCEL
0297     const bool hideGlobals = !(actionTypes & KShortcutsEditor::GlobalAction);
0298 #else
0299     const bool hideGlobals = true;
0300 #endif
0301 
0302     if (hideGlobals) {
0303         setGlobalColumnsHidden(true);
0304     } else if (!(actionTypes & ~KShortcutsEditor::GlobalAction)) {
0305         setLocalColumnsHidden(true);
0306     }
0307 
0308     // Create the Delegate. It is responsible for the KKeySeqeunceWidgets that
0309     // really change the shortcuts.
0310     delegate = new KShortcutsEditorDelegate(ui.list, allowLetterShortcuts == KShortcutsEditor::LetterShortcutsAllowed);
0311 
0312     ui.list->setItemDelegate(delegate);
0313     ui.list->setSelectionBehavior(QAbstractItemView::SelectItems);
0314     ui.list->setSelectionMode(QAbstractItemView::SingleSelection);
0315     // we have our own editing mechanism
0316     ui.list->setEditTriggers(QAbstractItemView::NoEditTriggers);
0317     ui.list->setAlternatingRowColors(true);
0318 
0319     // TODO listen to changes to global shortcuts
0320     QObject::connect(delegate, &KShortcutsEditorDelegate::shortcutChanged, q, [this](const QVariant &newShortcut, const QModelIndex &index) {
0321         capturedShortcut(newShortcut, index);
0322     });
0323     // hide the editor widget chen its item becomes hidden
0324     QObject::connect(ui.searchFilter->searchLine(), &KTreeWidgetSearchLine::hiddenChanged, delegate, &KShortcutsEditorDelegate::hiddenBySearchLine);
0325 
0326     ui.searchFilter->setFocus();
0327 }
0328 
0329 void KShortcutsEditorPrivate::setGlobalColumnsHidden(bool hide)
0330 {
0331     QHeaderView *header = ui.list->header();
0332     header->setSectionHidden(GlobalPrimary, hide);
0333     header->setSectionHidden(GlobalAlternate, hide);
0334 }
0335 
0336 void KShortcutsEditorPrivate::setLocalColumnsHidden(bool hide)
0337 {
0338     QHeaderView *header = ui.list->header();
0339     header->setSectionHidden(LocalPrimary, hide);
0340     header->setSectionHidden(LocalAlternate, hide);
0341 }
0342 
0343 void KShortcutsEditorPrivate::setActionTypes(KShortcutsEditor::ActionTypes types)
0344 {
0345     if (actionTypes == types) {
0346         return;
0347     }
0348     actionTypes = types;
0349 
0350     // Show/hide columns based on the newly set action types
0351     setGlobalColumnsHidden(!(actionTypes & KShortcutsEditor::GlobalAction));
0352     setLocalColumnsHidden(!(actionTypes & ~KShortcutsEditor::GlobalAction));
0353 }
0354 
0355 bool KShortcutsEditorPrivate::addAction(QAction *action, QTreeWidgetItem *parent)
0356 {
0357     // If the action name starts with unnamed- spit out a warning and ignore
0358     // it. That name will change at will and will break loading and writing
0359     QString actionName = action->objectName();
0360     if (actionName.isEmpty() || actionName.startsWith(QLatin1String("unnamed-"))) {
0361         qCCritical(DEBUG_KXMLGUI) << "Skipping action without name " << action->text() << "," << actionName << "!";
0362         return false;
0363     }
0364 
0365     const QVariant value = action->property("isShortcutConfigurable");
0366     if (!value.isValid() || value.toBool()) {
0367         new KShortcutsEditorItem(parent, action);
0368 
0369 #if HAVE_GLOBALACCEL
0370         if (!m_hasAnyGlobalShortcuts) { // If one global action was found, skip
0371             m_hasAnyGlobalShortcuts = KGlobalAccel::self()->hasShortcut(action);
0372         }
0373 #endif
0374 
0375         return true;
0376     }
0377 
0378     return false;
0379 }
0380 
0381 void KShortcutsEditorPrivate::allDefault()
0382 {
0383     for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
0384         if (!(*it)->parent() || (*it)->type() != ActionItem) {
0385             continue;
0386         }
0387 
0388         KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
0389         QAction *act = item->m_action;
0390 
0391         QList<QKeySequence> defaultShortcuts = act->property("defaultShortcuts").value<QList<QKeySequence>>();
0392         if (act->shortcuts() != defaultShortcuts) {
0393             QKeySequence primary = defaultShortcuts.isEmpty() ? QKeySequence() : defaultShortcuts.at(0);
0394             QKeySequence alternate = defaultShortcuts.size() <= 1 ? QKeySequence() : defaultShortcuts.at(1);
0395             changeKeyShortcut(item, LocalPrimary, primary);
0396             changeKeyShortcut(item, LocalAlternate, alternate);
0397         }
0398 
0399 #if HAVE_GLOBALACCEL
0400         if (KGlobalAccel::self()->shortcut(act) != KGlobalAccel::self()->defaultShortcut(act)) {
0401             QList<QKeySequence> defaultShortcut = KGlobalAccel::self()->defaultShortcut(act);
0402             changeKeyShortcut(item, GlobalPrimary, primarySequence(defaultShortcut));
0403             changeKeyShortcut(item, GlobalAlternate, alternateSequence(defaultShortcut));
0404         }
0405 #endif
0406     }
0407 }
0408 
0409 // static
0410 KShortcutsEditorItem *KShortcutsEditorPrivate::itemFromIndex(QTreeWidget *const w, const QModelIndex &index)
0411 {
0412     QTreeWidgetItem *item = static_cast<QTreeWidgetHack *>(w)->itemFromIndex(index);
0413     if (item && item->type() == ActionItem) {
0414         return static_cast<KShortcutsEditorItem *>(item);
0415     }
0416     return nullptr;
0417 }
0418 
0419 QTreeWidgetItem *KShortcutsEditorPrivate::findOrMakeItem(QTreeWidgetItem *parent, const QString &name)
0420 {
0421     for (int i = 0; i < parent->childCount(); i++) {
0422         QTreeWidgetItem *child = parent->child(i);
0423         if (child->text(0) == name) {
0424             return child;
0425         }
0426     }
0427     QTreeWidgetItem *ret = new QTreeWidgetItem(parent, NonActionItem);
0428     ret->setText(0, name);
0429     ui.list->expandItem(ret);
0430     ret->setFlags(ret->flags() & ~Qt::ItemIsSelectable);
0431     return ret;
0432 }
0433 
0434 // private slot
0435 void KShortcutsEditorPrivate::capturedShortcut(const QVariant &newShortcut, const QModelIndex &index)
0436 {
0437     // dispatch to the right handler
0438     if (!index.isValid()) {
0439         return;
0440     }
0441     int column = index.column();
0442     KShortcutsEditorItem *item = itemFromIndex(ui.list, index);
0443     Q_ASSERT(item);
0444 
0445     if (column >= LocalPrimary && column <= GlobalAlternate) {
0446         changeKeyShortcut(item, column, newShortcut.value<QKeySequence>());
0447     }
0448 }
0449 
0450 void KShortcutsEditorPrivate::changeKeyShortcut(KShortcutsEditorItem *item, uint column, const QKeySequence &capture)
0451 {
0452     // The keySequence we get is cleared by KKeySequenceWidget. No conflicts.
0453     if (capture == item->keySequence(column)) {
0454         return;
0455     }
0456 
0457     item->setKeySequence(column, capture);
0458     Q_EMIT q->keyChange();
0459     // force view update
0460     item->setText(column, capture.toString(QKeySequence::NativeText));
0461 }
0462 
0463 void KShortcutsEditorPrivate::clearConfiguration()
0464 {
0465     for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
0466         if (!(*it)->parent()) {
0467             continue;
0468         }
0469 
0470         KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
0471 
0472         changeKeyShortcut(item, LocalPrimary, QKeySequence());
0473         changeKeyShortcut(item, LocalAlternate, QKeySequence());
0474 
0475         changeKeyShortcut(item, GlobalPrimary, QKeySequence());
0476         changeKeyShortcut(item, GlobalAlternate, QKeySequence());
0477     }
0478 }
0479 
0480 void KShortcutsEditorPrivate::importConfiguration(KConfigBase *config)
0481 {
0482     Q_ASSERT(config);
0483     if (!config) {
0484         return;
0485     }
0486 
0487     KConfigGroup globalShortcutsGroup(config, QStringLiteral("Global Shortcuts"));
0488     if ((actionTypes & KShortcutsEditor::GlobalAction) && globalShortcutsGroup.exists()) {
0489         for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
0490             if (!(*it)->parent()) {
0491                 continue;
0492             }
0493 
0494             KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
0495             const QString actionId = item->data(Id).toString();
0496             if (!globalShortcutsGroup.hasKey(actionId)) {
0497                 continue;
0498             }
0499 
0500             QList<QKeySequence> sc = QKeySequence::listFromString(globalShortcutsGroup.readEntry(actionId, QString()));
0501             changeKeyShortcut(item, GlobalPrimary, primarySequence(sc));
0502             changeKeyShortcut(item, GlobalAlternate, alternateSequence(sc));
0503         }
0504     }
0505 
0506     if (actionTypes & ~KShortcutsEditor::GlobalAction) {
0507         const KConfigGroup localShortcutsGroup(config, QStringLiteral("Shortcuts"));
0508         for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
0509             if (!(*it)->parent()) {
0510                 continue;
0511             }
0512             KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
0513             const QString actionId = item->data(Id).toString();
0514             if (!localShortcutsGroup.hasKey(actionId)) {
0515                 continue;
0516             }
0517 
0518             QList<QKeySequence> sc = QKeySequence::listFromString(localShortcutsGroup.readEntry(actionId, QString()));
0519             changeKeyShortcut(item, LocalPrimary, primarySequence(sc));
0520             changeKeyShortcut(item, LocalAlternate, alternateSequence(sc));
0521         }
0522     }
0523 }
0524 
0525 /*TODO for the printShortcuts function
0526 Nice to have features (which I'm not sure I can do before may due to
0527 more important things):
0528 
0529 - adjust the general page borders, IMHO they're too wide
0530 
0531 - add a custom printer options page that allows to filter out all
0532   actions that don't have a shortcut set to reduce this list. IMHO this
0533   should be optional as people might want to simply print all and  when
0534   they find a new action that they assign a shortcut they can simply use
0535   a pen to fill out the empty space
0536 
0537 - find a way to align the Main/Alternate/Global entries in the shortcuts
0538   column without adding borders. I first did this without a nested table
0539   but instead simply added 3 rows and merged the 3 cells in the Action
0540   name and description column, but unfortunately I didn't find a way to
0541   remove the borders between the 6 shortcut cells.
0542 */
0543 void KShortcutsEditorPrivate::printShortcuts() const
0544 {
0545 // One can't print on wince
0546 #ifndef _WIN32_WCE
0547     QTreeWidgetItem *root = ui.list->invisibleRootItem();
0548     QTextDocument doc;
0549 
0550     doc.setDefaultFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
0551 
0552     QTextCursor cursor(&doc);
0553     cursor.beginEditBlock();
0554     QTextCharFormat headerFormat;
0555     headerFormat.setProperty(QTextFormat::FontSizeAdjustment, 3);
0556     headerFormat.setFontWeight(QFont::Bold);
0557     cursor.insertText(i18nc("header for an applications shortcut list", "Shortcuts for %1", QGuiApplication::applicationDisplayName()), headerFormat);
0558     QTextCharFormat componentFormat;
0559     componentFormat.setProperty(QTextFormat::FontSizeAdjustment, 2);
0560     componentFormat.setFontWeight(QFont::Bold);
0561     QTextBlockFormat componentBlockFormat = cursor.blockFormat();
0562     componentBlockFormat.setTopMargin(16);
0563     componentBlockFormat.setBottomMargin(16);
0564 
0565     QTextTableFormat tableformat;
0566     tableformat.setHeaderRowCount(1);
0567     tableformat.setCellPadding(4.0);
0568     tableformat.setCellSpacing(0);
0569     tableformat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
0570     tableformat.setBorder(0.5);
0571 
0572     struct ColumnInfo {
0573         QString title;
0574         ColumnDesignation column;
0575     };
0576     const ColumnInfo shortcutTitleToColumnMap[] = {
0577         {i18n("Main:"), LocalPrimary},
0578         {i18n("Alternate:"), LocalAlternate},
0579         {i18n("Global:"), GlobalPrimary},
0580         {i18n("Global alternate:"), GlobalAlternate},
0581     };
0582 
0583     for (int i = 0; i < root->childCount(); i++) {
0584         QTreeWidgetItem *item = root->child(i);
0585         cursor.insertBlock(componentBlockFormat, componentFormat);
0586         cursor.insertText(item->text(0));
0587 
0588         QTextTable *table = cursor.insertTable(1, 3);
0589         table->setFormat(tableformat);
0590         int currow = 0;
0591 
0592         QTextTableCell cell = table->cellAt(currow, 0);
0593         QTextCharFormat format = cell.format();
0594         format.setFontWeight(QFont::Bold);
0595         cell.setFormat(format);
0596         cell.firstCursorPosition().insertText(i18n("Action Name"));
0597 
0598         cell = table->cellAt(currow, 1);
0599         cell.setFormat(format);
0600         cell.firstCursorPosition().insertText(i18n("Shortcuts"));
0601 
0602         cell = table->cellAt(currow, 2);
0603         cell.setFormat(format);
0604         cell.firstCursorPosition().insertText(i18n("Description"));
0605         currow++;
0606 
0607         for (QTreeWidgetItemIterator it(item); *it; ++it) {
0608             if ((*it)->type() != ActionItem) {
0609                 continue;
0610             }
0611 
0612             KShortcutsEditorItem *editoritem = static_cast<KShortcutsEditorItem *>(*it);
0613             table->insertRows(table->rows(), 1);
0614             QVariant data = editoritem->data(Name, Qt::DisplayRole);
0615             table->cellAt(currow, 0).firstCursorPosition().insertText(data.toString());
0616 
0617             QTextTable *shortcutTable = nullptr;
0618             for (const auto &[title, column] : shortcutTitleToColumnMap) {
0619                 data = editoritem->data(column, Qt::DisplayRole);
0620                 QString key = data.value<QKeySequence>().toString();
0621 
0622                 if (!key.isEmpty()) {
0623                     if (!shortcutTable) {
0624                         shortcutTable = table->cellAt(currow, 1).firstCursorPosition().insertTable(1, 2);
0625                         QTextTableFormat shortcutTableFormat = tableformat;
0626                         shortcutTableFormat.setCellSpacing(0.0);
0627                         shortcutTableFormat.setHeaderRowCount(0);
0628                         shortcutTableFormat.setBorder(0.0);
0629                         shortcutTable->setFormat(shortcutTableFormat);
0630                     } else {
0631                         shortcutTable->insertRows(shortcutTable->rows(), 1);
0632                     }
0633                     shortcutTable->cellAt(shortcutTable->rows() - 1, 0).firstCursorPosition().insertText(title);
0634                     shortcutTable->cellAt(shortcutTable->rows() - 1, 1).firstCursorPosition().insertText(key);
0635                 }
0636             }
0637 
0638             QAction *action = editoritem->m_action;
0639             cell = table->cellAt(currow, 2);
0640             format = cell.format();
0641             format.setProperty(QTextFormat::FontSizeAdjustment, -1);
0642             cell.setFormat(format);
0643             cell.firstCursorPosition().insertHtml(action->whatsThis());
0644 
0645             currow++;
0646         }
0647         cursor.movePosition(QTextCursor::End);
0648     }
0649     cursor.endEditBlock();
0650 
0651     QPrinter printer;
0652     QPrintDialog *dlg = new QPrintDialog(&printer, q);
0653     if (dlg->exec() == QDialog::Accepted) {
0654         doc.print(&printer);
0655     }
0656     delete dlg;
0657 #endif
0658 }
0659 
0660 #include "moc_kshortcutseditor.cpp"