KDEUI
kshortcutseditordelegate.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> 00003 Copyright (C) 1997 Nicolas Hadacek <hadacek@kde.org> 00004 Copyright (C) 1998 Matthias Ettrich <ettrich@kde.org> 00005 Copyright (C) 2001 Ellis Whitehead <ellis@kde.org> 00006 Copyright (C) 2006 Hamish Rodda <rodda@kde.org> 00007 Copyright (C) 2007 Roberto Raggi <roberto@kdevelop.org> 00008 Copyright (C) 2007 Andreas Hartmetz <ahartmetz@gmail.com> 00009 Copyright (C) 2008 Michael Jansen <kde@michael-jansen.biz> 00010 00011 This library is free software; you can redistribute it and/or 00012 modify it under the terms of the GNU Library General Public 00013 License as published by the Free Software Foundation; either 00014 version 2 of the License, or (at your option) any later version. 00015 00016 This library is distributed in the hope that it will be useful, 00017 but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 Library General Public License for more details. 00020 00021 You should have received a copy of the GNU Library General Public License 00022 along with this library; see the file COPYING.LIB. If not, write to 00023 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00024 Boston, MA 02110-1301, USA. 00025 */ 00026 00027 00028 00029 #include "kshortcutsdialog_p.h" 00030 #include "kaction_p.h" 00031 00032 #include <QApplication> 00033 #include <QHeaderView> 00034 #include <QKeyEvent> 00035 #include <QLabel> 00036 #include <QPainter> 00037 #include <QTreeWidgetItemIterator> 00038 00039 #include "kaction.h" 00040 00041 #include "kdebug.h" 00042 00043 00044 00045 00046 KShortcutsEditorDelegate::KShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts) 00047 : KExtendableItemDelegate(parent), 00048 m_allowLetterShortcuts(allowLetterShortcuts), 00049 m_editor(0) 00050 { 00051 Q_ASSERT(qobject_cast<QAbstractItemView *>(parent)); 00052 00053 QPixmap pixmap( 16, 16 ); 00054 pixmap.fill( QColor( Qt::transparent ) ); 00055 QPainter p( &pixmap ); 00056 QStyleOption option; 00057 option.rect = pixmap.rect(); 00058 00059 bool isRtl = QApplication::isRightToLeft(); 00060 QApplication::style()->drawPrimitive( isRtl ? QStyle::PE_IndicatorArrowLeft : QStyle::PE_IndicatorArrowRight, &option, &p ); 00061 p.end(); 00062 setExtendPixmap( pixmap ); 00063 00064 pixmap.fill( QColor( Qt::transparent ) ); 00065 p.begin( &pixmap ); 00066 QApplication::style()->drawPrimitive( QStyle::PE_IndicatorArrowDown, &option, &p ); 00067 p.end(); 00068 setContractPixmap( pixmap ); 00069 00070 parent->installEventFilter(this); 00071 00072 // Listen to activation signals 00073 // connect(parent, SIGNAL(activated(QModelIndex)), this, SLOT(itemActivated(QModelIndex))); 00074 connect(parent, SIGNAL(clicked(QModelIndex)), this, SLOT(itemActivated(QModelIndex))); 00075 00076 // Listen to collapse signals 00077 connect(parent, SIGNAL(collapsed(QModelIndex)), this, SLOT(itemCollapsed(QModelIndex))); 00078 } 00079 00080 00081 void KShortcutsEditorDelegate::stealShortcut( 00082 const QKeySequence &seq, 00083 KAction *action) 00084 { 00085 QTreeWidget *view = static_cast<QTreeWidget *>(parent()); 00086 00087 // Iterate over all items 00088 QTreeWidgetItemIterator it(view, QTreeWidgetItemIterator::NoChildren); 00089 00090 for (; (*it); ++it) { 00091 KShortcutsEditorItem* item = dynamic_cast<KShortcutsEditorItem *>(*it); 00092 if (item && item->data(0, ObjectRole).value<QObject*>() == action) { 00093 00094 // We found the action, snapshot the current state. Steal the 00095 // shortcut. We will save the change later. 00096 KShortcut cut = action->shortcut(); 00097 if ( cut.primary().matches(seq) != QKeySequence::NoMatch 00098 || seq.matches(cut.primary()) != QKeySequence::NoMatch) { 00099 item->setKeySequence(LocalPrimary, QKeySequence()); 00100 } 00101 00102 if ( cut.alternate().matches(seq) != QKeySequence::NoMatch 00103 || seq.matches(cut.alternate()) != QKeySequence::NoMatch) { 00104 item->setKeySequence(LocalAlternate, QKeySequence()); 00105 } 00106 break; 00107 } 00108 } 00109 00110 } 00111 00112 00113 QSize KShortcutsEditorDelegate::sizeHint(const QStyleOptionViewItem &option, 00114 const QModelIndex &index) const 00115 { 00116 QSize ret(KExtendableItemDelegate::sizeHint(option, index)); 00117 ret.rheight() += 4; 00118 return ret; 00119 } 00120 00121 00122 //slot 00123 void KShortcutsEditorDelegate::itemActivated(QModelIndex index) 00124 { 00125 //As per our constructor our parent *is* a QTreeWidget 00126 QTreeWidget *view = static_cast<QTreeWidget *>(parent()); 00127 00128 KShortcutsEditorItem *item = KShortcutsEditorPrivate::itemFromIndex(view, index); 00129 if (!item) { 00130 //that probably was a non-leaf (type() !=ActionItem) item 00131 return; 00132 } 00133 00134 int column = index.column(); 00135 if (column == Name) { 00136 // If user click in the name column activate the (Global|Local)Primary 00137 // column if possible. 00138 if (!view->header()->isSectionHidden(LocalPrimary)) { 00139 column = LocalPrimary; 00140 } else if (!view->header()->isSectionHidden(GlobalPrimary)) { 00141 column = GlobalPrimary; 00142 } else { 00143 // do nothing. 00144 } 00145 index = index.sibling(index.row(), column); 00146 view->selectionModel()->select(index, QItemSelectionModel::SelectCurrent); 00147 } 00148 00149 // Check if the models wants us to edit the item at index 00150 if (!index.data(ShowExtensionIndicatorRole).value<bool>()) { 00151 return; 00152 } 00153 00154 if (!isExtended(index)) { 00155 //we only want maximum ONE extender open at any time. 00156 if (m_editingIndex.isValid()) { 00157 KShortcutsEditorItem *oldItem = KShortcutsEditorPrivate::itemFromIndex(view, 00158 m_editingIndex); 00159 Q_ASSERT(oldItem); //here we really expect nothing but a real KShortcutsEditorItem 00160 00161 oldItem->setNameBold(false); 00162 contractItem(m_editingIndex); 00163 } 00164 00165 m_editingIndex = index; 00166 QWidget *viewport = static_cast<QAbstractItemView*>(parent())->viewport(); 00167 00168 if (column >= LocalPrimary && column <= GlobalAlternate) { 00169 ShortcutEditWidget *editor = new ShortcutEditWidget(viewport, 00170 index.data(DefaultShortcutRole).value<QKeySequence>(), 00171 index.data(ShortcutRole).value<QKeySequence>(), 00172 m_allowLetterShortcuts); 00173 if (column==GlobalPrimary) { 00174 QObject *action = index.data(ObjectRole).value<QObject*>(); 00175 connect( 00176 action, SIGNAL(globalShortcutChanged(QKeySequence)), 00177 editor, SLOT(setKeySequence(QKeySequence))); 00178 editor->setMultiKeyShortcutsAllowed(false); 00179 KAction *kaction = qobject_cast<KAction*>(action); 00180 if (kaction) { 00181 editor->setComponentName(kaction->d->componentData.componentName()); 00182 } 00183 } 00184 00185 m_editor = editor; 00186 // For global shortcuts check against the kde standard shortcuts 00187 if (column == GlobalPrimary || column == GlobalAlternate) { 00188 editor->setCheckForConflictsAgainst( 00189 KKeySequenceWidget::LocalShortcuts 00190 | KKeySequenceWidget::GlobalShortcuts 00191 | KKeySequenceWidget::StandardShortcuts ); 00192 } 00193 00194 editor->setCheckActionCollections(m_checkActionCollections); 00195 00196 connect(m_editor, SIGNAL(keySequenceChanged(const QKeySequence &)), 00197 this, SLOT(keySequenceChanged(const QKeySequence &))); 00198 connect(m_editor, SIGNAL(stealShortcut(const QKeySequence &, KAction*)), 00199 this, SLOT(stealShortcut(const QKeySequence&, KAction*))); 00200 00201 } else if (column == RockerGesture) { 00202 m_editor = new QLabel("A lame placeholder", viewport); 00203 00204 } else if (column == ShapeGesture) { 00205 m_editor = new QLabel("<i>A towel</i>", viewport); 00206 00207 } else 00208 return; 00209 00210 m_editor->installEventFilter(this); 00211 item->setNameBold(true); 00212 extendItem(m_editor, index); 00213 00214 } else { 00215 //the item is extended, and clicking on it again closes it 00216 item->setNameBold(false); 00217 contractItem(index); 00218 view->selectionModel()->select(index, QItemSelectionModel::Clear); 00219 m_editingIndex = QModelIndex(); 00220 m_editor = 0; 00221 } 00222 } 00223 00224 00225 //slot 00226 void KShortcutsEditorDelegate::itemCollapsed(QModelIndex index) 00227 { 00228 if (!m_editingIndex.isValid()) { 00229 return; 00230 } 00231 00232 const QAbstractItemModel *model = index.model(); 00233 for (int row = 0; row < model->rowCount(index); ++row) { 00234 QModelIndex rowIndex = model->index(row, 0, index); 00235 00236 for (int col = 0; col < index.model()->columnCount(index); ++col) { 00237 QModelIndex colIndex = model->index(row, col, index); 00238 00239 if (colIndex == m_editingIndex) { 00240 itemActivated(m_editingIndex); //this will *close* the item's editor because it's already open 00241 } 00242 } 00243 } 00244 } 00245 00246 00247 //slot 00248 void KShortcutsEditorDelegate::hiddenBySearchLine(QTreeWidgetItem *item, bool hidden) 00249 { 00250 if (!hidden || !item) { 00251 return; 00252 } 00253 QTreeWidget *view = static_cast<QTreeWidget *>(parent()); 00254 QTreeWidgetItem *editingItem = KShortcutsEditorPrivate::itemFromIndex(view, m_editingIndex); 00255 if (editingItem == item) { 00256 itemActivated(m_editingIndex); //this will *close* the item's editor because it's already open 00257 } 00258 } 00259 00260 00261 bool KShortcutsEditorDelegate::eventFilter(QObject *o, QEvent *e) 00262 { 00263 if (o == m_editor) { 00264 //Prevent clicks in the empty part of the editor widget from closing the editor 00265 //because they would propagate to the itemview and be interpreted as a click in 00266 //an item's rect. That in turn would lead to an itemActivated() call, closing 00267 //the current editor. 00268 00269 switch (e->type()) { 00270 case QEvent::MouseButtonPress: 00271 case QEvent::MouseButtonRelease: 00272 case QEvent::MouseButtonDblClick: 00273 return true; 00274 default: 00275 return false; 00276 } 00277 } else if (o == parent()) { 00278 // Make left/right cursor keys switch items instead of operate the scroll bar 00279 // (subclassing QtreeView/Widget would be cleaner but much more of a hassle) 00280 // Note that in our case we know that the selection mode is SingleSelection, 00281 // so we don't have to ask QAbstractItemView::selectionCommand() et al. 00282 00283 if (e->type() != QEvent::KeyPress) { 00284 return false; 00285 } 00286 QKeyEvent *ke = static_cast<QKeyEvent *>(e); 00287 QTreeWidget *view = static_cast<QTreeWidget *>(parent()); 00288 QItemSelectionModel *selection = view->selectionModel(); 00289 QModelIndex index = selection->currentIndex(); 00290 00291 switch (ke->key()) { 00292 case Qt::Key_Space: 00293 case Qt::Key_Select: 00294 // we are not using the standard "open editor" mechanism of QAbstractItemView, 00295 // so let's emulate that here. 00296 itemActivated(index); 00297 return true; 00298 case Qt::Key_Left: 00299 index = index.sibling(index.row(), index.column() - 1); 00300 break; 00301 case Qt::Key_Right: 00302 index = index.sibling(index.row(), index.column() + 1); 00303 break; 00304 default: 00305 return false; 00306 } 00307 // a cursor key was pressed 00308 if (index.isValid()) { 00309 selection->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); 00310 //### using PositionAtCenter for now; 00311 // EnsureVisible has no effect which seems to be a bug. 00312 view->scrollTo(index, QAbstractItemView::PositionAtCenter); 00313 } 00314 return true; 00315 } 00316 return false; 00317 } 00318 00319 00320 //slot 00321 void KShortcutsEditorDelegate::keySequenceChanged(const QKeySequence &seq) 00322 { 00323 QVariant ret = QVariant::fromValue(seq); 00324 emit shortcutChanged(ret, m_editingIndex); 00325 } 00326 00327 00328 void KShortcutsEditorDelegate::setCheckActionCollections( 00329 const QList<KActionCollection*> checkActionCollections ) 00330 { 00331 m_checkActionCollections = checkActionCollections; 00332 } 00333 00334 //slot 00335 void KShortcutsEditorDelegate::shapeGestureChanged(const KShapeGesture &gest) 00336 { 00337 //this is somewhat verbose because the gesture types are not "built in" to QVariant 00338 QVariant ret = QVariant::fromValue(gest); 00339 emit shortcutChanged(ret, m_editingIndex); 00340 } 00341 00342 00343 //slot 00344 void KShortcutsEditorDelegate::rockerGestureChanged(const KRockerGesture &gest) 00345 { 00346 QVariant ret = QVariant::fromValue(gest); 00347 emit shortcutChanged(ret, m_editingIndex); 00348 } 00349
KDE 4.6 API Reference