File indexing completed on 2024-04-14 03:57:11

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