Commit d2d1affa authored by Nikita Melnichenko's avatar Nikita Melnichenko

Added cycling through file name part selections when renaming files

Implemented a feature that allows cycling through various name
and extension selections in the file-renaming editor by consecutively
hitting the rename shortcut (F2 by default).

For simple names like "notes.txt", it will cycle through "notes.txt",
"notes" and "txt". For more complex names like "archive.tar.xz", it will
cycle through "archive.tar.xz", "archive", "archive.tar", "xz" and "tar.xz".
The feature will help users to quickly select part of the name they need
to rename.

In addition, fixed the warning appeared when consecutively hitting
the rename shortcut:
warning default unknown@0 # edit: editing failed

ADDED: Cycling through file name part selections when renaming files
GUI: Added cycling through file name part selections when renaming files
CCBUG: 328923

Discussion: !13
parent a4736254
......@@ -21,6 +21,7 @@
#include "krinterbriefview.h"
// QtCore
#include <QDebug>
#include <QDir>
#include <QHashIterator>
#include <QItemSelection>
......@@ -621,8 +622,21 @@ void KrInterBriefView::currentChanged(const QModelIndex & current, const QModelI
void KrInterBriefView::renameCurrentItem()
{
QModelIndex cIndex = currentIndex();
QModelIndex nameIndex = _model->index(cIndex.row(), KrViewProperties::Name);
QModelIndex nameIndex = _model->index(currentIndex().row(), KrViewProperties::Name);
// cycle through various text selections if we are in the editing mode already
if (state() == QAbstractItemView::EditingState) {
auto delegate = dynamic_cast<KrViewItemDelegate *>(itemDelegate(nameIndex));
if (!delegate) {
qWarning() << "KrInterView item delegate is not KrViewItemDelegate, selection is not updated";
return;
}
delegate->cycleEditorSelection();
return;
}
// create and show file name editor
edit(nameIndex);
updateEditorData();
update(nameIndex);
......
......@@ -24,6 +24,7 @@
// QtCore
#include <QDir>
#include <QHashIterator>
#include <QDebug>
// QtWidgets
#include <QApplication>
#include <QDirModel>
......@@ -260,8 +261,21 @@ bool KrInterDetailedView::event(QEvent * e)
void KrInterDetailedView::renameCurrentItem()
{
QModelIndex cIndex = currentIndex();
QModelIndex nameIndex = _model->index(cIndex.row(), KrViewProperties::Name);
QModelIndex nameIndex = _model->index(currentIndex().row(), KrViewProperties::Name);
// cycle through various text selections if we are in the editing mode already
if (state() == QAbstractItemView::EditingState) {
auto delegate = dynamic_cast<KrViewItemDelegate *>(itemDelegate(nameIndex));
if (!delegate) {
qWarning() << "KrInterView item delegate is not KrViewItemDelegate, selection is not updated";
return;
}
delegate->cycleEditorSelection();
return;
}
// create and show file name editor
edit(nameIndex);
updateEditorData();
update(nameIndex);
......
......@@ -25,6 +25,8 @@
#include "../listpanel.h"
#include "../krcolorcache.h"
// QtCore
#include <QDebug>
// QtGui
#include <QKeyEvent>
#include <QPainter>
......@@ -36,7 +38,7 @@
#include <KConfigCore/KSharedConfig>
KrViewItemDelegate::KrViewItemDelegate(QObject *parent) :
QItemDelegate(parent), _currentlyEdited(-1), _dontDraw(false) {}
QItemDelegate(parent), _currentlyEdited(-1), _dontDraw(false), _editor(nullptr) {}
void KrViewItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
......@@ -55,7 +57,8 @@ void KrViewItemDelegate::drawDisplay(QPainter * painter, const QStyleOptionViewI
QWidget * KrViewItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &sovi, const QModelIndex &index) const
{
_currentlyEdited = index.row();
return QItemDelegate::createEditor(parent, sovi, index);
_editor = QItemDelegate::createEditor(parent, sovi, index);
return _editor;
}
void KrViewItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
......@@ -109,7 +112,7 @@ bool KrViewItemDelegate::eventFilter(QObject *object, QEvent *event)
switch (dynamic_cast<QKeyEvent *>(event)->key()) {
case Qt::Key_Tab:
case Qt::Key_Backtab:
_currentlyEdited = -1;
onEditorClose();
emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
return true;
case Qt::Key_Enter:
......@@ -120,14 +123,14 @@ bool KrViewItemDelegate::eventFilter(QObject *object, QEvent *event)
event->accept();
emit commitData(editor);
emit closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
_currentlyEdited = -1;
onEditorClose();
return true;
}
return false;
case Qt::Key_Escape:
event->accept();
// don't commit data
_currentlyEdited = -1;
onEditorClose();
emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
break;
default:
......@@ -151,7 +154,7 @@ bool KrViewItemDelegate::eventFilter(QObject *object, QEvent *event)
&& !QApplication::activeModalWidget()->isAncestorOf(editor)
&& qobject_cast<QDialog*>(QApplication::activeModalWidget()))
return false;
_currentlyEdited = -1;
onEditorClose();
// manually set focus back to panel after rename canceled by focusing another window
ACTIVE_PANEL->gui->slotFocusOnMe();
emit closeEditor(editor, RevertModelCache);
......@@ -166,3 +169,93 @@ bool KrViewItemDelegate::eventFilter(QObject *object, QEvent *event)
}
return false;
}
//! Helper class to represent an editor selection
class EditorSelection : public QPair<int, int>
{
public:
EditorSelection(int start, int length) : QPair<int, int>(start, length) {}
int start() const { return first; }
int length() const { return second; }
};
//! Generate helpful file name selections: full name (always present), name candidates, extension candidates
static QList<EditorSelection> generateFileNameSelections(const QString &text)
{
auto selections = QList<EditorSelection>();
auto length = text.length();
auto parts = text.split('.');
// append full selection
selections.append(EditorSelection(0, length));
// append forward selections
int selectionLength = 0;
bool isFirstPart = true;
for (auto part : parts) {
// if the part is not the first one, we need to add one character to account for the dot
selectionLength += part.length() + !isFirstPart;
isFirstPart = false;
// if we reached the full length, don't add the selection, since it's a full selection
if (selectionLength == length)
break;
// don't add empty selections (could happen if the full name starts with a dot)
if (selectionLength > 0)
selections.append(EditorSelection(0, selectionLength));
}
// append backward selections
std::reverse(parts.begin(), parts.end());
selectionLength = 0;
isFirstPart = true;
for (auto part : parts) {
// if the part is not the first one, we need to add one character to account for the dot
selectionLength += part.length() + !isFirstPart;
isFirstPart = false;
// if we reached the full length, don't add the selection, since it's a full selection
if (selectionLength == length)
break;
// don't add empty selections (could happen if the full name ends with a dot)
if (selectionLength > 0)
selections.append(EditorSelection(length - selectionLength, selectionLength));
}
return selections;
}
void KrViewItemDelegate::cycleEditorSelection()
{
auto editor = qobject_cast<QLineEdit *>(_editor);
if (!editor) {
qWarning() << "Unable to cycle through editor selections due to a missing or unsupported type of item editor" << _editor;
return;
}
EditorSelection currentSelection(editor->selectionStart(), editor->selectionLength());
auto text = editor->text();
auto selections = generateFileNameSelections(text);
// try to find current selection in the list
int currentIndex = 0;
for (auto selection : selections) {
if (selection == currentSelection)
break;
currentIndex++;
}
// if we found current selection, pick the next in the cycle
auto selectionCount = selections.length();
if (currentIndex < selections.length())
currentIndex = (currentIndex + 1) % selectionCount;
// otherwise pick the first one - the full selection
else
currentIndex = 0;
// set the selection
auto selection = selections[currentIndex];
qDebug() << "setting selection" << selection << "index" << currentIndex;
editor->setSelection(selection.start(), selection.length());
}
......@@ -40,9 +40,20 @@ public:
const QModelIndex &index) const override;
bool eventFilter(QObject *object, QEvent *event) override;
/// Set the next file name selection in the editor.
void cycleEditorSelection();
private:
mutable int _currentlyEdited;
mutable bool _dontDraw;
mutable QWidget *_editor;
/// Init editor-related members when editor is closed.
void onEditorClose()
{
_currentlyEdited = -1;
_editor = nullptr;
}
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment