diff --git a/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp b/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp index 1ede5ab066..c2c990e32b 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp +++ b/libs/widgetutils/xmlgui/KisShortcutsEditor.cpp @@ -1,311 +1,322 @@ /* This file is part of the KDE libraries Copyright (C) 1998 Mark Donohoe Copyright (C) 1997 Nicolas Hadacek Copyright (C) 1998 Matthias Ettrich Copyright (C) 2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz Copyright (C) 2008 Michael Jansen 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 "KisShortcutsEditor.h" #include "KisShortcutsEditor_p.h" #include "kshortcutschemeshelper_p.h" #include "config-xmlgui.h" #include "kis_action_registry.h" // The following is needed for KisShortcutsEditorPrivate and QTreeWidgetHack // #include "KisShortcutsDialog_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kactioncollection.h" #include "kactioncategory.h" #include //--------------------------------------------------------------------- // KisShortcutsEditor //--------------------------------------------------------------------- Q_DECLARE_METATYPE(KisShortcutsEditorItem *) KisShortcutsEditor::KisShortcutsEditor(QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts) : QWidget(parent) , d(new KisShortcutsEditorPrivate(this)) { d->initGUI(actionType, allowLetterShortcuts); } KisShortcutsEditor::~KisShortcutsEditor() { delete d; } bool KisShortcutsEditor::isModified() const { // Iterate over all items QTreeWidgetItemIterator it(d->ui.list, QTreeWidgetItemIterator::NoChildren); for (; (*it); ++it) { KisShortcutsEditorItem *item = dynamic_cast(*it); if (item && item->isModified()) { return true; } } return false; } void KisShortcutsEditor::clearCollections() { d->delegate->contractAll(); d->ui.list->clear(); d->actionCollections.clear(); QTimer::singleShot(0, this, SLOT(resizeColumns())); } void KisShortcutsEditor::clearSearch() { d->ui.searchFilter->searchLine()->clear(); } void KisShortcutsEditor::addCollection(KActionCollection *collection, const QString &title) { // KXmlGui add action collections unconditionally. If some plugin doesn't // provide actions we don't want to create empty subgroups. if (collection->isEmpty()) { return; } // Pause updating. setUpdatesEnabled(false); /** * Forward this actioncollection to the delegate which will do conflict checking. * This _replaces_ existing collections in the delegate. */ d->actionCollections.append(collection); d->delegate->setCheckActionCollections(d->actionCollections); // Determine how we should label this collection in the widget. QString collectionTitle; if (!title.isEmpty()) { collectionTitle = title; } else { // Use the programName (Translated). collectionTitle = collection->componentDisplayName(); } // Create the collection root node. QTreeWidgetItem *hierarchy[3]; hierarchy[KisShortcutsEditorPrivate::Root] = d->ui.list->invisibleRootItem(); hierarchy[KisShortcutsEditorPrivate::Program] = d->findOrMakeItem(hierarchy[KisShortcutsEditorPrivate::Root], collectionTitle); hierarchy[KisShortcutsEditorPrivate::Action] = 0; // Remember which actions we have seen. We will be adding categorized // actions first, so this will help us keep track of which actions haven't // been categorized yet, so we can add them as uncategorized at the end. QSet actionsSeen; // Add a subtree for each category? Perhaps easier to think that this // doesn't exist. Basically you add KActionCategory as a QObject child of // KActionCollection, and then tag objects as belonging to the category. foreach (KActionCategory *category, collection->categories()) { // Don't display empty categories. if (category->actions().isEmpty()) { continue; } hierarchy[KisShortcutsEditorPrivate::Action] = d->findOrMakeItem(hierarchy[KisShortcutsEditorPrivate::Program], category->text()); // Add every item from the category. foreach (QAction *action, category->actions()) { actionsSeen.insert(action); d->addAction(action, hierarchy, KisShortcutsEditorPrivate::Action); } // Fold in each KActionCategory by default. hierarchy[KisShortcutsEditorPrivate::Action]->setExpanded(false); } // Finally, tack on any uncategorized actions. foreach (QAction *action, collection->actions()) { if (!actionsSeen.contains(action)) { d->addAction(action, hierarchy, KisShortcutsEditorPrivate::Program); } } // sort the list d->ui.list->sortItems(Name, Qt::AscendingOrder); // Now turn on updating again. setUpdatesEnabled(true); QTimer::singleShot(0, this, SLOT(resizeColumns())); } void KisShortcutsEditor::clearConfiguration() { d->clearConfiguration(); } void KisShortcutsEditor::importConfiguration(KConfigBase *config, bool isScheme) { Q_ASSERT(config); if (!config) { return; } // If this is a shortcut scheme, apply it if (isScheme) { KisActionRegistry::instance()->applyShortcutScheme(config); } // Update the dialog entry items const KConfigGroup schemeShortcuts(config, QStringLiteral("Shortcuts")); for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (!(*it)->parent()) { continue; } KisShortcutsEditorItem *item = static_cast(*it); const QString actionId = item->data(Id).toString(); if (!schemeShortcuts.hasKey(actionId)) continue; QList sc = QKeySequence::listFromString(schemeShortcuts.readEntry(actionId, QString())); d->changeKeyShortcut(item, LocalPrimary, primarySequence(sc)); d->changeKeyShortcut(item, LocalAlternate, alternateSequence(sc)); } } void KisShortcutsEditor::exportConfiguration(KConfigBase *config) const { Q_ASSERT(config); if (!config) { return; } if (d->actionTypes) { QString groupName(QStringLiteral("Shortcuts")); KConfigGroup group(config, groupName); foreach (KActionCollection *collection, d->actionCollections) { collection->writeSettings(&group, true); } } KisActionRegistry::instance()->notifySettingsUpdated(); } void KisShortcutsEditor::saveShortcuts(KConfigGroup *config) const { // This is a horrible mess with pointers... KConfigGroup cg; if (config == 0) { cg = KConfigGroup(KSharedConfig::openConfig("kritashortcutsrc"), QStringLiteral("Shortcuts")); config = &cg; } // Clear and reset temporary shortcuts config->deleteGroup(); foreach (KActionCollection *collection, d->actionCollections) { collection->writeSettings(config, false); } KisActionRegistry::instance()->notifySettingsUpdated(); } //slot void KisShortcutsEditor::resizeColumns() { for (int i = 0; i < d->ui.list->columnCount(); i++) { d->ui.list->resizeColumnToContents(i); } } void KisShortcutsEditor::commit() { for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (KisShortcutsEditorItem *item = dynamic_cast(*it)) { item->commit(); } } } void KisShortcutsEditor::save() { saveShortcuts(); commit(); // Not doing this would be bad } void KisShortcutsEditor::undo() { // TODO: is this working? for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) { if (KisShortcutsEditorItem *item = dynamic_cast(*it)) { item->undo(); } } } void KisShortcutsEditor::allDefault() { d->allDefault(); } void KisShortcutsEditor::printShortcuts() const { d->printShortcuts(); } +void KisShortcutsEditor::searchUpdated(QString s) +{ + if (s.isEmpty()) { + // Reset the tree area + d->ui.list->collapseAll(); + d->ui.list->expandToDepth(0); + } else { + d->ui.list->expandAll(); + } +} + KisShortcutsEditor::ActionTypes KisShortcutsEditor::actionTypes() const { return d->actionTypes; } void KisShortcutsEditor::setActionTypes(ActionTypes actionTypes) { d->setActionTypes(actionTypes); } #include "moc_KisShortcutsEditor.cpp" diff --git a/libs/widgetutils/xmlgui/KisShortcutsEditor.h b/libs/widgetutils/xmlgui/KisShortcutsEditor.h index 9a2ebe9cac..b9e5c65666 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsEditor.h +++ b/libs/widgetutils/xmlgui/KisShortcutsEditor.h @@ -1,248 +1,253 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Nicolas Hadacek Copyright (C) 2001,2001 Ellis Whitehead Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Roberto Raggi Copyright (C) 2007 Andreas Hartmetz Copyright (C) 2008 Michael Jansen Copyright (c) 2015 Michael Abrahams 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. */ #ifndef KISSHORTCUTSEDITOR_H #define KISSHORTCUTSEDITOR_H #include #include class KActionCollection; class KConfig; class KConfigBase; class KConfigGroup; class KGlobalAccel; class KisShortcutsEditorPrivate; /** * WARNING: KisShortcutsEditor expects that the list of existing shortcuts is * already free of conflicts. If it is not, nothing will crash, but your users * won't like the resulting behavior. * * TODO: Find the right place to check for conflicts. */ /** * @short Widget for configuration of KAccel and KGlobalAccel. * * Configure dictionaries of key/action associations for KActions, * including global shortcuts. * * The class takes care of all aspects of configuration, including * handling key conflicts internally. Connect to the allDefault() * slot if you want to set all configurable shortcuts to their * default values. * * @see KShortcutsDialog * @author Nicolas Hadacek * @author Hamish Rodda (KDE 4 porting) * @author Michael Jansen */ class KRITAWIDGETUTILS_EXPORT KisShortcutsEditor : public QWidget { Q_OBJECT Q_PROPERTY(ActionTypes actionTypes READ actionTypes WRITE setActionTypes) public: /* * These attempt to build some sort of characterization of all actions. The * idea is to determine which sorts of actions will be configured in the * dialog. * * Enumerating all possible actions is a sorrowful, pitiable endeavor, * useless for Krita. We should do something about this. */ enum ActionType { /// Actions which are triggered by any keypress in a widget which has the action added to it WidgetAction = Qt::WidgetShortcut /*0*/, /// Actions which are triggered by any keypress in a window which has the action added to it or its child widget(s) WindowAction = Qt::WindowShortcut /*1*/, /// Actions which are triggered by any keypress in the application ApplicationAction = Qt::ApplicationShortcut /*2*/, /// Actions which are triggered by any keypress in the windowing system GlobalAction = 4, /// All actions AllActions = 0xffffffff }; Q_DECLARE_FLAGS(ActionTypes, ActionType) enum LetterShortcuts { /// Shortcuts without a modifier are not allowed, so 'A' would not be /// valid, whereas 'Ctrl+A' would be. This only applies to printable /// characters, however. 'F1', 'Insert' etc. could still be used. LetterShortcutsDisallowed = 0, /// Letter shortcuts are allowed LetterShortcutsAllowed }; /** * \overload * * Creates a key chooser without a starting action collection. * * @param parent parent widget * @param actionTypes types of actions to display in this widget. * @param allowLetterShortcuts set to LetterShortcutsDisallowed if unmodified alphanumeric * keys ('A', '1', etc.) are not permissible shortcuts. */ explicit KisShortcutsEditor(QWidget *parent, ActionTypes actionTypes = AllActions, LetterShortcuts allowLetterShortcuts = LetterShortcutsAllowed); /// Destructor virtual ~KisShortcutsEditor(); /** * @ret true if there are unsaved changes. */ bool isModified() const; /** * Removes all action collections from the editor */ void clearCollections(); /** * Clears search area */ void clearSearch(); /** * Note: the reason this is so damn complicated is because it's supposed to * support having multiple applications running inside of each other through * KisParts. That means we have to be able to separate sections within each * configuration file. * * Insert an action collection, i.e. add all its actions to the ones * already associated with the KisShortcutsEditor object. * * @param title subtree title of this collection of shortcut. */ void addCollection(KActionCollection *, const QString &title = QString()); /** * Undo all change made since the last commit(). */ void undo(); /** * Save the changes. * * Before saving the changes are committed. This saves the actions to disk. * Any KActionCollection objects with the xmlFile() value set will be * written to an XML file. All other will be written to the application's * rc file. */ void save(); /** * Update the dialog entries without saving. * * @since 4.2 */ void commit(); /** * Removes all configured shortcuts. */ void clearConfiguration(); /** * Write the current custom shortcut settings to the \p config object. * * @param config Config object to save to. Default is kritashortcutsrc. * */ void saveShortcuts(KConfigGroup *config = 0) const; /** * Write the current shortcuts to a new scheme to configuration file * * @param config Config object to save to. */ void exportConfiguration(KConfigBase *config) const; /** * Import a shortcut configuration file. * * @param config Config object to load from. * @param isScheme true for shortcut scheme, false for custom shortcuts */ void importConfiguration(KConfigBase *config, bool isScheme); /** * Sets the types of actions to display in this widget. * * @param actionTypes New types of actions * @since 5.0 */ void setActionTypes(ActionTypes actionTypes); /** * * @return The types of actions currently displayed in this widget. * @since 5.0 */ ActionTypes actionTypes() const; Q_SIGNALS: /** * Emitted when an action's shortcut has been changed. **/ void keyChange(); public Q_SLOTS: /** * Resize columns to width required */ void resizeColumns(); /** * Set all shortcuts to their default values (bindings). **/ void allDefault(); /** * Opens a printing dialog to print all the shortcuts */ void printShortcuts() const; + /** + * Expand or collapse the tree view when the search text changes + */ + void searchUpdated(QString s); + private: Q_PRIVATE_SLOT(d, void capturedShortcut(QVariant, const QModelIndex &)) private: friend class KisShortcutsDialog; friend class KisShortcutsEditorPrivate; KisShortcutsEditorPrivate *const d; Q_DISABLE_COPY(KisShortcutsEditor) }; Q_DECLARE_OPERATORS_FOR_FLAGS(KisShortcutsEditor::ActionTypes) #endif // KISSHORTCUTSEDITOR_H diff --git a/libs/widgetutils/xmlgui/KisShortcutsEditor_p.cpp b/libs/widgetutils/xmlgui/KisShortcutsEditor_p.cpp index 9a5ee7dd71..4996e9a331 100644 --- a/libs/widgetutils/xmlgui/KisShortcutsEditor_p.cpp +++ b/libs/widgetutils/xmlgui/KisShortcutsEditor_p.cpp @@ -1,343 +1,346 @@ /** * Copyright (C) 1997 Nicolas Hadacek * Copyright (C) 1998 Matthias Ettrich * Copyright (C) 2001 Ellis Whitehead * Copyright (C) 2006 Hamish Rodda * Copyright (C) 2007 Roberto Raggi * Copyright (C) 2007 Andreas Hartmetz * Copyright (C) 2008 Michael Jansen * Copyright (c) 2015 Michael Abrahams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisShortcutsEditor.h" #include "KisShortcutsEditor_p.h" #include #include #include #include #include #include #include #include #include #include "kis_action_registry.h" //--------------------------------------------------------------------- // KisShortcutsEditorPrivate //--------------------------------------------------------------------- namespace { KisShortcutsEditorItem *itemFromIndex(QTreeWidget *const w, const QModelIndex &index) { QTreeWidgetItem *item = static_cast(w)->itemFromIndex(index); if (item && item->type() == ActionItem) { return static_cast(item); } return 0; } } KisShortcutsEditorPrivate::KisShortcutsEditorPrivate(KisShortcutsEditor *q) : q(q), delegate(0) {} void KisShortcutsEditorPrivate::initGUI(KisShortcutsEditor::ActionTypes types, KisShortcutsEditor::LetterShortcuts allowLetterShortcuts) { actionTypes = types; ui.setupUi(q); q->layout()->setMargin(0); ui.searchFilter->searchLine()->setTreeWidget(ui.list); // Plug into search line ui.list->header()->setSectionResizeMode(QHeaderView::ResizeToContents); // Create the Delegate. It is responsible for the KKeySeqeunceWidgets that // really change the shortcuts. delegate = new KisShortcutsEditorDelegate( ui.list, allowLetterShortcuts == KisShortcutsEditor::LetterShortcutsAllowed); ui.list->setItemDelegate(delegate); ui.list->setSelectionBehavior(QAbstractItemView::SelectItems); ui.list->setSelectionMode(QAbstractItemView::SingleSelection); //we have our own editing mechanism ui.list->setEditTriggers(QAbstractItemView::NoEditTriggers); ui.list->setAlternatingRowColors(true); QObject::connect(delegate, SIGNAL(shortcutChanged(QVariant,QModelIndex)), q, SLOT(capturedShortcut(QVariant,QModelIndex))); //hide the editor widget chen its item becomes hidden QObject::connect(ui.searchFilter->searchLine(), SIGNAL(hiddenChanged(QTreeWidgetItem*,bool)), delegate, SLOT(hiddenBySearchLine(QTreeWidgetItem*,bool))); + //Expand items when searching + QObject::connect(ui.searchFilter->searchLine(), SIGNAL(searchUpdated(QString)), + q, SLOT(searchUpdated(QString))); ui.searchFilter->setFocus(); } void KisShortcutsEditorPrivate::setActionTypes(KisShortcutsEditor::ActionTypes types) { if (actionTypes == types) { return; } actionTypes = types; // show/hide the sections based on new selection QHeaderView *header = ui.list->header(); header->showSection(LocalPrimary); header->showSection(LocalAlternate); } bool KisShortcutsEditorPrivate::addAction(QAction *action, QTreeWidgetItem *hier[], hierarchyLevel level) { // We cannot handle unnamed actions, even though Qt automatically assigns // them names prefixed by `unnamed-`. Unfortunately those automatic names // are not guaranteed to be the same each time, especially not within // KisParts, so they cannot be reliably saved and loaded. QString actionName = action->objectName(); if (actionName.isEmpty() || actionName.startsWith(QStringLiteral("unnamed-"))) { qCritical() << "Skipping action without name " << action->text() << "," << actionName << "!"; return false; } // Construct the actual treeview items. The work happens here. // // Don't feed the editor raw QActions. This code requires that the // "defaultShortcut" dynamic property be set. // // Note: Krita never sets the property "isShortcutConfigurable". // Perhaps it could be useful. const QVariant value = action->property("isShortcutConfigurable"); if (!value.isValid() || value.toBool()) { new KisShortcutsEditorItem((hier[level]), action); return true; } return false; } void KisShortcutsEditorPrivate::allDefault() { for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent() || (*it)->type() != ActionItem) { continue; } KisShortcutsEditorItem *item = static_cast(*it); QAction *act = item->m_action; QList defaultShortcuts = act->property("defaultShortcuts").value >(); if (act->shortcuts() != defaultShortcuts) { QKeySequence primary = defaultShortcuts.isEmpty() ? QKeySequence() : defaultShortcuts.at(0); QKeySequence alternate = defaultShortcuts.size() <= 1 ? QKeySequence() : defaultShortcuts.at(1); changeKeyShortcut(item, LocalPrimary, primary); changeKeyShortcut(item, LocalAlternate, alternate); } } } //static QTreeWidgetItem *KisShortcutsEditorPrivate::findOrMakeItem(QTreeWidgetItem *parent, const QString &name) { for (int i = 0; i < parent->childCount(); i++) { QTreeWidgetItem *child = parent->child(i); if (child->text(0) == name) { return child; } } QTreeWidgetItem *ret = new QTreeWidgetItem(parent, NonActionItem); ret->setText(0, name); ui.list->expandItem(ret); ret->setFlags(ret->flags() & ~Qt::ItemIsSelectable); return ret; } //private slot void KisShortcutsEditorPrivate::capturedShortcut(const QVariant &newShortcut, const QModelIndex &index) { //dispatch to the right handler if (!index.isValid()) { return; } int column = index.column(); KisShortcutsEditorItem *item = itemFromIndex(ui.list, index); Q_ASSERT(item); if (column >= LocalPrimary && column <= Id) { changeKeyShortcut(item, column, newShortcut.value()); } } void KisShortcutsEditorPrivate::changeKeyShortcut(KisShortcutsEditorItem *item, uint column, const QKeySequence &capture) { // The keySequence we get is cleared by KKeySequenceWidget. No conflicts. if (capture == item->keySequence(column)) { return; } item->setKeySequence(column, capture); q->keyChange(); //force view update item->setText(column, capture.toString(QKeySequence::NativeText)); } void KisShortcutsEditorPrivate::clearConfiguration() { for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) { if (!(*it)->parent()) { continue; } KisShortcutsEditorItem *item = static_cast(*it); changeKeyShortcut(item, LocalPrimary, QKeySequence()); changeKeyShortcut(item, LocalAlternate, QKeySequence()); } } /*TODO for the printShortcuts function Nice to have features (which I'm not sure I can do before may due to more important things): - adjust the general page borders, IMHO they're too wide - add a custom printer options page that allows to filter out all actions that don't have a shortcut set to reduce this list. IMHO this should be optional as people might want to simply print all and when they find a new action that they assign a shortcut they can simply use a pen to fill out the empty space - find a way to align the Main/Alternate entries in the shortcuts column without adding borders. I first did this without a nested table but instead simply added 3 rows and merged the 3 cells in the Action name and description column, but unfortunately I didn't find a way to remove the borders between the 6 shortcut cells. */ void KisShortcutsEditorPrivate::printShortcuts() const { QTreeWidgetItem *root = ui.list->invisibleRootItem(); QTextDocument doc; doc.setDefaultFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); QTextCursor cursor(&doc); cursor.beginEditBlock(); QTextCharFormat headerFormat; headerFormat.setProperty(QTextFormat::FontSizeAdjustment, 3); headerFormat.setFontWeight(QFont::Bold); cursor.insertText(i18nc("header for an applications shortcut list", "Shortcuts for %1", QGuiApplication::applicationDisplayName()), headerFormat); QTextCharFormat componentFormat; componentFormat.setProperty(QTextFormat::FontSizeAdjustment, 2); componentFormat.setFontWeight(QFont::Bold); QTextBlockFormat componentBlockFormat = cursor.blockFormat(); componentBlockFormat.setTopMargin(16); componentBlockFormat.setBottomMargin(16); QTextTableFormat tableformat; tableformat.setHeaderRowCount(1); tableformat.setCellPadding(4.0); tableformat.setCellSpacing(0); tableformat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid); tableformat.setBorder(0.5); QList > shortcutTitleToColumn; shortcutTitleToColumn << qMakePair(i18n("Main:"), LocalPrimary); shortcutTitleToColumn << qMakePair(i18n("Alternate:"), LocalAlternate); for (int i = 0; i < root->childCount(); i++) { QTreeWidgetItem *item = root->child(i); cursor.insertBlock(componentBlockFormat, componentFormat); cursor.insertText(item->text(0)); QTextTable *table = cursor.insertTable(1, 3); table->setFormat(tableformat); int currow = 0; QTextTableCell cell = table->cellAt(currow, 0); QTextCharFormat format = cell.format(); format.setFontWeight(QFont::Bold); cell.setFormat(format); cell.firstCursorPosition().insertText(i18n("Action Name")); cell = table->cellAt(currow, 1); cell.setFormat(format); cell.firstCursorPosition().insertText(i18n("Shortcuts")); cell = table->cellAt(currow, 2); cell.setFormat(format); cell.firstCursorPosition().insertText(i18n("Description")); currow++; for (QTreeWidgetItemIterator it(item); *it; ++it) { if ((*it)->type() != ActionItem) { continue; } KisShortcutsEditorItem *editoritem = static_cast(*it); table->insertRows(table->rows(), 1); QVariant data = editoritem->data(Name, Qt::DisplayRole); table->cellAt(currow, 0).firstCursorPosition().insertText(data.toString()); QTextTable *shortcutTable = 0; for (int k = 0; k < shortcutTitleToColumn.count(); k++) { data = editoritem->data(shortcutTitleToColumn.at(k).second, Qt::DisplayRole); QString key = data.value().toString(); if (!key.isEmpty()) { if (!shortcutTable) { shortcutTable = table->cellAt(currow, 1).firstCursorPosition().insertTable(1, 2); QTextTableFormat shortcutTableFormat = tableformat; shortcutTableFormat.setCellSpacing(0.0); shortcutTableFormat.setHeaderRowCount(0); shortcutTableFormat.setBorder(0.0); shortcutTable->setFormat(shortcutTableFormat); } else { shortcutTable->insertRows(shortcutTable->rows(), 1); } shortcutTable->cellAt(shortcutTable->rows() - 1, 0)\ .firstCursorPosition().insertText(shortcutTitleToColumn.at(k).first); shortcutTable->cellAt(shortcutTable->rows() - 1, 1)\ .firstCursorPosition().insertText(key); } } QAction *action = editoritem->m_action; cell = table->cellAt(currow, 2); format = cell.format(); format.setProperty(QTextFormat::FontSizeAdjustment, -1); cell.setFormat(format); cell.firstCursorPosition().insertHtml(action->whatsThis()); currow++; } cursor.movePosition(QTextCursor::End); } cursor.endEditBlock(); QPrinter printer; QPrintDialog *dlg = new QPrintDialog(&printer, q); if (dlg->exec() == QDialog::Accepted) { doc.print(&printer); } delete dlg; }