File indexing completed on 2024-05-12 17:22:03
0001 /* 0002 SPDX-FileCopyrightText: 2009 Csaba Karai <cskarai@freemail.hu> 0003 SPDX-FileCopyrightText: 2009-2022 Krusader Krew <https://krusader.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "krviewitemdelegate.h" 0009 0010 #include "../../compat.h" 0011 #include "../krcolorcache.h" 0012 #include "../krglobal.h" 0013 #include "../listpanel.h" 0014 #include "krviewproperties.h" 0015 0016 // QtCore 0017 #include <QDebug> 0018 // QtGui 0019 #include <QKeyEvent> 0020 #include <QPainter> 0021 // QtWidgets 0022 #include <QApplication> 0023 #include <QDialog> 0024 #include <QLineEdit> 0025 0026 #include <KConfigCore/KSharedConfig> 0027 0028 KrViewItemDelegate::KrViewItemDelegate(QObject *parent) 0029 : QItemDelegate(parent) 0030 , _currentlyEdited(-1) 0031 , _dontDraw(false) 0032 , _editor(nullptr) 0033 { 0034 } 0035 0036 void KrViewItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0037 { 0038 QStyleOptionViewItem opt = option; 0039 opt.state &= ~QStyle::State_Selected; 0040 _dontDraw = (_currentlyEdited == index.row()) && (index.column() == KrViewProperties::Ext); 0041 QItemDelegate::paint(painter, opt, index); 0042 } 0043 0044 void KrViewItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QString &text) const 0045 { 0046 if (!_dontDraw) 0047 QItemDelegate::drawDisplay(painter, option, rect, text); 0048 } 0049 0050 QWidget *KrViewItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &sovi, const QModelIndex &index) const 0051 { 0052 _currentlyEdited = index.row(); 0053 _editor = QItemDelegate::createEditor(parent, sovi, index); 0054 return _editor; 0055 } 0056 0057 void KrViewItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const 0058 { 0059 QItemDelegate::setEditorData(editor, index); 0060 auto *lineEdit = qobject_cast<QLineEdit *>(editor); 0061 if (lineEdit) { 0062 KConfigGroup gl(krConfig, "Look&Feel"); 0063 QFont font = index.data(Qt::FontRole).value<QFont>(); 0064 lineEdit->setFont(font); 0065 if (gl.readEntry("Rename Selects Extension", true)) 0066 lineEdit->selectAll(); 0067 else { 0068 QString nameWithoutExt = index.data(Qt::UserRole).toString(); 0069 lineEdit->deselect(); 0070 lineEdit->setSelection(0, nameWithoutExt.length()); 0071 } 0072 0073 KrColorSettings colorSettings; 0074 0075 if (!colorSettings.getBoolValue("KDE Default")) { 0076 QPalette renamePalette = lineEdit->palette(); 0077 0078 if (!colorSettings.getColorTextValue("Rename Foreground").isEmpty()) 0079 renamePalette.setColor(QPalette::Text, colorSettings.getColorValue("Rename Foreground")); 0080 0081 if (!colorSettings.getColorTextValue("Rename Background").isEmpty()) 0082 renamePalette.setColor(QPalette::Base, colorSettings.getColorValue("Rename Background")); 0083 0084 lineEdit->setPalette(renamePalette); 0085 } 0086 } 0087 } 0088 0089 QSize KrViewItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0090 { 0091 QSize size = QItemDelegate::sizeHint(option, index); 0092 if (size.isEmpty()) { 0093 // prevent items without text from bloating the view vertically 0094 return QSize(0, 0); 0095 } 0096 return size; 0097 } 0098 0099 bool KrViewItemDelegate::eventFilter(QObject *object, QEvent *event) 0100 { 0101 QWidget *editor = qobject_cast<QWidget *>(object); 0102 if (!editor) 0103 return false; 0104 if (event->type() == QEvent::KeyPress) { 0105 switch (dynamic_cast<QKeyEvent *>(event)->key()) { 0106 case Qt::Key_Tab: 0107 case Qt::Key_Backtab: 0108 onEditorClose(); 0109 emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache); 0110 return true; 0111 case Qt::Key_Enter: 0112 case Qt::Key_Return: 0113 if (auto *e = qobject_cast<QLineEdit *>(editor)) { 0114 if (!e->hasAcceptableInput()) 0115 return true; 0116 event->accept(); 0117 emit commitData(editor); 0118 emit closeEditor(editor, QAbstractItemDelegate::SubmitModelCache); 0119 onEditorClose(); 0120 return true; 0121 } 0122 return false; 0123 case Qt::Key_Escape: 0124 event->accept(); 0125 // don't commit data 0126 onEditorClose(); 0127 emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache); 0128 break; 0129 default: 0130 return false; 0131 } 0132 0133 if (editor->parentWidget()) 0134 editor->parentWidget()->setFocus(); 0135 return true; 0136 } else if (event->type() == QEvent::FocusOut) { 0137 if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) { 0138 QWidget *w = QApplication::focusWidget(); 0139 while (w) { // don't worry about focus changes internally in the editor 0140 if (w == editor) 0141 return false; 0142 w = w->parentWidget(); 0143 } 0144 // Opening a modal dialog will start a new eventloop 0145 // that will process the deleteLater event. 0146 if (QApplication::activeModalWidget() && !QApplication::activeModalWidget()->isAncestorOf(editor) 0147 && qobject_cast<QDialog *>(QApplication::activeModalWidget())) 0148 return false; 0149 onEditorClose(); 0150 // manually set focus back to panel after rename canceled by focusing another window 0151 ACTIVE_PANEL->gui->slotFocusOnMe(); 0152 emit closeEditor(editor, RevertModelCache); 0153 } 0154 } else if (event->type() == QEvent::ShortcutOverride) { 0155 const QKeyEvent *ke = dynamic_cast<QKeyEvent *>(event); 0156 if (ke->key() == Qt::Key_Escape || (ke->key() == Qt::Key_Backspace && ke->modifiers() == Qt::ControlModifier)) { 0157 event->accept(); 0158 return true; 0159 } 0160 } 0161 return false; 0162 } 0163 0164 //! Helper class to represent an editor selection 0165 class EditorSelection : public QPair<int, int> 0166 { 0167 public: 0168 EditorSelection(int start, int length) 0169 : QPair<int, int>(start, length) 0170 { 0171 } 0172 0173 int start() const 0174 { 0175 return first; 0176 } 0177 int length() const 0178 { 0179 return second; 0180 } 0181 }; 0182 0183 //! Generate helpful file name selections: full name (always present), name candidates, extension candidates 0184 static QList<EditorSelection> generateFileNameSelections(const QString &text) 0185 { 0186 auto selections = QList<EditorSelection>(); 0187 auto length = text.length(); 0188 auto parts = text.split('.'); 0189 0190 // append full selection 0191 selections.append(EditorSelection(0, length)); 0192 0193 // append forward selections 0194 int selectionLength = 0; 0195 bool isFirstPart = true; 0196 for (auto part : parts) { 0197 // if the part is not the first one, we need to add one character to account for the dot 0198 selectionLength += part.length() + !isFirstPart; 0199 isFirstPart = false; 0200 // if we reached the full length, don't add the selection, since it's a full selection 0201 if (selectionLength == length) 0202 break; 0203 0204 // don't add empty selections (could happen if the full name starts with a dot) 0205 if (selectionLength > 0) 0206 selections.append(EditorSelection(0, selectionLength)); 0207 } 0208 0209 // append backward selections 0210 std::reverse(parts.begin(), parts.end()); 0211 selectionLength = 0; 0212 isFirstPart = true; 0213 for (auto part : parts) { 0214 // if the part is not the first one, we need to add one character to account for the dot 0215 selectionLength += part.length() + !isFirstPart; 0216 isFirstPart = false; 0217 // if we reached the full length, don't add the selection, since it's a full selection 0218 if (selectionLength == length) 0219 break; 0220 0221 // don't add empty selections (could happen if the full name ends with a dot) 0222 if (selectionLength > 0) 0223 selections.append(EditorSelection(length - selectionLength, selectionLength)); 0224 } 0225 0226 return selections; 0227 } 0228 0229 void KrViewItemDelegate::cycleEditorSelection() 0230 { 0231 auto editor = qobject_cast<QLineEdit *>(_editor); 0232 if (!editor) { 0233 qWarning() << "Unable to cycle through editor selections due to a missing or unsupported type of item editor" << _editor; 0234 return; 0235 } 0236 0237 EditorSelection currentSelection(editor->selectionStart(), editor->selectionLength()); 0238 auto text = editor->text(); 0239 const auto selections = generateFileNameSelections(text); 0240 0241 // try to find current selection in the list 0242 int currentIndex = 0; 0243 for (auto selection : selections) { 0244 if (selection == currentSelection) 0245 break; 0246 currentIndex++; 0247 } 0248 0249 // if we found current selection, pick the next in the cycle 0250 auto selectionCount = selections.length(); 0251 if (currentIndex < selections.length()) 0252 currentIndex = (currentIndex + 1) % selectionCount; 0253 // otherwise pick the first one - the full selection 0254 else 0255 currentIndex = 0; 0256 0257 // set the selection 0258 auto selection = selections[currentIndex]; 0259 qDebug() << "setting selection" << selection << "index" << currentIndex; 0260 editor->setSelection(selection.start(), selection.length()); 0261 }