Krita Source Code Documentation
Loading...
Searching...
No Matches
KisShortcutsEditor_p.cpp
Go to the documentation of this file.
1
14#include "KisShortcutsEditor.h"
16#include <QHeaderView>
17#include <QTreeWidget>
18#include <QDebug>
19#include <QTextTable>
20#include <QTextDocument>
21#include <QPrinter>
22#include <QPrintDialog>
23#include <ksharedconfig.h>
24#include <KConfigGroup>
25#include "kis_action_registry.h"
26#include <KisKineticScroller.h>
27
28//---------------------------------------------------------------------
29// KisShortcutsEditorPrivate
30//---------------------------------------------------------------------
31
32namespace {
33 KisShortcutsEditorItem *itemFromIndex(QTreeWidget *const w, const QModelIndex &index)
34 {
35 QTreeWidgetItem *item = static_cast<QTreeWidgetHack *>(w)->itemFromIndex(index);
36 if (item && item->type() == ActionItem) {
37 return static_cast<KisShortcutsEditorItem *>(item);
38 }
39 return 0;
40 }
41}
42
43
48
49void KisShortcutsEditorPrivate::initGUI(KisShortcutsEditor::ActionTypes types,
50 KisShortcutsEditor::LetterShortcuts allowLetterShortcuts)
51{
52 actionTypes = types;
53
54 ui.setupUi(q);
55 q->layout()->setContentsMargins(0, 0, 0, 0);
56 ui.searchFilter->searchLine()->setTreeWidget(ui.list); // Plug into search line
57 ui.list->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
58
59 // Create the Delegate. It is responsible for the KKeySequenceWidgets that
60 // really change the shortcuts.
62 ui.list,
63 allowLetterShortcuts == KisShortcutsEditor::LetterShortcutsAllowed);
64
65 ui.list->setItemDelegate(delegate);
66 ui.list->setSelectionBehavior(QAbstractItemView::SelectItems);
67 ui.list->setSelectionMode(QAbstractItemView::SingleSelection);
68 //we have our own editing mechanism
69 ui.list->setEditTriggers(QAbstractItemView::NoEditTriggers);
70 ui.list->setAlternatingRowColors(true);
71
72 QScroller *scroller = KisKineticScroller::createPreconfiguredScroller(ui.list);
73 if (scroller){
74 QObject::connect(scroller, SIGNAL(stateChanged(QScroller::State)),
75 q, SLOT(slotScrollerStateChanged(QScroller::State)));
76 }
77
78 QObject::connect(delegate, SIGNAL(shortcutChanged(QVariant,QModelIndex)),
79 q, SLOT(capturedShortcut(QVariant,QModelIndex)));
80 //hide the editor widget when its item becomes hidden
81 QObject::connect(ui.searchFilter->searchLine(), SIGNAL(hiddenChanged(QTreeWidgetItem*,bool)),
82 delegate, SLOT(hiddenBySearchLine(QTreeWidgetItem*,bool)));
83 //Expand items when searching
84 QObject::connect(ui.searchFilter->searchLine(), SIGNAL(searchUpdated(QString)),
85 q, SLOT(searchUpdated(QString)));
86
87 ui.searchFilter->setFocus();
88}
89
90void KisShortcutsEditorPrivate::setActionTypes(KisShortcutsEditor::ActionTypes types)
91{
92 if (actionTypes == types) {
93 return;
94 }
95 actionTypes = types;
96
97 // show/hide the sections based on new selection
98 QHeaderView *header = ui.list->header();
99 header->showSection(LocalPrimary);
100 header->showSection(LocalAlternate);
101}
102
103bool KisShortcutsEditorPrivate::addAction(QAction *action, QTreeWidgetItem *hier[], hierarchyLevel level)
104{
105
106 // We cannot handle unnamed actions, even though Qt automatically assigns
107 // them names prefixed by `unnamed-`. Unfortunately those automatic names
108 // are not guaranteed to be the same each time, especially not within
109 // KisParts, so they cannot be reliably saved and loaded.
110 QString actionName = action->objectName();
111 if (actionName.isEmpty() || actionName.startsWith(QStringLiteral("unnamed-"))) {
112 qCritical() << "Skipping action without name " << action->text() << "," << actionName << "!";
113 return false;
114 }
115
116 // Construct the actual treeview items. The work happens here.
117 //
118 // Don't feed the editor raw QActions. This code requires that the
119 // "defaultShortcut" dynamic property be set.
120 //
121 // Note: Krita never sets the property "isShortcutConfigurable".
122 // Perhaps it could be useful.
123 const QVariant value = action->property("isShortcutConfigurable");
124 if (!value.isValid() || value.toBool()) {
125 new KisShortcutsEditorItem((hier[level]), action);
126 return true;
127 }
128
129 return false;
130}
131
133{
134 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
135 if (!(*it)->parent() || (*it)->type() != ActionItem) {
136 continue;
137 }
138
139 KisShortcutsEditorItem *item = static_cast<KisShortcutsEditorItem *>(*it);
140 QAction *act = item->m_action;
141
142 QList<QKeySequence> defaultShortcuts = act->property("defaultShortcuts").value<QList<QKeySequence> >();
143 if (act->shortcuts() != defaultShortcuts) {
144 QKeySequence primary = defaultShortcuts.isEmpty() ? QKeySequence() : defaultShortcuts.at(0);
145 QKeySequence alternate = defaultShortcuts.size() <= 1 ? QKeySequence() : defaultShortcuts.at(1);
146 changeKeyShortcut(item, LocalPrimary, primary);
147 changeKeyShortcut(item, LocalAlternate, alternate);
148 }
149 }
150}
151
152//static
153
154QTreeWidgetItem *KisShortcutsEditorPrivate::findOrMakeItem(QTreeWidgetItem *parent, const QString &name)
155{
156 for (int i = 0; i < parent->childCount(); i++) {
157 QTreeWidgetItem *child = parent->child(i);
158 if (child->text(0) == name) {
159 return child;
160 }
161 }
162 QTreeWidgetItem *ret = new QTreeWidgetItem(parent, NonActionItem);
163 ret->setText(0, name);
164 ui.list->expandItem(ret);
165 ret->setFlags(ret->flags() & ~Qt::ItemIsSelectable);
166 return ret;
167}
168
169//private slot
170void KisShortcutsEditorPrivate::capturedShortcut(const QVariant &newShortcut, const QModelIndex &index)
171{
172 //dispatch to the right handler
173 if (!index.isValid()) {
174 return;
175 }
176 int column = index.column();
177 KisShortcutsEditorItem *item = itemFromIndex(ui.list, index);
178 Q_ASSERT(item);
179
180 if (column >= LocalPrimary && column <= Id) {
181 changeKeyShortcut(item, column, newShortcut.value<QKeySequence>());
182 }
183}
184
185void KisShortcutsEditorPrivate::changeKeyShortcut(KisShortcutsEditorItem *item, uint column, const QKeySequence &capture)
186{
187 // The keySequence we get is cleared by KisKKeySequenceWidget. No conflicts.
188 if (capture == item->keySequence(column)) {
189 return;
190 }
191
192 item->setKeySequence(column, capture);
193 q->keyChange();
194 //force view update
195 item->setText(column, capture.toString(QKeySequence::NativeText));
196}
197
198
200{
201 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
202 if (!(*it)->parent()) {
203 continue;
204 }
205
206 KisShortcutsEditorItem *item = static_cast<KisShortcutsEditorItem *>(*it);
207
208 changeKeyShortcut(item, LocalPrimary, QKeySequence());
209 changeKeyShortcut(item, LocalAlternate, QKeySequence());
210
211 }
212}
213
214/*TODO for the printShortcuts function
215Nice to have features (which I'm not sure I can do before may due to
216more important things):
217
218- adjust the general page borders, IMHO they're too wide
219
220- add a custom printer options page that allows to filter out all
221 actions that don't have a shortcut set to reduce this list. IMHO this
222 should be optional as people might want to simply print all and when
223 they find a new action that they assign a shortcut they can simply use
224 a pen to fill out the empty space
225
226- find a way to align the Main/Alternate entries in the shortcuts column without
227 adding borders. I first did this without a nested table but instead simply
228 added 3 rows and merged the 3 cells in the Action name and description column,
229 but unfortunately I didn't find a way to remove the borders between the 6
230 shortcut cells.
231*/
233{
234 QTreeWidgetItem *root = ui.list->invisibleRootItem();
235 QTextDocument doc;
236
237 doc.setDefaultFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
238
239 QTextCursor cursor(&doc);
240 cursor.beginEditBlock();
241 QTextCharFormat headerFormat;
242 headerFormat.setProperty(QTextFormat::FontSizeAdjustment, 3);
243 headerFormat.setFontWeight(QFont::Bold);
244 cursor.insertText(i18nc("header for an applications shortcut list", "Shortcuts for %1",
245 QGuiApplication::applicationDisplayName()),
246 headerFormat);
247 QTextCharFormat componentFormat;
248 componentFormat.setProperty(QTextFormat::FontSizeAdjustment, 2);
249 componentFormat.setFontWeight(QFont::Bold);
250 QTextBlockFormat componentBlockFormat = cursor.blockFormat();
251 componentBlockFormat.setTopMargin(16);
252 componentBlockFormat.setBottomMargin(16);
253
254 QTextTableFormat tableformat;
255 tableformat.setHeaderRowCount(1);
256 tableformat.setCellPadding(4.0);
257 tableformat.setCellSpacing(0);
258 tableformat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
259 tableformat.setBorder(0.5);
260
261 QList<QPair<QString, ColumnDesignation> > shortcutTitleToColumn;
262 shortcutTitleToColumn << qMakePair(i18n("Main:"), LocalPrimary);
263 shortcutTitleToColumn << qMakePair(i18n("Alternate:"), LocalAlternate);
264
265 for (int i = 0; i < root->childCount(); i++) {
266 QTreeWidgetItem *item = root->child(i);
267 cursor.insertBlock(componentBlockFormat, componentFormat);
268 cursor.insertText(item->text(0));
269
270 QTextTable *table = cursor.insertTable(1, 3);
271 table->setFormat(tableformat);
272 int currow = 0;
273
274 QTextTableCell cell = table->cellAt(currow, 0);
275 QTextCharFormat format = cell.format();
276 format.setFontWeight(QFont::Bold);
277 cell.setFormat(format);
278 cell.firstCursorPosition().insertText(i18n("Action Name"));
279
280 cell = table->cellAt(currow, 1);
281 cell.setFormat(format);
282 cell.firstCursorPosition().insertText(i18n("Shortcuts"));
283
284 cell = table->cellAt(currow, 2);
285 cell.setFormat(format);
286 cell.firstCursorPosition().insertText(i18n("Description"));
287 currow++;
288
289 for (QTreeWidgetItemIterator it(item); *it; ++it) {
290 if ((*it)->type() != ActionItem) {
291 continue;
292 }
293
294 KisShortcutsEditorItem *editoritem = static_cast<KisShortcutsEditorItem *>(*it);
295 table->insertRows(table->rows(), 1);
296 QVariant data = editoritem->data(Name, Qt::DisplayRole);
297 table->cellAt(currow, 0).firstCursorPosition().insertText(data.toString());
298
299 QTextTable *shortcutTable = 0;
300 for (int k = 0; k < shortcutTitleToColumn.count(); k++) {
301 data = editoritem->data(shortcutTitleToColumn.at(k).second, Qt::DisplayRole);
302 QString key = data.value<QKeySequence>().toString();
303
304 if (!key.isEmpty()) {
305 if (!shortcutTable) {
306 shortcutTable = table->cellAt(currow, 1).firstCursorPosition().insertTable(1, 2);
307 QTextTableFormat shortcutTableFormat = tableformat;
308 shortcutTableFormat.setCellSpacing(0.0);
309 shortcutTableFormat.setHeaderRowCount(0);
310 shortcutTableFormat.setBorder(0.0);
311 shortcutTable->setFormat(shortcutTableFormat);
312 } else {
313 shortcutTable->insertRows(shortcutTable->rows(), 1);
314 }
315 shortcutTable->cellAt(shortcutTable->rows() - 1, 0)\
316 .firstCursorPosition().insertText(shortcutTitleToColumn.at(k).first);
317 shortcutTable->cellAt(shortcutTable->rows() - 1, 1)\
318 .firstCursorPosition().insertText(key);
319 }
320 }
321
322 QAction *action = editoritem->m_action;
323 cell = table->cellAt(currow, 2);
324 format = cell.format();
325 format.setProperty(QTextFormat::FontSizeAdjustment, -1);
326 cell.setFormat(format);
327 cell.firstCursorPosition().insertHtml(action->whatsThis());
328
329 currow++;
330 }
331 cursor.movePosition(QTextCursor::End);
332 }
333 cursor.endEditBlock();
334
335 QPrinter printer;
336 QPrintDialog *dlg = new QPrintDialog(&printer, q);
337 if (dlg->exec() == QDialog::Accepted) {
338 doc.print(&printer);
339 }
340 delete dlg;
341}
float value(const T *src, size_t ch)
@ NonActionItem
@ LocalAlternate
@ LocalPrimary
unsigned int uint
QAction * m_action
The action this item is responsible for.
QKeySequence keySequence(uint column) const
QVariant data(int column, int role=Qt::DisplayRole) const override
void setKeySequence(uint column, const QKeySequence &seq)
void capturedShortcut(const QVariant &, const QModelIndex &)
KisShortcutsEditorDelegate * delegate
KisShortcutsEditor::ActionTypes actionTypes
Ui::KisShortcutsDialog ui
void initGUI(KisShortcutsEditor::ActionTypes actionTypes, KisShortcutsEditor::LetterShortcuts allowLetterShortcuts)
QTreeWidgetItem * findOrMakeItem(QTreeWidgetItem *parent, const QString &name)
hierarchyLevel
Represents the three hierarchies the dialog displays.
bool addAction(QAction *action, QTreeWidgetItem *hier[], hierarchyLevel level)
void changeKeyShortcut(KisShortcutsEditorItem *item, uint column, const QKeySequence &capture)
void setActionTypes(KisShortcutsEditor::ActionTypes actionTypes)
KisShortcutsEditorPrivate(KisShortcutsEditor *q)
Widget for configuration of KAccel and KGlobalAccel.
@ LetterShortcutsAllowed
Letter shortcuts are allowed.
KRITAWIDGETUTILS_EXPORT QScroller * createPreconfiguredScroller(QAbstractScrollArea *target)