00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "kshortcutseditor.h"
00027
00028
00029 #include "kshortcutsdialog_p.h"
00030
00031 #include <QHeaderView>
00032 #include <QList>
00033 #include <QObject>
00034 #include <QTimer>
00035 #include <QTextDocument>
00036 #include <QTextTable>
00037 #include <QTextCursor>
00038 #include <QTextTableFormat>
00039 #include <QPrinter>
00040 #include <QPrintDialog>
00041
00042 #include "kaction.h"
00043 #include "kactioncollection.h"
00044 #include "kactioncategory.h"
00045 #include "kdebug.h"
00046 #include "kdeprintdialog.h"
00047 #include "kglobalaccel.h"
00048 #include "kmessagebox.h"
00049 #include "kaboutdata.h"
00050
00051
00052
00053
00054
00055 KShortcutsEditor::KShortcutsEditor(KActionCollection *collection, QWidget *parent, ActionTypes actionType,
00056 LetterShortcuts allowLetterShortcuts )
00057 : QWidget( parent )
00058 , d(new KShortcutsEditorPrivate(this))
00059 {
00060 d->initGUI(actionType, allowLetterShortcuts);
00061 addCollection(collection);
00062 }
00063
00064
00065 KShortcutsEditor::KShortcutsEditor(QWidget *parent, ActionTypes actionType, LetterShortcuts allowLetterShortcuts)
00066 : QWidget(parent)
00067 , d(new KShortcutsEditorPrivate(this))
00068 {
00069 d->initGUI(actionType, allowLetterShortcuts);
00070 }
00071
00072
00073 KShortcutsEditor::~KShortcutsEditor()
00074 {
00075 delete d;
00076 }
00077
00078
00079 bool KShortcutsEditor::isModified() const
00080 {
00081
00082 QTreeWidgetItemIterator it(d->ui.list, QTreeWidgetItemIterator::NoChildren);
00083
00084 for (; (*it); ++it) {
00085 KShortcutsEditorItem* item = dynamic_cast<KShortcutsEditorItem *>(*it);
00086 if (item && item->isModified()) {
00087 return true;
00088 }
00089 }
00090 return false;
00091 }
00092
00093 void KShortcutsEditor::clearCollections()
00094 {
00095 d->delegate->contractAll();
00096 d->ui.list->clear();
00097 d->actionCollections.clear();
00098 QTimer::singleShot(0, this, SLOT(resizeColumns()));
00099 }
00100
00101 void KShortcutsEditor::addCollection(KActionCollection *collection, const QString &title)
00102 {
00103
00104
00105 if (collection->isEmpty()) {
00106 return;
00107 }
00108
00109
00110
00111 setUpdatesEnabled(false);
00112
00113 d->actionCollections.append(collection);
00114
00115
00116 d->delegate->setCheckActionCollections(d->actionCollections);
00117 QString displayTitle = title;
00118
00119 if (displayTitle.isEmpty()) {
00120
00121 if (const KAboutData *about = collection->componentData().aboutData()) {
00122 displayTitle = about->programName();
00123 }
00124
00125 if (displayTitle.isEmpty()) {
00126 displayTitle = "Unknown";
00127 }
00128 }
00129
00130 QTreeWidgetItem *hier[3];
00131 hier[KShortcutsEditorPrivate::Root] = d->ui.list->invisibleRootItem();
00132 hier[KShortcutsEditorPrivate::Program] = d->findOrMakeItem( hier[KShortcutsEditorPrivate::Root], displayTitle);
00133 hier[KShortcutsEditorPrivate::Action] = NULL;
00134
00135
00136 QSet<QAction*> actionsSeen;
00137
00138
00139 QList<KActionCategory*> categories = collection->findChildren<KActionCategory*>();
00140 foreach (KActionCategory *category, categories) {
00141 hier[KShortcutsEditorPrivate::Action] = d->findOrMakeItem(hier[KShortcutsEditorPrivate::Program], category->text());
00142 foreach(QAction *action, category->actions()) {
00143
00144 actionsSeen.insert(action);
00145 d->addAction(action, hier, KShortcutsEditorPrivate::Action);
00146 }
00147 }
00148
00149
00150
00151 foreach (QAction *action, collection->actions()) {
00152 if (actionsSeen.contains(action)) {
00153 continue;
00154 }
00155
00156 d->addAction(action, hier, KShortcutsEditorPrivate::Program);
00157 }
00158
00159
00160 d->ui.list->sortItems(Name, Qt::AscendingOrder);
00161
00162
00163 setUpdatesEnabled(true);
00164
00165 QTimer::singleShot(0, this, SLOT(resizeColumns()));
00166 }
00167
00168
00169 void KShortcutsEditor::importConfiguration( KConfig *config)
00170 {
00171 if (d->actionTypes & KShortcutsEditor::GlobalAction) {
00172 QString groupName = "Global Shortcuts";
00173 KConfigGroup group( config, groupName );
00174 foreach (KActionCollection* collection, d->actionCollections) {
00175 collection->importGlobalShortcuts( &group );
00176 }
00177 }
00178 if (d->actionTypes & ~KShortcutsEditor::GlobalAction) {
00179 QString groupName = "Shortcuts";
00180 KConfigGroup group( config, groupName );
00181 foreach (KActionCollection* collection, d->actionCollections) {
00182 collection->readSettings( &group );
00183 }
00184 }
00185 }
00186
00187 void KShortcutsEditor::exportConfiguration( KConfig *config) const
00188 {
00189 if (d->actionTypes & KShortcutsEditor::GlobalAction) {
00190 QString groupName = "Global Shortcuts";
00191 KConfigGroup group( config, groupName );
00192 foreach (KActionCollection* collection, d->actionCollections) {
00193 collection->exportGlobalShortcuts( &group, true );
00194 }
00195 }
00196 if (d->actionTypes & ~KShortcutsEditor::GlobalAction) {
00197 QString groupName = "Shortcuts";
00198 KConfigGroup group( config, groupName );
00199 foreach (KActionCollection* collection, d->actionCollections) {
00200 collection->writeSettings( &group, true );
00201 }
00202 }
00203 }
00204
00205 void KShortcutsEditor::writeConfiguration( KConfigGroup *config) const
00206 {
00207 foreach (KActionCollection* collection, d->actionCollections)
00208 collection->writeSettings(config);
00209 }
00210
00211
00212
00213 void KShortcutsEditor::resizeColumns()
00214 {
00215 for (int i = 0; i < d->ui.list->columnCount(); i++)
00216 d->ui.list->resizeColumnToContents(i);
00217 }
00218
00219
00220 void KShortcutsEditor::commit()
00221 {
00222 for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
00223 if (KShortcutsEditorItem* item = dynamic_cast<KShortcutsEditorItem*>(*it)) {
00224 item->commit();
00225 }
00226 }
00227 }
00228
00229
00230 void KShortcutsEditor::save()
00231 {
00232 writeConfiguration();
00233
00234
00235
00236
00237 commit();
00238 }
00239
00240
00241
00242 void KShortcutsEditor::undoChanges()
00243 {
00244
00245
00246
00247 for (QTreeWidgetItemIterator it(d->ui.list); (*it); ++it) {
00248 if (KShortcutsEditorItem* item = dynamic_cast<KShortcutsEditorItem*>(*it)) {
00249 item->undo();
00250 }
00251 }
00252 }
00253
00254
00255
00256
00257
00258 void KShortcutsEditor::allDefault()
00259 {
00260 d->allDefault();
00261 }
00262
00263
00264 void KShortcutsEditor::printShortcuts() const
00265 {
00266 d->printShortcuts();
00267 }
00268
00269
00270
00271
00272
00273
00274 KShortcutsEditorPrivate::KShortcutsEditorPrivate( KShortcutsEditor *q )
00275 : q(q),
00276 delegate(0)
00277 {}
00278
00279 void KShortcutsEditorPrivate::initGUI( KShortcutsEditor::ActionTypes types, KShortcutsEditor::LetterShortcuts allowLetterShortcuts )
00280 {
00281 actionTypes = types;
00282
00283 ui.setupUi(q);
00284 q->layout()->setMargin(0);
00285 ui.searchFilter->searchLine()->setTreeWidget(ui.list);
00286 ui.list->header()->setResizeMode(QHeaderView::ResizeToContents);
00287 ui.list->header()->hideSection(GlobalAlternate);
00288 ui.list->header()->hideSection(ShapeGesture);
00289 ui.list->header()->hideSection(RockerGesture);
00290 if (!(actionTypes & KShortcutsEditor::GlobalAction)) {
00291 ui.list->header()->hideSection(GlobalPrimary);
00292 } else if (!(actionTypes & ~KShortcutsEditor::GlobalAction)) {
00293 ui.list->header()->hideSection(LocalPrimary);
00294 ui.list->header()->hideSection(LocalAlternate);
00295 }
00296
00297
00298
00299 delegate = new KShortcutsEditorDelegate(
00300 ui.list,
00301 allowLetterShortcuts == KShortcutsEditor::LetterShortcutsAllowed);
00302
00303 ui.list->setItemDelegate(delegate);
00304 ui.list->setSelectionBehavior(QAbstractItemView::SelectItems);
00305 ui.list->setSelectionMode(QAbstractItemView::SingleSelection);
00306
00307 ui.list->setEditTriggers(QAbstractItemView::NoEditTriggers);
00308 ui.list->setAlternatingRowColors(true);
00309
00310
00311 QObject::connect(delegate, SIGNAL(shortcutChanged(QVariant, const QModelIndex &)),
00312 q, SLOT(capturedShortcut(QVariant, const QModelIndex &)));
00313
00314 QObject::connect(ui.searchFilter->searchLine(), SIGNAL(hiddenChanged(QTreeWidgetItem *, bool)),
00315 delegate, SLOT(hiddenBySearchLine(QTreeWidgetItem *, bool)));
00316
00317 ui.searchFilter->setFocus();
00318 }
00319
00320
00321 bool KShortcutsEditorPrivate::addAction(QAction *action, QTreeWidgetItem *hier[], hierarchyLevel level)
00322 {
00323
00324
00325 QString actionName = action->objectName();
00326 if (actionName.isEmpty() || actionName.startsWith("unnamed-")) {
00327 kError() << "Skipping action without name " << action->text() << "," << actionName << "!";
00328 return false;
00329 }
00330
00331
00332
00333
00334 KAction *kact;
00335 if ((kact = qobject_cast<KAction *>(action)) && kact->isShortcutConfigurable()) {
00336 new KShortcutsEditorItem((hier[level]), kact);
00337 return true;
00338 }
00339
00340 return false;
00341 }
00342
00343 void KShortcutsEditorPrivate::allDefault()
00344 {
00345 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
00346 if (!(*it)->parent())
00347 continue;
00348
00349 KShortcutsEditorItem *item = static_cast<KShortcutsEditorItem *>(*it);
00350 KAction *act = item->m_action;
00351
00352 if (act->shortcut() != act->shortcut(KAction::DefaultShortcut)) {
00353 changeKeyShortcut(item, LocalPrimary, act->shortcut(KAction::DefaultShortcut).primary());
00354 changeKeyShortcut(item, LocalAlternate, act->shortcut(KAction::DefaultShortcut).alternate());
00355 }
00356
00357 if (act->globalShortcut() != act->globalShortcut(KAction::DefaultShortcut)) {
00358 changeKeyShortcut(item, GlobalPrimary, act->globalShortcut(KAction::DefaultShortcut).primary());
00359 changeKeyShortcut(item, GlobalAlternate, act->globalShortcut(KAction::DefaultShortcut).alternate());
00360 }
00361
00362 if (act->shapeGesture() != act->shapeGesture(KAction::DefaultShortcut))
00363 changeShapeGesture(item, act->shapeGesture(KAction::DefaultShortcut));
00364
00365 if (act->rockerGesture() != act->rockerGesture(KAction::DefaultShortcut))
00366 changeRockerGesture(item, act->rockerGesture(KAction::DefaultShortcut));
00367 }
00368 }
00369
00370
00371 KShortcutsEditorItem *KShortcutsEditorPrivate::itemFromIndex(QTreeWidget *const w,
00372 const QModelIndex &index)
00373 {
00374 QTreeWidgetItem *item = static_cast<QTreeWidgetHack *>(w)->itemFromIndex(index);
00375 if (item && item->type() == ActionItem) {
00376 return static_cast<KShortcutsEditorItem *>(item);
00377 }
00378 return 0;
00379 }
00380
00381
00382 QTreeWidgetItem *KShortcutsEditorPrivate::findOrMakeItem(QTreeWidgetItem *parent, const QString &name)
00383 {
00384 for (int i = 0; i < parent->childCount(); i++) {
00385 QTreeWidgetItem *child = parent->child(i);
00386 if (child->text(0) == name)
00387 return child;
00388 }
00389 QTreeWidgetItem *ret = new QTreeWidgetItem(parent, NonActionItem);
00390 ret->setText(0, name);
00391 ui.list->expandItem(ret);
00392 ret->setFlags(ret->flags() & ~Qt::ItemIsSelectable);
00393 return ret;
00394 }
00395
00396
00397
00398 void KShortcutsEditorPrivate::capturedShortcut(const QVariant &newShortcut, const QModelIndex &index)
00399 {
00400
00401 if (!index.isValid())
00402 return;
00403 int column = index.column();
00404 KShortcutsEditorItem *item = itemFromIndex(ui.list, index);
00405 Q_ASSERT(item);
00406
00407 if (column >= LocalPrimary && column <= GlobalAlternate)
00408 changeKeyShortcut(item, column, newShortcut.value<QKeySequence>());
00409 else if (column == ShapeGesture)
00410 changeShapeGesture(item, newShortcut.value<KShapeGesture>());
00411 else if (column == RockerGesture)
00412 changeRockerGesture(item, newShortcut.value<KRockerGesture>());
00413 }
00414
00415
00416 void KShortcutsEditorPrivate::changeKeyShortcut(KShortcutsEditorItem *item, uint column, const QKeySequence &capture)
00417 {
00418
00419 if (capture == item->keySequence(column)) {
00420 return;
00421 }
00422
00423 item->setKeySequence(column, capture);
00424 q->keyChange();
00425
00426 item->setText(column, capture.toString(QKeySequence::NativeText));
00427 }
00428
00429
00430 void KShortcutsEditorPrivate::changeShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &capture)
00431 {
00432 if (capture == item->m_action->shapeGesture())
00433 return;
00434
00435 if (capture.isValid()) {
00436 bool conflict = false;
00437 KShortcutsEditorItem *otherItem;
00438
00439
00440 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
00441 if (!(*it)->parent() || (*it == item))
00442 continue;
00443
00444 otherItem = static_cast<KShortcutsEditorItem *>(*it);
00445
00446
00447 if (!otherItem->m_action->shapeGesture().isValid())
00448 continue;
00449
00450 if (capture == otherItem->m_action->shapeGesture()) {
00451 conflict = true;
00452 break;
00453 }
00454 }
00455
00456 if (conflict && !stealShapeGesture(otherItem, capture))
00457 return;
00458 }
00459
00460 item->setShapeGesture(capture);
00461 }
00462
00463
00464 void KShortcutsEditorPrivate::changeRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &capture)
00465 {
00466 if (capture == item->m_action->rockerGesture())
00467 return;
00468
00469 if (capture.isValid()) {
00470 bool conflict = false;
00471 KShortcutsEditorItem *otherItem;
00472
00473 for (QTreeWidgetItemIterator it(ui.list); (*it); ++it) {
00474 if (!(*it)->parent() || (*it == item))
00475 continue;
00476
00477 otherItem = static_cast<KShortcutsEditorItem *>(*it);
00478
00479 if (capture == otherItem->m_action->rockerGesture()) {
00480 conflict = true;
00481 break;
00482 }
00483 }
00484
00485 if (conflict && !stealRockerGesture(otherItem, capture))
00486 return;
00487 }
00488
00489 item->setRockerGesture(capture);
00490 }
00491
00492
00493 bool KShortcutsEditorPrivate::stealShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &gst)
00494 {
00495 QString title = i18n("Key Conflict");
00496 QString message = i18n("The '%1' shape gesture has already been allocated to the \"%2\" action.\n"
00497 "Do you want to reassign it from that action to the current one?",
00498 gst.shapeName(), item->m_action->text());
00499
00500 if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign")))
00501 != KMessageBox::Continue)
00502 return false;
00503
00504 item->setShapeGesture(KShapeGesture());
00505 return true;
00506 }
00507
00508
00509 bool KShortcutsEditorPrivate::stealRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &gst)
00510 {
00511 QString title = i18n("Key Conflict");
00512 QString message = i18n("The '%1' rocker gesture has already been allocated to the \"%2\" action.\n"
00513 "Do you want to reassign it from that action to the current one?",
00514 gst.rockerName(), item->m_action->text());
00515
00516 if (KMessageBox::warningContinueCancel(q, message, title, KGuiItem(i18n("Reassign")))
00517 != KMessageBox::Continue)
00518 return false;
00519
00520 item->setRockerGesture(KRockerGesture());
00521 return true;
00522 }
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543 void KShortcutsEditorPrivate::printShortcuts() const
00544 {
00545 QTreeWidgetItem* root = ui.list->invisibleRootItem();
00546 QTextDocument doc;
00547 doc.setDefaultFont(KGlobalSettings::generalFont());
00548 QTextCursor cursor(&doc);
00549 cursor.beginEditBlock();
00550 QTextCharFormat headerFormat;
00551 headerFormat.setProperty(QTextFormat::FontSizeAdjustment, 3);
00552 headerFormat.setFontWeight(QFont::Bold);
00553 cursor.insertText(i18nc("header for an applications shortcut list","Shortcuts for %1",
00554 KGlobal::mainComponent().aboutData()->programName()),
00555 headerFormat);
00556 QTextCharFormat componentFormat;
00557 componentFormat.setProperty(QTextFormat::FontSizeAdjustment, 2);
00558 componentFormat.setFontWeight(QFont::Bold);
00559 QTextBlockFormat componentBlockFormat = cursor.blockFormat();
00560 componentBlockFormat.setTopMargin(16);
00561 componentBlockFormat.setBottomMargin(16);
00562
00563 QTextTableFormat tableformat;
00564 tableformat.setHeaderRowCount(1);
00565 tableformat.setCellPadding(4.0);
00566 tableformat.setCellSpacing(0);
00567 tableformat.setBorderStyle(QTextFrameFormat::BorderStyle_Solid);
00568 tableformat.setBorder(0.5);
00569
00570 QList<QPair<QString,ColumnDesignation> > shortcutTitleToColumn;
00571 shortcutTitleToColumn << qMakePair(i18n("Main:"), LocalPrimary);
00572 shortcutTitleToColumn << qMakePair(i18n("Alternate:"), LocalAlternate);
00573 shortcutTitleToColumn << qMakePair(i18n("Global:"), GlobalPrimary);
00574
00575 for (int i = 0; i < root->childCount(); i++) {
00576 QTreeWidgetItem* item = root->child(i);
00577 cursor.insertBlock(componentBlockFormat, componentFormat);
00578 cursor.insertText(item->text(0));
00579
00580 QTextTable* table = cursor.insertTable(1,3);
00581 table->setFormat(tableformat);
00582 int currow = 0;
00583
00584 QTextTableCell cell = table->cellAt(currow,0);
00585 QTextCharFormat format = cell.format();
00586 format.setFontWeight(QFont::Bold);
00587 cell.setFormat(format);
00588 cell.firstCursorPosition().insertText(i18n("Action Name"));
00589
00590 cell = table->cellAt(currow,1);
00591 cell.setFormat(format);
00592 cell.firstCursorPosition().insertText(i18n("Shortcuts"));
00593
00594 cell = table->cellAt(currow,2);
00595 cell.setFormat(format);
00596 cell.firstCursorPosition().insertText(i18n("Description"));
00597 currow++;
00598
00599 for (int j = 0; j < item->childCount(); j++) {
00600 QTreeWidgetItem* actionitem = item->child(j);
00601 KShortcutsEditorItem* editoritem =
00602 dynamic_cast<KShortcutsEditorItem*>(actionitem);
00603 table->insertRows(table->rows(),1);
00604 QVariant data = editoritem->data(Name,Qt::DisplayRole);
00605 table->cellAt(currow, 0).firstCursorPosition().insertText(data.toString());
00606
00607 QTextTable* shortcutTable = 0 ;
00608 for(int k = 0; k < shortcutTitleToColumn.count(); k++) {
00609 data = editoritem->data(shortcutTitleToColumn.at(k).second,Qt::DisplayRole);
00610 QString key = data.value<QKeySequence>().toString();
00611
00612 if(!key.isEmpty()) {
00613 if( !shortcutTable ) {
00614 shortcutTable = table->cellAt(currow, 1).firstCursorPosition().insertTable(1,2);
00615 QTextTableFormat shortcutTableFormat = tableformat;
00616 shortcutTableFormat.setCellSpacing(0.0);
00617 shortcutTableFormat.setHeaderRowCount(0);
00618 shortcutTableFormat.setBorder(0.0);
00619 shortcutTable->setFormat(shortcutTableFormat);
00620 } else {
00621 shortcutTable->insertRows(shortcutTable->rows(),1);
00622 }
00623 shortcutTable->cellAt(shortcutTable->rows()-1,0).firstCursorPosition().insertText(shortcutTitleToColumn.at(k).first);
00624 shortcutTable->cellAt(shortcutTable->rows()-1,1).firstCursorPosition().insertText(key);
00625 }
00626 }
00627
00628 KAction* action = editoritem->m_action;
00629 cell = table->cellAt(currow, 2);
00630 format = cell.format();
00631 format.setProperty(QTextFormat::FontSizeAdjustment, -1);
00632 cell.setFormat(format);
00633 cell.firstCursorPosition().insertHtml(action->whatsThis());
00634
00635 currow++;
00636 }
00637 cursor.movePosition(QTextCursor::End);
00638 }
00639 cursor.endEditBlock();
00640
00641 QPrinter printer;
00642 QPrintDialog *dlg = KdePrint::createPrintDialog(&printer, q);
00643 if (dlg->exec() == QDialog::Accepted) {
00644 doc.print(&printer);
00645 }
00646 delete dlg;
00647 }
00648
00649 #include "kshortcutseditor.moc"