mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-22 10:00:33 +00:00
Generalise the deletion protection mechanism from math to text (#9540)
Now backspace and delete in text will select non-empty math and text insets before deleting them. This is consistent with what happens in math already. This is implemented for InsetText as well but can be disabled in case of negative feedback. This can be set for any sort of inset with the new virtual method Inset::confirmDeletion. New option "force" for the LFUN_*_DELETE_* commands, that bypasses the confirmDeletion check.
This commit is contained in:
parent
ffacdd8b46
commit
71623b88b2
@ -551,24 +551,6 @@ void Cursor::checkNewWordPosition()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Cursor::posBackward()
|
|
||||||
{
|
|
||||||
if (pos() == 0)
|
|
||||||
return false;
|
|
||||||
--pos();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Cursor::posForward()
|
|
||||||
{
|
|
||||||
if (pos() == lastpos())
|
|
||||||
return false;
|
|
||||||
++pos();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Cursor::posVisRight(bool skip_inset)
|
bool Cursor::posVisRight(bool skip_inset)
|
||||||
{
|
{
|
||||||
Cursor new_cur = *this; // where we will move to
|
Cursor new_cur = *this; // where we will move to
|
||||||
@ -1301,7 +1283,7 @@ void Cursor::insert(MathData const & ar)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Cursor::backspace()
|
bool Cursor::backspace(bool const force)
|
||||||
{
|
{
|
||||||
if (selection()) {
|
if (selection()) {
|
||||||
cap::eraseSelection(*this);
|
cap::eraseSelection(*this);
|
||||||
@ -1337,7 +1319,7 @@ bool Cursor::backspace()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos() != 0 && prevAtom()->nargs() > 0) {
|
if (pos() != 0 && !force && prevAtom()->confirmDeletion()) {
|
||||||
// let's require two backspaces for 'big stuff' and
|
// let's require two backspaces for 'big stuff' and
|
||||||
// highlight on the first
|
// highlight on the first
|
||||||
resetAnchor();
|
resetAnchor();
|
||||||
@ -1351,7 +1333,7 @@ bool Cursor::backspace()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Cursor::erase()
|
bool Cursor::erase(bool const force)
|
||||||
{
|
{
|
||||||
if (inMacroMode())
|
if (inMacroMode())
|
||||||
return true;
|
return true;
|
||||||
@ -1386,7 +1368,7 @@ bool Cursor::erase()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 'clever' UI hack: only erase large items if previously slected
|
// 'clever' UI hack: only erase large items if previously slected
|
||||||
if (pos() != lastpos() && nextAtom()->nargs() > 0) {
|
if (pos() != lastpos() && !force && nextAtom()->confirmDeletion()) {
|
||||||
resetAnchor();
|
resetAnchor();
|
||||||
selection(true);
|
selection(true);
|
||||||
++pos();
|
++pos();
|
||||||
@ -2436,4 +2418,21 @@ void Cursor::checkBufferStructure()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Cursor::confirmDeletion(bool const before) const
|
||||||
|
{
|
||||||
|
if (!selection()) {
|
||||||
|
if (Inset const * inset = before ? prevInset() : nextInset())
|
||||||
|
return inset->confirmDeletion();
|
||||||
|
} else {
|
||||||
|
DocIterator dit = selectionBegin();
|
||||||
|
DocIterator const sel_end = selectionEnd();
|
||||||
|
for (; dit < sel_end; dit.posForward())
|
||||||
|
if (Inset const * inset = dit.nextInset())
|
||||||
|
if (inset->confirmDeletion())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace lyx
|
} // namespace lyx
|
||||||
|
16
src/Cursor.h
16
src/Cursor.h
@ -232,10 +232,6 @@ public:
|
|||||||
//
|
//
|
||||||
// common part
|
// common part
|
||||||
//
|
//
|
||||||
/// move one step backwards
|
|
||||||
bool posBackward();
|
|
||||||
/// move one step forward
|
|
||||||
bool posForward();
|
|
||||||
/// move visually one step to the right
|
/// move visually one step to the right
|
||||||
/**
|
/**
|
||||||
* @note: This method may move into an inset unless skip_inset == true.
|
* @note: This method may move into an inset unless skip_inset == true.
|
||||||
@ -398,6 +394,11 @@ public:
|
|||||||
/// and after leaving the word the result is empty.
|
/// and after leaving the word the result is empty.
|
||||||
DocIterator newWord() const { return new_word_; }
|
DocIterator newWord() const { return new_word_; }
|
||||||
|
|
||||||
|
/// Return true if the next or previous inset has confirmDeletion depending
|
||||||
|
/// on the boolean before. If there is a selection, return true if at least
|
||||||
|
/// one inset in the selection has confirmDeletion.
|
||||||
|
bool confirmDeletion(bool before = false) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//private:
|
//private:
|
||||||
|
|
||||||
@ -454,9 +455,10 @@ public:
|
|||||||
///
|
///
|
||||||
void insert(MathData const &);
|
void insert(MathData const &);
|
||||||
/// return false for empty math insets
|
/// return false for empty math insets
|
||||||
bool erase();
|
/// Use force to skip the confirmDeletion check.
|
||||||
/// return false for empty math insets
|
bool erase(bool force = false);
|
||||||
bool backspace();
|
bool backspace(bool force = false);
|
||||||
|
|
||||||
/// move the cursor up by sending an internal LFUN_UP
|
/// move the cursor up by sending an internal LFUN_UP
|
||||||
/// return true if fullscreen update is needed
|
/// return true if fullscreen update is needed
|
||||||
bool up();
|
bool up();
|
||||||
|
@ -318,6 +318,24 @@ Inset * DocIterator::innerInsetOfType(int code) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DocIterator::posBackward()
|
||||||
|
{
|
||||||
|
if (pos() == 0)
|
||||||
|
return false;
|
||||||
|
--pos();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DocIterator::posForward()
|
||||||
|
{
|
||||||
|
if (pos() == lastpos())
|
||||||
|
return false;
|
||||||
|
++pos();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// This duplicates code above, but is in the critical path.
|
// This duplicates code above, but is in the critical path.
|
||||||
// So please think twice before adding stuff
|
// So please think twice before adding stuff
|
||||||
void DocIterator::forwardPos()
|
void DocIterator::forwardPos()
|
||||||
|
@ -184,6 +184,10 @@ public:
|
|||||||
//
|
//
|
||||||
// elementary moving
|
// elementary moving
|
||||||
//
|
//
|
||||||
|
/// move one step backwards
|
||||||
|
bool posBackward();
|
||||||
|
/// move one step forward
|
||||||
|
bool posForward();
|
||||||
/**
|
/**
|
||||||
* move on one logical position, descend into nested insets
|
* move on one logical position, descend into nested insets
|
||||||
* including collapsed insets
|
* including collapsed insets
|
||||||
|
@ -1080,7 +1080,8 @@ void LyXAction::init()
|
|||||||
/*!
|
/*!
|
||||||
* \var lyx::FuncCode lyx::LFUN_CHAR_DELETE_BACKWARD
|
* \var lyx::FuncCode lyx::LFUN_CHAR_DELETE_BACKWARD
|
||||||
* \li Action: Deletes one character in the backward direction (usually the "BackSpace" key).
|
* \li Action: Deletes one character in the backward direction (usually the "BackSpace" key).
|
||||||
* \li Syntax: char-delete-backward
|
* \li Syntax: char-delete-backward [force]
|
||||||
|
* \li Params: force: Delete big insets, do no only select them.
|
||||||
* \endvar
|
* \endvar
|
||||||
*/
|
*/
|
||||||
{ LFUN_CHAR_DELETE_BACKWARD, "char-delete-backward", SingleParUpdate, Edit },
|
{ LFUN_CHAR_DELETE_BACKWARD, "char-delete-backward", SingleParUpdate, Edit },
|
||||||
@ -1088,7 +1089,8 @@ void LyXAction::init()
|
|||||||
/*!
|
/*!
|
||||||
* \var lyx::FuncCode lyx::LFUN_CHAR_DELETE_FORWARD
|
* \var lyx::FuncCode lyx::LFUN_CHAR_DELETE_FORWARD
|
||||||
* \li Action: Deletes one character in the backward direction (usually the "Delete" key).
|
* \li Action: Deletes one character in the backward direction (usually the "Delete" key).
|
||||||
* \li Syntax: char-delete-forward
|
* \li Syntax: char-delete-forward [force]
|
||||||
|
* \li Params: force: Delete big insets, do no only select them.
|
||||||
* \endvar
|
* \endvar
|
||||||
*/
|
*/
|
||||||
{ LFUN_CHAR_DELETE_FORWARD, "char-delete-forward", SingleParUpdate, Edit },
|
{ LFUN_CHAR_DELETE_FORWARD, "char-delete-forward", SingleParUpdate, Edit },
|
||||||
@ -4007,7 +4009,8 @@ void LyXAction::init()
|
|||||||
/*!
|
/*!
|
||||||
* \var lyx::FuncCode lyx::LFUN_WORD_DELETE_BACKWARD
|
* \var lyx::FuncCode lyx::LFUN_WORD_DELETE_BACKWARD
|
||||||
* \li Action: Deletes characters to the beginning of the word (usually the "C+BackSpace" key).
|
* \li Action: Deletes characters to the beginning of the word (usually the "C+BackSpace" key).
|
||||||
* \li Syntax: word-delete-backward
|
* \li Syntax: word-delete-backward [force]
|
||||||
|
* \li Params: force: Delete big insets, do no only select them.
|
||||||
* \endvar
|
* \endvar
|
||||||
*/
|
*/
|
||||||
{ LFUN_WORD_DELETE_BACKWARD, "word-delete-backward", Noop, Edit },
|
{ LFUN_WORD_DELETE_BACKWARD, "word-delete-backward", Noop, Edit },
|
||||||
@ -4015,7 +4018,8 @@ void LyXAction::init()
|
|||||||
/*!
|
/*!
|
||||||
* \var lyx::FuncCode lyx::LFUN_WORD_DELETE_FORWARD
|
* \var lyx::FuncCode lyx::LFUN_WORD_DELETE_FORWARD
|
||||||
* \li Action: Deletes characters to the end of the word (usually the "C+Delete" key).
|
* \li Action: Deletes characters to the end of the word (usually the "C+Delete" key).
|
||||||
* \li Syntax: word-delete-forward
|
* \li Syntax: word-delete-forward [force]
|
||||||
|
* \li Params: force: Delete big insets, do no only select them.
|
||||||
* \endvar
|
* \endvar
|
||||||
*/
|
*/
|
||||||
{ LFUN_WORD_DELETE_FORWARD, "word-delete-forward", Noop, Edit },
|
{ LFUN_WORD_DELETE_FORWARD, "word-delete-forward", Noop, Edit },
|
||||||
|
16
src/Text.cpp
16
src/Text.cpp
@ -1466,7 +1466,7 @@ void Text::rejectChanges()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Text::deleteWordForward(Cursor & cur)
|
void Text::deleteWordForward(Cursor & cur, bool const force)
|
||||||
{
|
{
|
||||||
LBUFERR(this == cur.text());
|
LBUFERR(this == cur.text());
|
||||||
if (cur.lastpos() == 0)
|
if (cur.lastpos() == 0)
|
||||||
@ -1476,13 +1476,15 @@ void Text::deleteWordForward(Cursor & cur)
|
|||||||
cur.selection(true);
|
cur.selection(true);
|
||||||
cursorForwardOneWord(cur);
|
cursorForwardOneWord(cur);
|
||||||
cur.setSelection();
|
cur.setSelection();
|
||||||
cutSelection(cur, true, false);
|
if (force || !cur.confirmDeletion()) {
|
||||||
cur.checkBufferStructure();
|
cutSelection(cur, true, false);
|
||||||
|
cur.checkBufferStructure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Text::deleteWordBackward(Cursor & cur)
|
void Text::deleteWordBackward(Cursor & cur, bool const force)
|
||||||
{
|
{
|
||||||
LBUFERR(this == cur.text());
|
LBUFERR(this == cur.text());
|
||||||
if (cur.lastpos() == 0)
|
if (cur.lastpos() == 0)
|
||||||
@ -1492,8 +1494,10 @@ void Text::deleteWordBackward(Cursor & cur)
|
|||||||
cur.selection(true);
|
cur.selection(true);
|
||||||
cursorBackwardOneWord(cur);
|
cursorBackwardOneWord(cur);
|
||||||
cur.setSelection();
|
cur.setSelection();
|
||||||
cutSelection(cur, true, false);
|
if (force || !cur.confirmDeletion()) {
|
||||||
cur.checkBufferStructure();
|
cutSelection(cur, true, false);
|
||||||
|
cur.checkBufferStructure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,9 +223,10 @@ public:
|
|||||||
///
|
///
|
||||||
bool cursorVisRightOneWord(Cursor & cur);
|
bool cursorVisRightOneWord(Cursor & cur);
|
||||||
/// Delete from cursor up to the end of the current or next word.
|
/// Delete from cursor up to the end of the current or next word.
|
||||||
void deleteWordForward(Cursor & cur);
|
/// Use force to skip the confirmDeletion check.
|
||||||
|
void deleteWordForward(Cursor & cur, bool force = false);
|
||||||
/// Delete from cursor to start of current or prior word.
|
/// Delete from cursor to start of current or prior word.
|
||||||
void deleteWordBackward(Cursor & cur);
|
void deleteWordBackward(Cursor & cur, bool force = false);
|
||||||
///
|
///
|
||||||
bool cursorUpParagraph(Cursor & cur);
|
bool cursorUpParagraph(Cursor & cur);
|
||||||
///
|
///
|
||||||
|
@ -586,7 +586,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
|||||||
if (cur.selection())
|
if (cur.selection())
|
||||||
cutSelection(cur, true, false);
|
cutSelection(cur, true, false);
|
||||||
else
|
else
|
||||||
deleteWordForward(cur);
|
deleteWordForward(cur, cmd.getArg(0) == "force");
|
||||||
finishChange(cur, false);
|
finishChange(cur, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -594,7 +594,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
|||||||
if (cur.selection())
|
if (cur.selection())
|
||||||
cutSelection(cur, true, false);
|
cutSelection(cur, true, false);
|
||||||
else
|
else
|
||||||
deleteWordBackward(cur);
|
deleteWordBackward(cur, cmd.getArg(0) == "force");
|
||||||
finishChange(cur, false);
|
finishChange(cur, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1054,6 +1054,13 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
|||||||
if (cur.pos() == cur.paragraph().size())
|
if (cur.pos() == cur.paragraph().size())
|
||||||
// Par boundary, force full-screen update
|
// Par boundary, force full-screen update
|
||||||
singleParUpdate = false;
|
singleParUpdate = false;
|
||||||
|
else if (cmd.getArg(0) != "force" && cur.confirmDeletion()) {
|
||||||
|
cur.resetAnchor();
|
||||||
|
cur.selection(true);
|
||||||
|
cur.posForward();
|
||||||
|
cur.setSelection();
|
||||||
|
break;
|
||||||
|
}
|
||||||
needsUpdate |= erase(cur);
|
needsUpdate |= erase(cur);
|
||||||
cur.resetAnchor();
|
cur.resetAnchor();
|
||||||
} else {
|
} else {
|
||||||
@ -1071,6 +1078,13 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
|
|||||||
// Par boundary, full-screen update
|
// Par boundary, full-screen update
|
||||||
if (par_boundary)
|
if (par_boundary)
|
||||||
singleParUpdate = false;
|
singleParUpdate = false;
|
||||||
|
else if (cmd.getArg(0) != "force" && cur.confirmDeletion(true)) {
|
||||||
|
cur.resetAnchor();
|
||||||
|
cur.selection(true);
|
||||||
|
cur.posBackward();
|
||||||
|
cur.setSelection();
|
||||||
|
break;
|
||||||
|
}
|
||||||
needsUpdate |= backspace(cur);
|
needsUpdate |= backspace(cur);
|
||||||
cur.resetAnchor();
|
cur.resetAnchor();
|
||||||
if (par_boundary && !first_par && cur.pos() > 0
|
if (par_boundary && !first_par && cur.pos() > 0
|
||||||
|
@ -581,6 +581,10 @@ public:
|
|||||||
//
|
//
|
||||||
enum { TEXT_TO_INSET_OFFSET = 4 };
|
enum { TEXT_TO_INSET_OFFSET = 4 };
|
||||||
|
|
||||||
|
/// Determine the action of backspace and delete: do we select instead of
|
||||||
|
/// deleting if not already selected?
|
||||||
|
virtual bool confirmDeletion() const { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Constructors
|
/// Constructors
|
||||||
Inset(Buffer * buf) : buffer_(buf) {}
|
Inset(Buffer * buf) : buffer_(buf) {}
|
||||||
|
@ -219,6 +219,10 @@ public:
|
|||||||
std::string contextMenuName() const;
|
std::string contextMenuName() const;
|
||||||
///
|
///
|
||||||
void doDispatch(Cursor & cur, FuncRequest & cmd);
|
void doDispatch(Cursor & cur, FuncRequest & cmd);
|
||||||
|
|
||||||
|
///
|
||||||
|
bool confirmDeletion() const { return !text().empty(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
///
|
///
|
||||||
void iterateForToc(DocIterator const & cdit, bool output_active,
|
void iterateForToc(DocIterator const & cdit, bool output_active,
|
||||||
|
@ -189,6 +189,8 @@ public:
|
|||||||
InsetCode lyxCode() const { return MATH_HULL_CODE; }
|
InsetCode lyxCode() const { return MATH_HULL_CODE; }
|
||||||
///
|
///
|
||||||
bool canPaintChange(BufferView const &) const;
|
bool canPaintChange(BufferView const &) const;
|
||||||
|
///
|
||||||
|
bool confirmDeletion() const { return nargs() != 1 || !cell(0).empty(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
InsetMathHull(InsetMathHull const &);
|
InsetMathHull(InsetMathHull const &);
|
||||||
|
@ -862,8 +862,8 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
|
|||||||
else if (!cur.inMacroMode())
|
else if (!cur.inMacroMode())
|
||||||
cur.recordUndoSelection();
|
cur.recordUndoSelection();
|
||||||
// if the inset can not be removed from within, delete it
|
// if the inset can not be removed from within, delete it
|
||||||
if (!cur.backspace()) {
|
if (!cur.backspace(cmd.getArg(0) == "force")) {
|
||||||
FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
|
FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD, "force");
|
||||||
cur.innerText()->dispatch(cur, cmd);
|
cur.innerText()->dispatch(cur, cmd);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -876,8 +876,8 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
|
|||||||
else
|
else
|
||||||
cur.recordUndoSelection();
|
cur.recordUndoSelection();
|
||||||
// if the inset can not be removed from within, delete it
|
// if the inset can not be removed from within, delete it
|
||||||
if (!cur.erase()) {
|
if (!cur.erase(cmd.getArg(0) == "force")) {
|
||||||
FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
|
FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD, "force");
|
||||||
cur.innerText()->dispatch(cur, cmd);
|
cur.innerText()->dispatch(cur, cmd);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -131,6 +131,9 @@ public:
|
|||||||
///
|
///
|
||||||
InsetCode lyxCode() const { return MATH_NEST_CODE; }
|
InsetCode lyxCode() const { return MATH_NEST_CODE; }
|
||||||
|
|
||||||
|
///
|
||||||
|
bool confirmDeletion() const { return nargs() > 0; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
///
|
///
|
||||||
InsetMathNest(InsetMathNest const & inset);
|
InsetMathNest(InsetMathNest const & inset);
|
||||||
|
Loading…
Reference in New Issue
Block a user