Local undo per Fl_Text_Buffer and Fl_Input_ (#557)

This commit is contained in:
Matthias Melcher
2022-11-26 20:28:12 +01:00
committed by GitHub
parent 191aeefc4a
commit 87fe29372c
4 changed files with 171 additions and 121 deletions
+5
View File
@@ -38,6 +38,8 @@
#define FL_MULTILINE_INPUT_WRAP (FL_MULTILINE_INPUT | FL_INPUT_WRAP) #define FL_MULTILINE_INPUT_WRAP (FL_MULTILINE_INPUT | FL_INPUT_WRAP)
#define FL_MULTILINE_OUTPUT_WRAP (FL_MULTILINE_INPUT | FL_INPUT_READONLY | FL_INPUT_WRAP) #define FL_MULTILINE_OUTPUT_WRAP (FL_MULTILINE_INPUT | FL_INPUT_READONLY | FL_INPUT_WRAP)
class Fl_Input_Undo_Action;
/** /**
This class provides a low-overhead text input field. This class provides a low-overhead text input field.
@@ -144,6 +146,9 @@ class FL_EXPORT Fl_Input_ : public Fl_Widget {
/** \internal color of the text cursor */ /** \internal color of the text cursor */
Fl_Color cursor_color_; Fl_Color cursor_color_;
/** \internal local undo event */
Fl_Input_Undo_Action* undo_;
/** \internal Horizontal cursor position in pixels while moving up or down. */ /** \internal Horizontal cursor position in pixels while moving up or down. */
static double up_down_pos; static double up_down_pos;
+2
View File
@@ -60,6 +60,7 @@
#include "Fl_Export.H" #include "Fl_Export.H"
class Fl_Text_Undo_Action;
/** /**
\class Fl_Text_Selection \class Fl_Text_Selection
@@ -836,6 +837,7 @@ protected:
int mPreferredGapSize; /**< the default allocation for the text gap is 1024 int mPreferredGapSize; /**< the default allocation for the text gap is 1024
bytes and should only be increased if frequent bytes and should only be increased if frequent
and large changes in buffer size are expected */ and large changes in buffer size are expected */
Fl_Text_Undo_Action* mUndo; /**< local undo event */
}; };
#endif #endif
+78 -60
View File
@@ -33,6 +33,45 @@ extern void fl_draw(const char*, int, float, float);
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
class Fl_Input_Undo_Action {
public:
Fl_Input_Undo_Action() :
undobuffer(NULL),
undobufferlength(0),
undoat(0),
undocut(0),
undoinsert(0),
undoyankcut(0)
{ }
~Fl_Input_Undo_Action() {
if (undobuffer)
::free(undobuffer);
}
char *undobuffer;
int undobufferlength;
int undoat; // points after insertion
int undocut; // number of characters deleted there
int undoinsert; // number of characters inserted
int undoyankcut; // length of valid contents of buffer, even if undocut=0
/*
Resize the undo buffer to match at least the requested size.
*/
void undobuffersize(int n)
{
if (n > undobufferlength) {
undobufferlength = n + 128;
undobuffer = (char *)realloc(undobuffer, undobufferlength);
}
}
void clear() {
undocut = undoinsert = 0;
}
};
/** \internal /** \internal
Converts a given text segment into the text that will be rendered on screen. Converts a given text segment into the text that will be rendered on screen.
@@ -723,26 +762,6 @@ int Fl_Input_::copy(int clipboard) {
#define MAXFLOATSIZE 40 #define MAXFLOATSIZE 40
static char* undobuffer;
static int undobufferlength;
static Fl_Input_* undowidget;
static int undoat; // points after insertion
static int undocut; // number of characters deleted there
static int undoinsert; // number of characters inserted
static int yankcut; // length of valid contents of buffer, even if undocut=0
static void undobuffersize(int n) {
if (n > undobufferlength) {
if (undobuffer) {
do {undobufferlength *= 2;} while (undobufferlength < n);
undobuffer = (char*)realloc(undobuffer, undobufferlength);
} else {
undobufferlength = n+9;
undobuffer = (char*)malloc(undobufferlength);
}
}
}
/** /**
Append text at the end. Append text at the end.
@@ -860,45 +879,43 @@ int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
put_in_buffer(size_+ilen); put_in_buffer(size_+ilen);
if (e>b) { if (e>b) {
if (undowidget == this && b == undoat) { if (b == undo_->undoat) {
undobuffersize(undocut+(e-b)); undo_->undobuffersize(undo_->undocut+(e-b));
memcpy(undobuffer+undocut, value_+b, e-b); memcpy(undo_->undobuffer+undo_->undocut, value_+b, e-b);
undocut += e-b; undo_->undocut += e-b;
} else if (undowidget == this && e == undoat && !undoinsert) { } else if (e == undo_->undoat && !undo_->undoinsert) {
undobuffersize(undocut+(e-b)); undo_->undobuffersize(undo_->undocut+(e-b));
memmove(undobuffer+(e-b), undobuffer, undocut); memmove(undo_->undobuffer+(e-b), undo_->undobuffer, undo_->undocut);
memcpy(undobuffer, value_+b, e-b); memcpy(undo_->undobuffer, value_+b, e-b);
undocut += e-b; undo_->undocut += e-b;
} else if (undowidget == this && e == undoat && (e-b)<undoinsert) { } else if (e == undo_->undoat && (e-b)<undo_->undoinsert) {
undoinsert -= e-b; undo_->undoinsert -= e-b;
} else { } else {
undobuffersize(e-b); undo_->undobuffersize(e-b);
memcpy(undobuffer, value_+b, e-b); memcpy(undo_->undobuffer, value_+b, e-b);
undocut = e-b; undo_->undocut = e-b;
undoinsert = 0; undo_->undoinsert = 0;
} }
memmove(buffer+b, buffer+e, size_-e+1); memmove(buffer+b, buffer+e, size_-e+1);
size_ -= e-b; size_ -= e-b;
undowidget = this; undo_->undoat = b;
undoat = b; if (input_type() == FL_SECRET_INPUT) undo_->undoyankcut = 0; else undo_->undoyankcut = undo_->undocut;
if (input_type() == FL_SECRET_INPUT) yankcut = 0; else yankcut = undocut;
} }
if (ilen) { if (ilen) {
if (undowidget == this && b == undoat) if (b == undo_->undoat)
undoinsert += ilen; undo_->undoinsert += ilen;
else { else {
undocut = 0; undo_->undocut = 0;
undoinsert = ilen; undo_->undoinsert = ilen;
} }
memmove(buffer+b+ilen, buffer+b, size_-b+1); memmove(buffer+b+ilen, buffer+b, size_-b+1);
memcpy(buffer+b, text, ilen); memcpy(buffer+b, text, ilen);
size_ += ilen; size_ += ilen;
} }
undowidget = this;
om = mark_; om = mark_;
op = position_; op = position_;
mark_ = position_ = undoat = b+ilen; mark_ = position_ = undo_->undoat = b+ilen;
// Insertions into the word at the end of the line will cause it to // Insertions into the word at the end of the line will cause it to
// wrap to the next line, so we must indicate that the changes may start // wrap to the next line, so we must indicate that the changes may start
@@ -922,7 +939,7 @@ int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
minimal_update(b); minimal_update(b);
mark_ = position_ = undoat; mark_ = position_ = undo_->undoat;
set_changed(); set_changed();
if (when()&FL_WHEN_CHANGED) do_callback(); if (when()&FL_WHEN_CHANGED) do_callback();
@@ -938,33 +955,33 @@ int Fl_Input_::replace(int b, int e, const char* text, int ilen) {
*/ */
int Fl_Input_::undo() { int Fl_Input_::undo() {
was_up_down = 0; was_up_down = 0;
if ( undowidget != this || (!undocut && !undoinsert) ) return 0; if (!undo_->undocut && !undo_->undoinsert) return 0;
int ilen = undocut; int ilen = undo_->undocut;
int xlen = undoinsert; int xlen = undo_->undoinsert;
int b = undoat-xlen; int b = undo_->undoat-xlen;
int b1 = b; int b1 = b;
put_in_buffer(size_+ilen); put_in_buffer(size_+ilen);
if (ilen) { if (ilen) {
memmove(buffer+b+ilen, buffer+b, size_-b+1); memmove(buffer+b+ilen, buffer+b, size_-b+1);
memcpy(buffer+b, undobuffer, ilen); memcpy(buffer+b, undo_->undobuffer, ilen);
size_ += ilen; size_ += ilen;
b += ilen; b += ilen;
} }
if (xlen) { if (xlen) {
undobuffersize(xlen); undo_->undobuffersize(xlen);
memcpy(undobuffer, buffer+b, xlen); memcpy(undo_->undobuffer, buffer+b, xlen);
memmove(buffer+b, buffer+b+xlen, size_-xlen-b+1); memmove(buffer+b, buffer+b+xlen, size_-xlen-b+1);
size_ -= xlen; size_ -= xlen;
} }
undocut = xlen; undo_->undocut = xlen;
if (xlen) yankcut = xlen; if (xlen) undo_->undoyankcut = xlen;
undoinsert = ilen; undo_->undoinsert = ilen;
undoat = b; undo_->undoat = b;
mark_ = b /* -ilen */; mark_ = b /* -ilen */;
position_ = b; position_ = b;
@@ -988,8 +1005,8 @@ int Fl_Input_::undo() {
*/ */
int Fl_Input_::copy_cuts() { int Fl_Input_::copy_cuts() {
// put the yank buffer into the X clipboard // put the yank buffer into the X clipboard
if (!yankcut || input_type()==FL_SECRET_INPUT) return 0; if (!undo_->undoyankcut || input_type()==FL_SECRET_INPUT) return 0;
Fl::copy(undobuffer, yankcut, 1); Fl::copy(undo_->undobuffer, undo_->undoyankcut, 1);
return 1; return 1;
} }
@@ -1151,6 +1168,7 @@ Fl_Input_::Fl_Input_(int X, int Y, int W, int H, const char* l)
xscroll_ = yscroll_ = 0; xscroll_ = yscroll_ = 0;
maximum_size_ = 32767; maximum_size_ = 32767;
shortcut_ = 0; shortcut_ = 0;
undo_ = new Fl_Input_Undo_Action();
set_flag(SHORTCUT_LABEL); set_flag(SHORTCUT_LABEL);
set_flag(MAC_USE_ACCENTS_MENU); set_flag(MAC_USE_ACCENTS_MENU);
set_flag(NEEDS_KEYBOARD); set_flag(NEEDS_KEYBOARD);
@@ -1217,7 +1235,7 @@ void Fl_Input_::put_in_buffer(int len) {
*/ */
int Fl_Input_::static_value(const char* str, int len) { int Fl_Input_::static_value(const char* str, int len) {
clear_changed(); clear_changed();
if (undowidget == this) undowidget = 0; undo_->clear();
if (str == value_ && len == size_) return 0; if (str == value_ && len == size_) return 0;
if (len) { // non-empty new value: if (len) { // non-empty new value:
if (xscroll_ || yscroll_) { if (xscroll_ || yscroll_) {
@@ -1318,7 +1336,7 @@ void Fl_Input_::resize(int X, int Y, int W, int H) {
from the parent Fl_Group. from the parent Fl_Group.
*/ */
Fl_Input_::~Fl_Input_() { Fl_Input_::~Fl_Input_() {
if (undowidget == this) undowidget = 0; delete undo_;
if (bufsize) free((void*)buffer); if (bufsize) free((void*)buffer);
} }
+86 -61
View File
@@ -66,32 +66,44 @@ static int min(int i1, int i2)
#endif #endif
class Fl_Text_Undo_Action {
public:
Fl_Text_Undo_Action() :
undobuffer(NULL),
undobufferlength(0),
undoat(0),
undocut(0),
undoinsert(0),
undoyankcut(0)
{ }
~Fl_Text_Undo_Action() {
if (undobuffer)
::free(undobuffer);
}
static char *undobuffer; char *undobuffer;
static int undobufferlength; int undobufferlength;
static Fl_Text_Buffer *undowidget; int undoat; // points after insertion
static int undoat; // points after insertion int undocut; // number of characters deleted there
static int undocut; // number of characters deleted there int undoinsert; // number of characters inserted
static int undoinsert; // number of characters inserted int undoyankcut; // length of valid contents of buffer, even if undocut=0
static int undoyankcut; // length of valid contents of buffer, even if undocut=0
/* /*
Resize the undo buffer to match at least the requested size. Resize the undo buffer to match at least the requested size.
*/ */
static void undobuffersize(int n) void undobuffersize(int n)
{ {
if (n > undobufferlength) { if (n > undobufferlength) {
if (undobuffer) { undobufferlength = n + 128;
do { undobuffer = (char *)realloc(undobuffer, undobufferlength);
undobufferlength *= 2;
} while (undobufferlength < n);
undobuffer = (char *) realloc(undobuffer, undobufferlength);
} else {
undobufferlength = n + 9;
undobuffer = (char *) malloc(undobufferlength);
} }
} }
}
void clear() {
undocut = undoinsert = 0;
}
};
static void def_transcoding_warning_action(Fl_Text_Buffer *text) static void def_transcoding_warning_action(Fl_Text_Buffer *text)
{ {
@@ -123,6 +135,7 @@ Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize)
mPredeleteCbArgs = NULL; mPredeleteCbArgs = NULL;
mCursorPosHint = 0; mCursorPosHint = 0;
mCanUndo = 1; mCanUndo = 1;
mUndo = new Fl_Text_Undo_Action();
input_file_was_transcoded = 0; input_file_was_transcoded = 0;
transcoding_warning_action = def_transcoding_warning_action; transcoding_warning_action = def_transcoding_warning_action;
} }
@@ -142,6 +155,7 @@ Fl_Text_Buffer::~Fl_Text_Buffer()
delete[] mPredeleteProcs; delete[] mPredeleteProcs;
delete[] mPredeleteCbArgs; delete[] mPredeleteCbArgs;
} }
delete mUndo;
} }
@@ -190,6 +204,9 @@ void Fl_Text_Buffer::text(const char *t)
/* Call the saved display routine(s) to update the screen */ /* Call the saved display routine(s) to update the screen */
call_modify_callbacks(0, deletedLength, insertedLength, 0, deletedText); call_modify_callbacks(0, deletedLength, insertedLength, 0, deletedText);
free((void *) deletedText); free((void *) deletedText);
if (mCanUndo)
mUndo->clear();
} }
@@ -449,36 +466,39 @@ void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart,
*/ */
int Fl_Text_Buffer::undo(int *cursorPos) int Fl_Text_Buffer::undo(int *cursorPos)
{ {
if (undowidget != this || (!undocut && !undoinsert && !mCanUndo)) if (!mCanUndo)
return 0; return 0;
int ilen = undocut; if (!mUndo->undocut && !mUndo->undoinsert)
int xlen = undoinsert; return 0;
int b = undoat - xlen;
if (xlen && undoyankcut && !ilen) { int ilen = mUndo->undocut;
ilen = undoyankcut; int xlen = mUndo->undoinsert;
int b = mUndo->undoat - xlen;
if (xlen && mUndo->undoyankcut && !ilen) {
ilen = mUndo->undoyankcut;
} }
if (xlen && ilen) { if (xlen && ilen) {
undobuffersize(ilen + 1); mUndo->undobuffersize(ilen + 1);
undobuffer[ilen] = 0; mUndo->undobuffer[ilen] = 0;
char *tmp = fl_strdup(undobuffer); char *tmp = fl_strdup(mUndo->undobuffer);
replace(b, undoat, tmp); replace(b, mUndo->undoat, tmp);
if (cursorPos) if (cursorPos)
*cursorPos = mCursorPosHint; *cursorPos = mCursorPosHint;
free(tmp); free(tmp);
} else if (xlen) { } else if (xlen) {
remove(b, undoat); remove(b, mUndo->undoat);
if (cursorPos) if (cursorPos)
*cursorPos = mCursorPosHint; *cursorPos = mCursorPosHint;
} else if (ilen) { } else if (ilen) {
undobuffersize(ilen + 1); mUndo->undobuffersize(ilen + 1);
undobuffer[ilen] = 0; mUndo->undobuffer[ilen] = 0;
insert(undoat, undobuffer); insert(mUndo->undoat, mUndo->undobuffer);
if (cursorPos) if (cursorPos)
*cursorPos = mCursorPosHint; *cursorPos = mCursorPosHint;
undoyankcut = 0; mUndo->undoyankcut = 0;
} }
return 1; return 1;
@@ -490,10 +510,17 @@ int Fl_Text_Buffer::undo(int *cursorPos)
*/ */
void Fl_Text_Buffer::canUndo(char flag) void Fl_Text_Buffer::canUndo(char flag)
{ {
if (flag) {
if (!mCanUndo) {
mUndo = new Fl_Text_Undo_Action();
}
} else {
if (mCanUndo) {
delete mUndo;
mUndo = NULL;
}
}
mCanUndo = flag; mCanUndo = flag;
// disabling undo also clears the last undo operation!
if (!mCanUndo && undowidget==this)
undowidget = 0;
} }
@@ -1190,15 +1217,14 @@ int Fl_Text_Buffer::insert_(int pos, const char *text)
update_selections(pos, 0, insertedLength); update_selections(pos, 0, insertedLength);
if (mCanUndo) { if (mCanUndo) {
if (undowidget == this && undoat == pos && undoinsert) { if (mUndo->undoat == pos && mUndo->undoinsert) {
undoinsert += insertedLength; mUndo->undoinsert += insertedLength;
} else { } else {
undoinsert = insertedLength; mUndo->undoinsert = insertedLength;
undoyankcut = (undoat == pos) ? undocut : 0; mUndo->undoyankcut = (mUndo->undoat == pos) ? mUndo->undocut : 0;
} }
undoat = pos + insertedLength; mUndo->undoat = pos + insertedLength;
undocut = 0; mUndo->undocut = 0;
undowidget = this;
} }
return insertedLength; return insertedLength;
@@ -1214,34 +1240,33 @@ void Fl_Text_Buffer::remove_(int start, int end)
/* if the gap is not contiguous to the area to remove, move it there */ /* if the gap is not contiguous to the area to remove, move it there */
if (mCanUndo) { if (mCanUndo) {
if (undowidget == this && undoat == end && undocut) { if (mUndo->undoat == end && mUndo->undocut) {
undobuffersize(undocut + end - start + 1); mUndo->undobuffersize(mUndo->undocut + end - start + 1);
memmove(undobuffer + end - start, undobuffer, undocut); memmove(mUndo->undobuffer + end - start, mUndo->undobuffer, mUndo->undocut);
undocut += end - start; mUndo->undocut += end - start;
} else { } else {
undocut = end - start; mUndo->undocut = end - start;
undobuffersize(undocut); mUndo->undobuffersize(mUndo->undocut);
} }
undoat = start; mUndo->undoat = start;
undoinsert = 0; mUndo->undoinsert = 0;
undoyankcut = 0; mUndo->undoyankcut = 0;
undowidget = this;
} }
if (start > mGapStart) { if (start > mGapStart) {
if (mCanUndo) if (mCanUndo)
memcpy(undobuffer, mBuf + (mGapEnd - mGapStart) + start, memcpy(mUndo->undobuffer, mBuf + (mGapEnd - mGapStart) + start,
end - start); end - start);
move_gap(start); move_gap(start);
} else if (end < mGapStart) { } else if (end < mGapStart) {
if (mCanUndo) if (mCanUndo)
memcpy(undobuffer, mBuf + start, end - start); memcpy(mUndo->undobuffer, mBuf + start, end - start);
move_gap(end); move_gap(end);
} else { } else {
int prelen = mGapStart - start; int prelen = mGapStart - start;
if (mCanUndo) { if (mCanUndo) {
memcpy(undobuffer, mBuf + start, prelen); memcpy(mUndo->undobuffer, mBuf + start, prelen);
memcpy(undobuffer + prelen, mBuf + mGapEnd, end - start - prelen); memcpy(mUndo->undobuffer + prelen, mBuf + mGapEnd, end - start - prelen);
} }
} }