summaryrefslogtreecommitdiff
path: root/src/Fl_Text_Buffer.cxx
diff options
context:
space:
mode:
authorMatthias Melcher <fltk@matthiasm.com>2010-11-03 22:01:43 +0000
committerMatthias Melcher <fltk@matthiasm.com>2010-11-03 22:01:43 +0000
commitcac40a9b024185cfa5f10d5e7845582ab30a5dd1 (patch)
tree186b289e6429408c98c7a6b27e4eacd7a06d69b5 /src/Fl_Text_Buffer.cxx
parentddd4bbff1b07daf5510b1c9aa233876cae332264 (diff)
STR 2158: partially solved. This commit is huge, I admit. I recoded most of Fl_Text_Buffer and large chunks of Fl_Text_Display to make it UTF-8 safe. Rendering of all left-to-right scripts works well on OS X for all tested fonts. International input works AFAIK. Copy and paste of UTF-8 data works. ----> what's not working yet though is line wrapping. Also, text search for internationsl characters is not working yet.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@7792 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/Fl_Text_Buffer.cxx')
-rw-r--r--src/Fl_Text_Buffer.cxx1695
1 files changed, 454 insertions, 1241 deletions
diff --git a/src/Fl_Text_Buffer.cxx b/src/Fl_Text_Buffer.cxx
index 002454462..cdd43e491 100644
--- a/src/Fl_Text_Buffer.cxx
+++ b/src/Fl_Text_Buffer.cxx
@@ -33,97 +33,47 @@
#include <FL/Fl.H>
#include <FL/Fl_Text_Buffer.H>
+
/*
This file is based on a port of NEdit to FLTK many years ago. NEdit at that
point was already stretched beyond the task it was designed for which explains
- why the source code is sometimes pretty convoluted. It still is a nice widget
- for FLTK.
+ why the source code is sometimes pretty convoluted. It still is a very useful
+ widget for FLTK, and we are thankful that the nedit team allowed us to
+ integrate their code.
With the introduction of Unicode and UTF-8, Fl_Text_... has to go into a whole
new generation of code. Originally designed for monspaced fonts only, many
features make les sense in the multibyte and multiwdth world of UTF-8.
- Columns are a good example. There is simply no such thing.
-
- Rectangular selections pose a real problem.
-
- Using multiple spaces to emulate tab stops will no longer work.
+ Columns are a good example. There is simply no such thing. The new Fl_Text_...
+ widget converts columns to pixels by multiplying them with the average
+ character width for a given font
- And constantly recalculating character widths is just much too expensive.
+ Rectangular selections were rearely used (if at all) and make little sense when
+ using variable width fonts. They have been removed.
- But nevertheless, we will get ths widget rolling again ;-)
+ Using multiple spaces to emulate tab stops has been replaced by pixel counting
+ routines. They are slower, but give the expected result for proportional fonts.
+ And constantly recalculating character widths is just much too expensive. Lines
+ of text are now subdivided into blocks of text which are measured at once
+ instead of individual characters.
*/
-/*
- \todo unicode check
- */
-static void insertColInLine(const char *line, char *insLine, int column,
- int insWidth, int tabDist, int useTabs,
- char *outStr, int *outLen,
- int *endOffset);
-
-/*
- \todo unicode check
- */
-static void deleteRectFromLine(const char *line, int rectStart,
- int rectEnd, int tabDist, int useTabs,
- char *outStr,
- int *outLen, int *endOffset);
-
-/*
- \todo unicode check
- */
-static void overlayRectInLine(const char *line, char *insLine,
- int rectStart, int rectEnd, int tabDist,
- int useTabs, char *outStr,
- int *outLen, int *endOffset);
-
-/*
- \todo unicode check
- */
-static void addPadding(char *string, int startIndent, int toIndent,
- int tabDist, int useTabs, int *charsAdded);
-
-/*
- \todo unicode check
- */
-static char *copyLine(const char *text, int *lineLen);
-
-/*
- unicode tested
- */
-static int countLines(const char *string);
-
-/*
- \todo unicode check
- */
-static int textWidth(const char *text, int tabDist);
-
-/*
- \todo unicode check
- */
-static char *realignTabs(const char *text, int origIndent, int newIndent,
- int tabDist, int useTabs, int *newLength);
-
-/*
- \todo unicode check
- */
-static char *expandTabs(const char *text, int startIndent, int tabDist, int *newLen);
+#ifndef min
/*
- \todo unicode check
+ Unicode safe.
*/
-static char *unexpandTabs(char *text, int startIndent, int tabDist, int *newLen);
-
-#ifndef min
-
static int max(int i1, int i2)
{
return i1 >= i2 ? i1 : i2;
}
+/*
+ Unicode safe.
+ */
static int min(int i1, int i2)
{
return i1 <= i2 ? i1 : i2;
@@ -131,12 +81,6 @@ static int min(int i1, int i2)
#endif
-static const char *ControlCodeTable[32] = {
- "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
- "bs", "ht", "nl", "vt", "np", "cr", "so", "si",
- "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
- "can", "em", "sub", "esc", "fs", "gs", "rs", "us"
-};
static char *undobuffer;
static int undobufferlength;
@@ -146,6 +90,10 @@ static int undocut; // number of characters deleted there
static int undoinsert; // number of characters inserted
static int undoyankcut; // length of valid contents of buffer, even if undocut=0
+/*
+ Resize the undo buffer to match at least the requested size.
+ Unicode safe.
+ */
static void undobuffersize(int n)
{
if (n > undobufferlength) {
@@ -162,7 +110,10 @@ static void undobuffersize(int n)
}
-// unicode ok
+/*
+ Initialize all variables.
+ Unicode safe.
+ */
Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize)
{
mLength = 0;
@@ -171,16 +122,12 @@ Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize)
mGapStart = 0;
mGapEnd = mPreferredGapSize;
mTabDist = 8;
- mUseTabs = 1;
mPrimary.mSelected = 0;
- mPrimary.mRectangular = 0;
mPrimary.mStart = mPrimary.mEnd = 0;
mSecondary.mSelected = 0;
mSecondary.mStart = mSecondary.mEnd = 0;
- mSecondary.mRectangular = 0;
mHighlight.mSelected = 0;
mHighlight.mStart = mHighlight.mEnd = 0;
- mHighlight.mRectangular = 0;
mModifyProcs = NULL;
mCbArgs = NULL;
mNModifyProcs = 0;
@@ -189,17 +136,13 @@ Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize)
mPredeleteCbArgs = NULL;
mCursorPosHint = 0;
mCanUndo = 1;
-#ifdef PURIFY
- {
- int i;
- for (i = mGapStart; i < mGapEnd; i++)
- mBuf[i] = '.';
}
-#endif
-}
-// unicode ok
+/*
+ Free all resources.
+ Unicode safe.
+ */
Fl_Text_Buffer::~Fl_Text_Buffer()
{
free(mBuf);
@@ -214,19 +157,24 @@ Fl_Text_Buffer::~Fl_Text_Buffer()
}
-// This function copies verbose whatever is in front and after the gap into a
-// single buffer.
-// - unicode ok
+/*
+ This function copies verbose whatever is in front and after the gap into a
+ single buffer.
+ Unicode safe.
+ */
char *Fl_Text_Buffer::text() const {
char *t = (char *) malloc(mLength + 1);
memcpy(t, mBuf, mGapStart);
- memcpy(&t[mGapStart], &mBuf[mGapEnd], mLength - mGapStart);
+ memcpy(t+mGapStart, mBuf+mGapEnd, mLength - mGapStart);
t[mLength] = '\0';
return t;
}
-// unicode ok, functions called have not been verified yet
+/*
+ Set the text buffer to a new string.
+ Unicode safe.
+ */
void Fl_Text_Buffer::text(const char *t)
{
call_predelete_callbacks(0, length());
@@ -243,13 +191,6 @@ void Fl_Text_Buffer::text(const char *t)
mGapStart = insertedLength;
mGapEnd = mGapStart + mPreferredGapSize;
memcpy(mBuf, t, insertedLength);
-#ifdef PURIFY
- {
- int i;
- for (i = mGapStart; i < mGapEnd; i++)
- mBuf[i] = '.';
- }
-#endif
/* Zero all of the existing selections */
update_selections(0, deletedLength, 0);
@@ -260,8 +201,10 @@ void Fl_Text_Buffer::text(const char *t)
}
-// Creates a new buffer and copies verbose from around the gap.
-// - unicode ok
+/*
+ Creates a range of text to a new buffer and copies verbose from around the gap.
+ Unicode safe.
+ */
char *Fl_Text_Buffer::text_range(int start, int end) const {
char *s = NULL;
@@ -285,22 +228,23 @@ char *Fl_Text_Buffer::text_range(int start, int end) const {
/* Copy the text from the buffer to the returned string */
if (end <= mGapStart) {
- memcpy(s, &mBuf[start], copiedLength);
+ memcpy(s, mBuf + start, copiedLength);
} else if (start >= mGapStart) {
- memcpy(s, &mBuf[start + (mGapEnd - mGapStart)], copiedLength);
+ memcpy(s, mBuf + start + (mGapEnd - mGapStart), copiedLength);
} else {
int part1Length = mGapStart - start;
- memcpy(s, &mBuf[start], part1Length);
- memcpy(&s[part1Length], &mBuf[mGapEnd], copiedLength - part1Length);
+ memcpy(s, mBuf + start, part1Length);
+ memcpy(s + part1Length, mBuf + mGapEnd, copiedLength - part1Length);
}
s[copiedLength] = '\0';
return s;
}
-
-// TODO: we will need the same signature function to get bytes (style buffer)
-// unicode ok
-unsigned int Fl_Text_Buffer::character(int pos) const {
+/*
+ Return a UCS-4 character at the given index.
+ Unicode safe. Pos must be at a character boundary.
+ */
+unsigned int Fl_Text_Buffer::char_at(int pos) const {
if (pos < 0 || pos >= mLength)
return '\0';
const char *src = address(pos);
@@ -308,7 +252,22 @@ unsigned int Fl_Text_Buffer::character(int pos) const {
}
-// unicode ok, dependents not tested
+/*
+ Return the raw byte at the given index.
+ This function ignores all unicode encoding.
+ */
+char Fl_Text_Buffer::byte_at(int pos) const {
+ if (pos < 0 || pos >= mLength)
+ return '\0';
+ const char *src = address(pos);
+ return *src;
+}
+
+
+/*
+ Insert some text at the given index.
+ Unicode safe. Pos must be at a character boundary.
+*/
void Fl_Text_Buffer::insert(int pos, const char *text)
{
/* if pos is not contiguous to existing text, make it */
@@ -327,7 +286,10 @@ void Fl_Text_Buffer::insert(int pos, const char *text)
}
-// unicode ok, dependents not tested
+/*
+ Replace a range of text with new text.
+ Unicode safe. Start and end must be at a character boundary.
+*/
void Fl_Text_Buffer::replace(int start, int end, const char *text)
{
// Range check...
@@ -348,7 +310,10 @@ void Fl_Text_Buffer::replace(int start, int end, const char *text)
}
-// unicode ok, dependents not tested
+/*
+ Remove a range of text.
+ Unicode safe. Start and End must be at a character boundary.
+*/
void Fl_Text_Buffer::remove(int start, int end)
{
/* Make sure the arguments make sense */
@@ -378,6 +343,11 @@ void Fl_Text_Buffer::remove(int start, int end)
free((void *) deletedText);
}
+
+/*
+ Copy a range of text from another text buffer.
+ Unicode safe. FromDtart, fromEnd, and toPos must be at a character boundary.
+ */
void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart,
int fromEnd, int toPos)
{
@@ -398,8 +368,7 @@ void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart,
memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], copiedLength);
} else if (fromStart >= fromBuf->mGapStart) {
memcpy(&mBuf[toPos],
- &fromBuf->mBuf[fromStart +
- (fromBuf->mGapEnd - fromBuf->mGapStart)],
+ &fromBuf->mBuf[fromStart + (fromBuf->mGapEnd - fromBuf->mGapStart)],
copiedLength);
} else {
int part1Length = fromBuf->mGapStart - fromStart;
@@ -412,9 +381,15 @@ void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart,
update_selections(toPos, 0, copiedLength);
}
+
+/*
+ Take the previous changes and undo them. Return the previous
+ cursor position in cursorPos. Returns 1 if the undo was applied.
+ Unicode safe. CursorPos will be at a character boundary.
+ */
int Fl_Text_Buffer::undo(int *cursorPos)
{
- if (undowidget != this || (!undocut && !undoinsert && !mCanUndo) )
+ if (undowidget != this || !undocut && !undoinsert && !mCanUndo)
return 0;
int ilen = undocut;
@@ -450,181 +425,27 @@ int Fl_Text_Buffer::undo(int *cursorPos)
}
-// unicode ok
+/*
+ Set a flag is undo function will work.
+ Unicode safe.
+ */
void Fl_Text_Buffer::canUndo(char flag)
{
mCanUndo = flag;
+ // disabeling undo also clears the last undo operation!
+ if (!mCanUndo && undowidget==this)
+ undowidget = 0;
}
-void Fl_Text_Buffer::insert_column(int column, int startPos,
- const char *text, int *charsInserted,
- int *charsDeleted)
-{
- int nLines = countLines(text);
- int lineStartPos = line_start(startPos);
- int nDeleted = line_end(skip_lines(startPos, nLines)) - lineStartPos;
- call_predelete_callbacks(lineStartPos, nDeleted);
- const char *deletedText =
- text_range(lineStartPos, lineStartPos + nDeleted);
- int insertDeleted, nInserted;
- insert_column_(column, lineStartPos, text, &insertDeleted, &nInserted,
- &mCursorPosHint);
- if (nDeleted != insertDeleted)
- Fl::error
- ("Fl_Text_Buffer::insert_column(): internal consistency check ins1 failed");
- call_modify_callbacks(lineStartPos, nDeleted, nInserted, 0, deletedText);
- free((void *) deletedText);
- if (charsInserted != NULL)
- *charsInserted = nInserted;
- if (charsDeleted != NULL)
- *charsDeleted = nDeleted;
-}
-
-void Fl_Text_Buffer::overlay_rectangular(int startPos, int rectStart,
- int rectEnd, const char *text,
- int *charsInserted,
- int *charsDeleted)
-{
-
- int nLines = countLines(text);
- int lineStartPos = line_start(startPos);
- int nDeleted = line_end(skip_lines(startPos, nLines)) - lineStartPos;
- call_predelete_callbacks(lineStartPos, nDeleted);
- const char *deletedText =
- text_range(lineStartPos, lineStartPos + nDeleted);
- int insertDeleted, nInserted;
- overlay_rectangular_(lineStartPos, rectStart, rectEnd, text,
- &insertDeleted, &nInserted, &mCursorPosHint);
- if (nDeleted != insertDeleted)
- Fl::error
- ("Fl_Text_Buffer::overlay_rectangle(): internal consistency check ovly1 failed");
- call_modify_callbacks(lineStartPos, nDeleted, nInserted, 0, deletedText);
- free((void *) deletedText);
- if (charsInserted != NULL)
- *charsInserted = nInserted;
- if (charsDeleted != NULL)
- *charsDeleted = nDeleted;
-}
-
-void Fl_Text_Buffer::replace_rectangular(int start, int end, int rectStart,
- int rectEnd, const char *text)
-{
- char *insText = (char *) "";
- int linesPadded = 0;
-
- /* Make sure start and end refer to complete lines, since the
- columnar delete and insert operations will replace whole lines */
- start = line_start(start);
- end = line_end(end);
-
- call_predelete_callbacks(start, end - start);
-
- /* If more lines will be deleted than inserted, pad the inserted text
- with newlines to make it as long as the number of deleted lines. This
- will indent all of the text to the right of the rectangle to the same
- column. If more lines will be inserted than deleted, insert extra
- lines in the buffer at the end of the rectangle to make room for the
- additional lines in "text" */
- int nInsertedLines = countLines(text);
- int nDeletedLines = count_lines(start, end);
- if (nInsertedLines < nDeletedLines) {
- int insLen = strlen(text);
- insText = (char *) malloc(insLen + nDeletedLines - nInsertedLines + 1);
- strcpy(insText, text);
- char *insPtr = insText + insLen;
- for (int i = 0; i < nDeletedLines - nInsertedLines; i++)
- *insPtr++ = '\n';
- *insPtr = '\0';
- } else if (nDeletedLines < nInsertedLines) {
- linesPadded = nInsertedLines - nDeletedLines;
- for (int i = 0; i < linesPadded; i++)
- insert_(end, "\n");
- }
-
- /* else nDeletedLines == nInsertedLines; */
- /* Save a copy of the text which will be modified for the modify CBs */
- const char *deletedText = text_range(start, end);
-
- /* Delete then insert */
- int insertDeleted, insertInserted, deleteInserted, hint;
- remove_rectangular_(start, end, rectStart, rectEnd, &deleteInserted,
- &hint);
- insert_column_(rectStart, start, insText, &insertDeleted,
- &insertInserted, &mCursorPosHint);
-
- /* Figure out how many chars were inserted and call modify callbacks */
- if (insertDeleted != deleteInserted + linesPadded)
- Fl::error
- ("Fl_Text_Buffer::replace_rectangular(): internal consistency check repl1 failed");
- call_modify_callbacks(start, end - start, insertInserted, 0,
- deletedText);
- free((void *) deletedText);
- if (nInsertedLines < nDeletedLines)
- free((void *) insText);
-}
-
-void Fl_Text_Buffer::remove_rectangular(int start, int end, int rectStart,
- int rectEnd)
-{
-
- start = line_start(start);
- end = line_end(end);
- call_predelete_callbacks(start, end - start);
- const char *deletedText = text_range(start, end);
- int nInserted;
- remove_rectangular_(start, end, rectStart, rectEnd, &nInserted,
- &mCursorPosHint);
- call_modify_callbacks(start, end - start, nInserted, 0, deletedText);
- free((void *) deletedText);
-}
-
-void Fl_Text_Buffer::clear_rectangular(int start, int end, int rectStart,
- int rectEnd)
-{
- int nLines = count_lines(start, end);
- char *newlineString = (char *) malloc(nLines + 1);
- int i;
- for (i = 0; i < nLines; i++)
- newlineString[i] = '\n';
- newlineString[i] = '\0';
- overlay_rectangular(start, rectStart, rectEnd, newlineString,
- NULL, NULL);
- free((void *) newlineString);
-}
-
-char *Fl_Text_Buffer::text_in_rectangle(int start, int end,
- int rectStart,
- int rectEnd) const {
- start = line_start(start);
- end = line_end(end);
- char *textOut = (char *) malloc((end - start) + 1);
- int lineStart = start;
- char *outPtr = textOut;
- int selLeft, selRight;
- while (lineStart <= end)
- {
- rectangular_selection_boundaries(lineStart, rectStart, rectEnd,
- &selLeft, &selRight);
- const char *textIn = text_range(selLeft, selRight);
- int len = selRight - selLeft;
- memcpy(outPtr, textIn, len);
- free((void *) textIn);
- outPtr += len;
- lineStart = line_end(selRight) + 1;
- *outPtr++ = '\n';
- } if (outPtr != textOut)
- outPtr--; /* don't leave trailing newline */
- *outPtr = '\0';
-
- /* If necessary, realign the tabs in the selection as if the text were
- positioned at the left margin */
- int len;
- char *retabbedStr = realignTabs(textOut, rectStart, 0, mTabDist,
- mUseTabs, &len);
- free((void *) textOut);
- return retabbedStr;
-}
+/*
+ Change the tab width. This will cause a couple of callbacks and a complete
+ redisplay.
+ Matt: I am not entirely sure why we need to trigger callbacks because
+ tabs are only a graphical hint, not changing any text at all, but I leave
+ this in here for back compatibility.
+ Unicode safe.
+ */
void Fl_Text_Buffer::tab_distance(int tabDist)
{
/* First call the pre-delete callbacks with the previous tab setting
@@ -641,6 +462,11 @@ void Fl_Text_Buffer::tab_distance(int tabDist)
free((void *) deletedText);
}
+
+/*
+ Select a range of text.
+ Unicode safe. Start and End must be at a character boundary.
+ */
void Fl_Text_Buffer::select(int start, int end)
{
Fl_Text_Selection oldSelection = mPrimary;
@@ -649,6 +475,11 @@ void Fl_Text_Buffer::select(int start, int end)
redisplay_selection(&oldSelection, &mPrimary);
}
+
+/*
+ Clear the primary selection.
+ Unicode safe.
+ */
void Fl_Text_Buffer::unselect()
{
Fl_Text_Selection oldSelection = mPrimary;
@@ -657,42 +488,51 @@ void Fl_Text_Buffer::unselect()
redisplay_selection(&oldSelection, &mPrimary);
}
-void Fl_Text_Buffer::select_rectangular(int start, int end, int rectStart,
- int rectEnd)
-{
- Fl_Text_Selection oldSelection = mPrimary;
- mPrimary.set_rectangular(start, end, rectStart, rectEnd);
- redisplay_selection(&oldSelection, &mPrimary);
-}
-
+/*
+ Return the primary selection range.
+ Unicode safe.
+ */
int Fl_Text_Buffer::selection_position(int *start, int *end)
{
return mPrimary.position(start, end);
}
-int Fl_Text_Buffer::selection_position(int *start, int *end,
- int *isRect, int *rectStart,
- int *rectEnd)
-{
- return mPrimary.position(start, end, isRect, rectStart, rectEnd);
-}
+/*
+ Return a copy of the selected text.
+ Unicode safe.
+ */
char *Fl_Text_Buffer::selection_text()
{
return selection_text_(&mPrimary);
}
+
+/*
+ Remove the selected text.
+ Unicode safe.
+ */
void Fl_Text_Buffer::remove_selection()
{
remove_selection_(&mPrimary);
}
+
+/*
+ Replace the selected text.
+ Unicode safe.
+ */
void Fl_Text_Buffer::replace_selection(const char *text)
{
replace_selection_(&mPrimary, text);
}
+
+/*
+ Select text.
+ Unicode safe. Start and End must be at a character boundary.
+ */
void Fl_Text_Buffer::secondary_select(int start, int end)
{
Fl_Text_Selection oldSelection = mSecondary;
@@ -701,6 +541,11 @@ void Fl_Text_Buffer::secondary_select(int start, int end)
redisplay_selection(&oldSelection, &mSecondary);
}
+
+/*
+ Deselect text.
+ Unicode safe.
+ */
void Fl_Text_Buffer::secondary_unselect()
{
Fl_Text_Selection oldSelection = mSecondary;
@@ -709,44 +554,51 @@ void Fl_Text_Buffer::secondary_unselect()
redisplay_selection(&oldSelection, &mSecondary);
}
-void Fl_Text_Buffer::secondary_select_rectangular(int start, int end,
- int rectStart,
- int rectEnd)
-{
- Fl_Text_Selection oldSelection = mSecondary;
- mSecondary.set_rectangular(start, end, rectStart, rectEnd);
- redisplay_selection(&oldSelection, &mSecondary);
-}
-
+/*
+ Return the selected range.
+ Unicode safe.
+ */
int Fl_Text_Buffer::secondary_selection_position(int *start, int *end)
{
return mSecondary.position(start, end);
}
-int Fl_Text_Buffer::secondary_selection_position(int *start, int *end,
- int *isRect,
- int *rectStart,
- int *rectEnd)
-{
- return mSecondary.position(start, end, isRect, rectStart, rectEnd);
-}
+/*
+ Return a copy of the text in this selection.
+ Unicode safe.
+ */
char *Fl_Text_Buffer::secondary_selection_text()
{
return selection_text_(&mSecondary);
}
+
+/*
+ Remove the selected text.
+ Unicode safe.
+ */
void Fl_Text_Buffer::remove_secondary_selection()
{
remove_selection_(&mSecondary);
}
+
+/*
+ Replace selected text.
+ Unicode safe.
+ */
void Fl_Text_Buffer::replace_secondary_selection(const char *text)
{
replace_selection_(&mSecondary, text);
}
+
+/*
+ Highlight a range of text.
+ Unicode safe. Start and End must be at a character boundary.
+ */
void Fl_Text_Buffer::highlight(int start, int end)
{
Fl_Text_Selection oldSelection = mHighlight;
@@ -755,6 +607,11 @@ void Fl_Text_Buffer::highlight(int start, int end)
redisplay_selection(&oldSelection, &mHighlight);
}
+
+/*
+ Remove text highlighting.
+ Unicode safe.
+ */
void Fl_Text_Buffer::unhighlight()
{
Fl_Text_Selection oldSelection = mHighlight;
@@ -763,32 +620,31 @@ void Fl_Text_Buffer::unhighlight()
redisplay_selection(&oldSelection, &mHighlight);
}
-void Fl_Text_Buffer::highlight_rectangular(int start, int end,
- int rectStart, int rectEnd)
-{
- Fl_Text_Selection oldSelection = mHighlight;
- mHighlight.set_rectangular(start, end, rectStart, rectEnd);
- redisplay_selection(&oldSelection, &mHighlight);
-}
-
+/*
+ Return position of highlight.
+ Unicode safe.
+ */
int Fl_Text_Buffer::highlight_position(int *start, int *end)
{
return mHighlight.position(start, end);
}
-int Fl_Text_Buffer::highlight_position(int *start, int *end,
- int *isRect, int *rectStart,
- int *rectEnd)
-{
- return mHighlight.position(start, end, isRect, rectStart, rectEnd);
-}
+/*
+ Return a copy of highlighted text.
+ Unicode safe.
+ */
char *Fl_Text_Buffer::highlight_text()
{
return selection_text_(&mHighlight);
}
+
+/*
+ Add a callback that is called whenever text is modified.
+ Unicode safe.
+ */
void Fl_Text_Buffer::add_modify_callback(Fl_Text_Modify_Cb bufModifiedCB,
void *cbArg)
{
@@ -810,8 +666,13 @@ void Fl_Text_Buffer::add_modify_callback(Fl_Text_Modify_Cb bufModifiedCB,
mCbArgs = newCBArgs;
}
-void Fl_Text_Buffer::
-remove_modify_callback(Fl_Text_Modify_Cb bufModifiedCB, void *cbArg)
+
+/*
+ Remove a callback.
+ Unicode safe.
+ */
+void Fl_Text_Buffer::remove_modify_callback(Fl_Text_Modify_Cb bufModifiedCB,
+ void *cbArg)
{
int i, toRemove = -1;
@@ -857,8 +718,13 @@ remove_modify_callback(Fl_Text_Modify_Cb bufModifiedCB, void *cbArg)
mCbArgs = newCBArgs;
}
-void Fl_Text_Buffer::
-add_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg)
+
+/*
+ Add a callback that is called before deleting text.
+ Unicode safe.
+ */
+void Fl_Text_Buffer::add_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB,
+ void *cbArg)
{
Fl_Text_Predelete_Cb *newPreDeleteProcs =
new Fl_Text_Predelete_Cb[mNPredeleteProcs + 1];
@@ -878,8 +744,12 @@ add_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg)
mPredeleteCbArgs = newCBArgs;
}
-void Fl_Text_Buffer::
-remove_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg)
+
+/*
+ Remove a callback.
+ Unicode safe.
+ */
+void Fl_Text_Buffer::remove_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg)
{
int i, toRemove = -1;
/* find the matching callback to remove */
@@ -926,156 +796,99 @@ remove_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg)
mPredeleteCbArgs = newCBArgs;
}
+
+/*
+ Return a copy of the line that contains a given index.
+ Unicode safe. Pos must be at a character boundary.
+ */
char *Fl_Text_Buffer::line_text(int pos) const {
return text_range(line_start(pos), line_end(pos));
}
-int Fl_Text_Buffer::line_start(int pos) const {
+
+/*
+ Find the beginning of the line.
+ NOT UNICODE SAFE.
+ */
+int Fl_Text_Buffer::line_start(int pos) const
+{
+ // FIXME: this currently works for unicode, but will be very inefficent when findchar_backward is fixed.
if (!findchar_backward(pos, '\n', &pos))
return 0;
return pos + 1;
}
+
+/*
+ Find the end of the line.
+ NOT UNICODE SAFE.
+ */
int Fl_Text_Buffer::line_end(int pos) const {
+ // FIXME: this currently works for unicode, but will be very inefficent when findchar_forward is fixed.
if (!findchar_forward(pos, '\n', &pos))
pos = mLength;
return pos;
}
+
+/*
+ Find the beginning of a word.
+ NOT UNICODE SAFE.
+ */
int Fl_Text_Buffer::word_start(int pos) const {
// FIXME: character is ucs-4
- while (pos && (isalnum(character(pos)) || character(pos) == '_')) {
+ while (pos && (isalnum(char_at(pos)) || char_at(pos) == '_')) {
pos--;
}
// FIXME: character is ucs-4
- if (!(isalnum(character(pos)) || character(pos) == '_'))
+ if (!(isalnum(char_at(pos)) || char_at(pos) == '_'))
pos++;
return pos;
}
+
+/*
+ Find the end of a word.
+ NOT UNICODE SAFE.
+ */
int Fl_Text_Buffer::word_end(int pos) const {
// FIXME: character is ucs-4
- while (pos < length() && (isalnum(character(pos)) || character(pos) == '_'))
+ while (pos < length() && (isalnum(char_at(pos)) || char_at(pos) == '_'))
{
pos++;
} return pos;
}
-// Expand from the byte representation into some readable text.
-// Under unicode, this is not really needed because all characters should
-// be prinatble one way or the other. But since we use this to (badly) simulate
-// tabs, we can leave this here anyway.
-// - unicode ok
-int Fl_Text_Buffer::expand_character(int pos, int indent, char *outStr) const {
- const char *src = address(pos);
- return expand_character(src, indent, outStr, mTabDist);
-}
-
-
-// static function and counterpart to "character_width"
-// - unicode ok
-// FIXME: harmonise with new character_width(char*...) version
-//
-int Fl_Text_Buffer::expand_character(const char *src, int indent, char *outStr, int tabDist)
-{
- char c = *src;
- /* Convert tabs to spaces */
- if (c == '\t') {
- int nSpaces = tabDist - (indent % tabDist);
- for (int i = 0; i < nSpaces; i++)
- outStr[i] = ' ';
- return nSpaces;
- }
-
- /* Convert control codes to readable character sequences */
- if (((unsigned char) c) <= 31) {
- sprintf(outStr, "<%s>", ControlCodeTable[(unsigned char) c]);
- return strlen(outStr);
- } else if (c == 127) {
- sprintf(outStr, "<del>");
- return 5;
- } else if ((c & 0x80) && !(c & 0x40)) {
-#ifdef DEBUG
- fprintf(stderr, "%s:%d - Error in UTF-8 encoding!\n", __FILE__, __LINE__);
-#endif
- *outStr = c;
- return 1;
- } else if (c & 0x80) {
- int i, n = fl_utf8len(c);
- for (i=0; i<n; i++) *outStr++ = *src++;
- return n;
- }
-
- /* Otherwise, just return the character */
- *outStr = c;
- return 1;
-}
-
-
-// This function takes a character and optionally converts it into a little
-// string which will replace the character on screen. This function returns
-// the number of bytes in the replacement string, but *not* the number of
-// bytes in the original character!
-// - unicode ok
-int Fl_Text_Buffer::character_width(const char *src, int indent, int tabDist)
-{
- char c = *src;
- if ((c & 0x80) && (c & 0x40)) { // first byte of UTF-8 sequence
- int len = fl_utf8len(c);
- int ret = 0;
- unsigned int ucs = fl_utf8decode(src, src+len, &ret);
- int width = fl_wcwidth_(ucs); // FIXME
- // fprintf(stderr, "mk_wcwidth(%x) -> %d (%d, %d, %s)\n", ucs, width, len, ret, src);
- return width;
- }
- if ((c & 0x80) && !(c & 0x40)) { // other byte of UTF-8 sequence
- return 0;
- }
- return character_width(c, indent, tabDist);
-}
-
-// FIXME: merge the following with the char* version above.
-// but the question then is: how to reorganise expand_character()?
-//
-int Fl_Text_Buffer::character_width(const char c, int indent, int tabDist)
-{
- /* Note, this code must parallel that in Fl_Text_Buffer::ExpandCharacter */
- if (c == '\t') {
- return tabDist - (indent % tabDist);
- } else if (((unsigned char) c) <= 31) {
- return strlen(ControlCodeTable[(unsigned char) c]) + 2;
- } else if (c == 127) {
- return 5;
- } else if ((c & 0x80) && !(c & 0x40)) {
-#ifdef DEBUG
- fprintf(stderr, "%s:%d - Error in UTF-8 encoding!\n", __FILE__, __LINE__);
-#endif
- return 1;
- } else if (c & 0x80) {
- // return fl_utf8len(c);
- return 1;
- }
- return 1;
-}
-
+/*
+ Matt: I am not sure why we need this function. Does it still make sense in
+ the world of proportional characters?
+ */
int Fl_Text_Buffer::count_displayed_characters(int lineStartPos,
int targetPos) const
{
+ // TODO: is this function still needed? If it is, put this functionality in handle_vline?
int charCount = 0;
- char expandedChar[FL_TEXT_MAX_EXP_CHAR_LEN];
int pos = lineStartPos;
- while (pos < targetPos)
- charCount += expand_character(pos++, charCount, expandedChar);
+ while (pos < targetPos) {
+ int len = fl_utf8len(*address(pos));
+ charCount += 1;
+ pos += len;
+ }
return charCount;
}
+/*
+ Matt: I am not sure why we need this function. Does it still make sense in
+ the world of proportional characters?
+ */
// All values are number of bytes.
-// - unicode ok
+// - unicode ok?
int Fl_Text_Buffer::skip_displayed_characters(int lineStartPos, int nChars)
{
+ // FIXME: is this function still needed?
int pos = lineStartPos;
for (int charCount = 0; charCount < nChars && pos < mLength;) {
@@ -1083,12 +896,17 @@ int Fl_Text_Buffer::skip_displayed_characters(int lineStartPos, int nChars)
char c = *src;
if (c == '\n')
return pos;
- charCount += character_width(src, charCount, mTabDist);
+ charCount++;
pos += fl_utf8len(c);
}
return pos;
}
+
+/*
+ Count the number of newline characters between start and end.
+ Unicode safe. StartPos and endPos must be at a character boundary.
+ */
int Fl_Text_Buffer::count_lines(int startPos, int endPos) const {
int gapLen = mGapEnd - mGapStart;
int lineCount = 0;
@@ -1100,7 +918,8 @@ int Fl_Text_Buffer::count_lines(int startPos, int endPos) const {
return lineCount;
if (mBuf[pos++] == '\n')
lineCount++;
- } while (pos < mLength) {
+ }
+ while (pos < mLength) {
if (pos == endPos)
return lineCount;
if (mBuf[pos++ + gapLen] == '\n')
@@ -1109,6 +928,11 @@ int Fl_Text_Buffer::count_lines(int startPos, int endPos) const {
return lineCount;
}
+
+/*
+ Skip to the first character, n lines ahead.
+ Unicode safe. StartPos must be at a character boundary.
+ */
int Fl_Text_Buffer::skip_lines(int startPos, int nLines)
{
if (nLines == 0)
@@ -1134,6 +958,11 @@ int Fl_Text_Buffer::skip_lines(int startPos, int nLines)
return pos;
}
+
+/*
+ Skip to the first character, n lines back.
+ Unicode safe. StartPos must be at a character boundary.
+ */
int Fl_Text_Buffer::rewind_lines(int startPos, int nLines)
{
int pos = startPos - 1;
@@ -1159,10 +988,15 @@ int Fl_Text_Buffer::rewind_lines(int startPos, int nLines)
return 0;
}
+
+/*
+ Find a matching string in the buffer.
+ NOT TESTED FOR UNICODE.
+ */
int Fl_Text_Buffer::search_forward(int startPos, const char *searchString,
- int *foundPos,
- int matchCase) const
+ int *foundPos, int matchCase) const
{
+ // FIXME: Unicode?
if (!searchString)
return 0;
int bp;
@@ -1176,17 +1010,22 @@ int Fl_Text_Buffer::search_forward(int startPos, const char *searchString,
return 1;
}
// FIXME: character is ucs-4
- } while ((matchCase ? character(bp++) == (unsigned int)*sp++ :
- toupper(character(bp++)) == toupper(*sp++))
+ } while ((matchCase ? char_at(bp++) == *sp++ :
+ toupper(char_at(bp++)) == toupper(*sp++))
&& bp < length());
startPos++;
}
return 0;
}
+
+/*
+ Find a matching string in the buffer.
+ NOT TESTED FOR UNICODE.
+ */
int Fl_Text_Buffer::search_backward(int startPos, const char *searchString,
- int *foundPos,
- int matchCase) const {
+ int *foundPos, int matchCase) const {
+ // FIXME: Unicode?
if (!searchString)
return 0;
int bp;
@@ -1201,17 +1040,22 @@ int Fl_Text_Buffer::search_backward(int startPos, const char *searchString,
return 1;
}
// FIXME: character is ucs-4
- } while ((matchCase ? character(bp--) == (unsigned int)*sp-- :
- toupper(character(bp--)) == toupper(*sp--))
+ } while ((matchCase ? char_at(bp--) == *sp-- :
+ toupper(char_at(bp--)) == toupper(*sp--))
&& bp >= 0);
startPos--;
}
return 0;
}
-int Fl_Text_Buffer::findchars_forward(int startPos,
- const char *searchChars,
+
+/*
+ Find a matching string in the buffer.
+ NOT TESTED FOR UNICODE.
+ */
+int Fl_Text_Buffer::findchars_forward(int startPos, const char *searchChars,
int *foundPos) const {
+ // FIXME: unicode?
int gapLen = mGapEnd - mGapStart;
const char *c;
@@ -1238,9 +1082,14 @@ int Fl_Text_Buffer::findchars_forward(int startPos,
return 0;
}
-int Fl_Text_Buffer::findchars_backward(int startPos,
- const char *searchChars,
+
+/*
+ Find a matching string in the buffer.
+ NOT TESTED FOR UNICODE.
+ */
+int Fl_Text_Buffer::findchars_backward(int startPos, const char *searchChars,
int *foundPos) const {
+ // FIXME: Unicode
int gapLen = mGapEnd - mGapStart;
const char *c;
@@ -1272,6 +1121,11 @@ int Fl_Text_Buffer::findchars_backward(int startPos,
return 0;
}
+
+/*
+ Insert a string into the buffer.
+ Unicode safe. Pos must be at a character boundary. Text must be a correct utf8 string.
+ */
int Fl_Text_Buffer::insert_(int pos, const char *text)
{
int insertedLength = strlen(text);
@@ -1307,6 +1161,11 @@ int Fl_Text_Buffer::insert_(int pos, const char *text)
return insertedLength;
}
+
+/*
+ Remove a string from the buffer.
+ Unicode safe. Start and end must be at a character boundary.
+ */
void Fl_Text_Buffer::remove_(int start, int end)
{
/* if the gap is not contiguous to the area to remove, move it there */
@@ -1354,458 +1213,23 @@ void Fl_Text_Buffer::remove_(int start, int end)
update_selections(start, end - start, 0);
}
-void Fl_Text_Buffer::insert_column_(int column, int startPos,
- const char *insText, int *nDeleted,
- int *nInserted, int *endPos)
-{
- if (column < 0)
- column = 0;
-
- /* Allocate a buffer for the replacement string large enough to hold
- possibly expanded tabs in both the inserted text and the replaced
- area, as well as per line: 1) an additional 2*FL_TEXT_MAX_EXP_CHAR_LEN
- characters for padding where tabs and control characters cross the
- column of the selection, 2) up to "column" additional spaces per
- line for padding out to the position of "column", 3) padding up
- to the width of the inserted text if that must be padded to align
- the text beyond the inserted column. (Space for additional
- newlines if the inserted text extends beyond the end of the buffer
- is counted with the length of insText) */
- int start = line_start(startPos);
- int nLines = countLines(insText) + 1;
- int insWidth = textWidth(insText, mTabDist); // this function probably returns a useless value
- int end = line_end(skip_lines(start, nLines - 1));
- int expReplLen, expInsLen, len, endOffset;
- const char *replText = text_range(start, end);
- char *expText = expandTabs(replText, 0, mTabDist, &expReplLen);
- free((void *) replText);
- free((void *) expText);
- expText = expandTabs(insText, 0, mTabDist, &expInsLen);
- free((void *) expText);
- char *outStr = (char *) malloc(expReplLen + expInsLen +
- nLines * (column + insWidth +
- FL_TEXT_MAX_EXP_CHAR_LEN) + 1);
-
- /* Loop over all lines in the buffer between start and end removing the
- text between rectStart and rectEnd and padding appropriately. Trim
- trailing space from line (whitespace at the ends of lines otherwise
- tends to multiply, since additional padding is added to maintain it */
- char *outPtr = outStr, *insLine;
- const char *insPtr = insText, *line;
- for (int lineStart = start, lineEnd;;) {
- lineEnd = line_end(lineStart);
- line = text_range(lineStart, lineEnd);
- insLine = copyLine(insPtr, &len);
- insPtr += len;
- insertColInLine(line, insLine, column, insWidth, mTabDist,
- mUseTabs, outPtr, &len, &endOffset);
- free((void *) line);
- free((void *) insLine);
- for (const char *c = outPtr + len - 1; c > outPtr && isspace(*c); c--)
- len--;
- outPtr += len;
- *outPtr++ = '\n';
- lineStart = lineEnd < mLength ? lineEnd + 1 : mLength;
- if (*insPtr == '\0')
- break;
- insPtr++;
- }
- if (outPtr != outStr)
- outPtr--; /* trim back off extra newline */
- *outPtr = '\0';
- /* replace the text between start and end with the new stuff */
- remove_(start, end);
- insert_(start, outStr);
- *nInserted = outPtr - outStr;
- *nDeleted = end - start;
- *endPos = start + (outPtr - outStr) - len + endOffset;
- free((void *) outStr);
-}
-
-void Fl_Text_Buffer::remove_rectangular_(int start, int end, int rectStart,
- int rectEnd, int *replaceLen,
- int *endPos)
-{
- /* allocate a buffer for the replacement string large enough to hold
- possibly expanded tabs as well as an additional FL_TEXT_MAX_EXP_CHAR_LEN * 2
- characters per line for padding where tabs and control characters cross
- the edges of the selection */
- start = line_start(start);
- end = line_end(end);
- int nLines = count_lines(start, end) + 1;
- const char *s = text_range(start, end);
- int len;
- char *expText = expandTabs(s, 0, mTabDist, &len);
- free((void *) s);
- free((void *) expText);
- char *outStr =
- (char *) malloc(len + nLines * FL_TEXT_MAX_EXP_CHAR_LEN * 2 + 1);
-
- /* loop over all lines in the buffer between start and end removing
- the text between rectStart and rectEnd and padding appropriately */
- int endOffset = 0;
- char *outPtr = outStr;
- const char *line;
- for (int lineStart = start, lineEnd;
- lineStart <= mLength && lineStart <= end;) {
- lineEnd = line_end(lineStart);
- line = text_range(lineStart, lineEnd);
- deleteRectFromLine(line, rectStart, rectEnd, mTabDist,
- mUseTabs, outPtr, &len, &endOffset);
- free((void *) line);
- outPtr += len;
- *outPtr++ = '\n';
- lineStart = lineEnd + 1;
- }
- if (outPtr != outStr)
- outPtr--; /* trim back off extra newline */
- *outPtr = '\0';
-
- /* replace the text between start and end with the newly created string */
- remove_(start, end);
- insert_(start, outStr);
- *replaceLen = outPtr - outStr;
- *endPos = start + (outPtr - outStr) - len + endOffset;
- free((void *) outStr);
-}
-
-void Fl_Text_Buffer::overlay_rectangular_(int startPos, int rectStart,
- int rectEnd, const char *insText,
- int *nDeleted, int *nInserted,
- int *endPos)
-{
-
- /* Allocate a buffer for the replacement string large enough to hold
- possibly expanded tabs in the inserted text, as well as per line: 1)
- an additional 2*FL_TEXT_MAX_EXP_CHAR_LEN characters for padding where tabs
- and control characters cross the column of the selection, 2) up to
- "column" additional spaces per line for padding out to the position
- of "column", 3) padding up to the width of the inserted text if that
- must be padded to align the text beyond the inserted column. (Space
- for additional newlines if the inserted text extends beyond the end
- of the buffer is counted with the length of insText) */
- int start = line_start(startPos);
- int nLines = countLines(insText) + 1;
- int end = line_end(skip_lines(start, nLines - 1)), expInsLen;
- char *expText = expandTabs(insText, 0, mTabDist, &expInsLen);
- free((void *) expText);
- char *outStr = (char *) malloc(end - start + expInsLen +
- nLines * (rectEnd +
- FL_TEXT_MAX_EXP_CHAR_LEN) + 1);
-
- /* Loop over all lines in the buffer between start and end overlaying the
- text between rectStart and rectEnd and padding appropriately. Trim
- trailing space from line (whitespace at the ends of lines otherwise
- tends to multiply, since additional padding is added to maintain it */
- int len, endOffset;
- char *outPtr = outStr, *insLine;
- const char *insPtr = insText, *line;
- for (int lineStart = start, lineEnd;;) {
- lineEnd = line_end(lineStart);
- line = text_range(lineStart, lineEnd);
- insLine = copyLine(insPtr, &len);
- insPtr += len;
- overlayRectInLine(line, insLine, rectStart, rectEnd, mTabDist,
- mUseTabs, outPtr, &len, &endOffset);
- free((void *) line);
- free((void *) insLine);
- for (const char *c = outPtr + len - 1; c > outPtr && isspace(*c); c--)
- len--;
- outPtr += len;
- *outPtr++ = '\n';
- lineStart = lineEnd < mLength ? lineEnd + 1 : mLength;
- if (*insPtr == '\0')
- break;
- insPtr++;
- }
- if (outPtr != outStr)
- outPtr--; /* trim back off extra newline */
- *outPtr = '\0';
-
- /* replace the text between start and end with the new stuff */
- remove_(start, end);
- insert_(start, outStr);
- *nInserted = outPtr - outStr;
- *nDeleted = end - start;
- *endPos = start + (outPtr - outStr) - len + endOffset;
- free((void *) outStr);
-}
-
/*
- Inserts characters from single-line string \p insLine in single-line string
- \p line at \p column, leaving \p insWidth space before continuing line.
- \p outLen returns the number of characters written to \p outStr, \p endOffset
- returns the number of characters from the beginning of the string to
- the right edge of the inserted text (as a hint for routines which need
- to position the cursor).
+ simple setter.
+ Unicode safe. Start and end must be at a character boundary.
*/
-static void insertColInLine(const char *line, char *insLine, int column,
- int insWidth, int tabDist, int useTabs,
- char *outStr, int *outLen,
- int *endOffset)
-{
- /* copy the line up to "column" */
- char *outPtr = outStr;
- int indent = 0, len;
- const char *linePtr;
-
- for (linePtr = line; *linePtr != '\0'; linePtr++) {
- len = Fl_Text_Buffer::character_width(linePtr, indent, tabDist);
- if (indent + len > column)
- break;
- indent += len;
- *outPtr++ = *linePtr;
- }
-
- /* If "column" falls in the middle of a character, and the character is a
- tab, leave it off and leave the indent short and it will get padded
- later. If it's a control character, insert it and adjust indent
- accordingly. */
- int postColIndent;
- if (indent < column && *linePtr != '\0') {
- postColIndent = indent + len;
- if (*linePtr == '\t')
- linePtr++;
- else {
- *outPtr++ = *linePtr++;
- indent += len;
- }
- } else
- postColIndent = indent;
-
- /* If there's no text after the column and no text to insert, that's all */
- if (*insLine == '\0' && *linePtr == '\0') {
- *outLen = *endOffset = outPtr - outStr;
- return;
- }
-
- /* pad out to column if text is too short */
- if (indent < column) {
- addPadding(outPtr, indent, column, tabDist, useTabs, &len);
- outPtr += len;
- indent = column;
- }
-
- /* Copy the text from "insLine" (if any), recalculating the tabs as if
- the inserted string began at column 0 to its new column destination */
- if (*insLine != '\0') {
- char *retabbedStr = realignTabs(insLine, 0, indent, tabDist, useTabs,
- &len);
- for (const char *c = retabbedStr; *c != '\0'; c++) {
- *outPtr++ = *c;
- len = Fl_Text_Buffer::character_width(c, indent, tabDist);
- indent += len;
- }
- free((void *) retabbedStr);
- }
-
- /* If the original line did not extend past "column", that's all */
- if (*linePtr == '\0') {
- *outLen = *endOffset = outPtr - outStr;
- return;
- }
-
- /* Pad out to column + width of inserted text + (additional original
- offset due to non-breaking character at column) */
- int toIndent = column + insWidth + postColIndent - column;
- addPadding(outPtr, indent, toIndent, tabDist, useTabs, &len);
- outPtr += len;
- indent = toIndent;
-
- /* realign tabs for text beyond "column" and write it out */
- char *retabbedStr = realignTabs(linePtr, postColIndent, indent, tabDist,
- useTabs, &len);
- strcpy(outPtr, retabbedStr);
- free((void *) retabbedStr);
- *endOffset = outPtr - outStr;
- *outLen = (outPtr - outStr) + len;
-}
-
-/**
- Removes characters in single-line string \p line between displayed positions
- \p rectStart and \p rectEnd, and write the result to \p outStr, which is
- assumed to be large enough to hold the returned string. Note that in
- certain cases, it is possible for the string to get longer due to
- expansion of tabs. \p endOffset returns the number of characters from
- the beginning of the string to the point where the characters were
- deleted (as a hint for routines which need to position the cursor).
- */
-static void deleteRectFromLine(const char *line, int rectStart,
- int rectEnd, int tabDist, int useTabs,
- char *outStr,
- int *outLen, int *endOffset)
-{
-
- /* copy the line up to rectStart */
- char *outPtr = outStr;
- int indent = 0, len;
- const char *c;
- for (c = line; *c != '\0'; c++) {
- if (indent > rectStart)
- break;
- len = Fl_Text_Buffer::character_width(c, indent, tabDist);
- if (indent + len > rectStart && (indent == rectStart || *c == '\t'))
- break;
- indent += len;
- *outPtr++ = *c;
- }
- int preRectIndent = indent;
-
- /* skip the characters between rectStart and rectEnd */
- for (; *c != '\0' && indent < rectEnd; c++)
- indent += Fl_Text_Buffer::character_width(c, indent, tabDist);
- int postRectIndent = indent;
-
- /* If the line ended before rectEnd, there's nothing more to do */
- if (*c == '\0') {
- *outPtr = '\0';
- *outLen = *endOffset = outPtr - outStr;
- return;
- }
-
- /* fill in any space left by removed tabs or control characters
- which straddled the boundaries */
- indent = max(rectStart + postRectIndent - rectEnd, preRectIndent);
- addPadding(outPtr, preRectIndent, indent, tabDist, useTabs, &len);
- outPtr += len;
-
- /* Copy the rest of the line. If the indentation has changed, preserve
- the position of non-whitespace characters by converting tabs to
- spaces, then back to tabs with the correct offset */
- char *retabbedStr =
- realignTabs(c, postRectIndent, indent, tabDist, useTabs, &len);
- strcpy(outPtr, retabbedStr);
- free((void *) retabbedStr);
- *endOffset = outPtr - outStr;
- *outLen = (outPtr - outStr) + len;
-}
-
-/**
- Overlay characters from single-line string \p insLine on single-line string
- \p line between displayed character offsets \p rectStart and \p rectEnd.
- \p outLen returns the number of characters written to \p outStr, \p endOffset
- returns the number of characters from the beginning of the string to
- the right edge of the inserted text (as a hint for routines which need
- to position the cursor).
- */
-static void overlayRectInLine(const char *line, char *insLine,
- int rectStart, int rectEnd, int tabDist,
- int useTabs, char *outStr,
- int *outLen, int *endOffset)
-{
- /* copy the line up to "rectStart" */
- char *outPtr = outStr;
- int inIndent = 0, outIndent = 0, len;
- const char *linePtr = line;
-
- for (; *linePtr != '\0'; linePtr++) {
- len = Fl_Text_Buffer::character_width(linePtr, inIndent, tabDist);
- if (inIndent + len > rectStart)
- break;
- inIndent += len;
- outIndent += len;
- *outPtr++ = *linePtr;
- }
-
- /* If "rectStart" falls in the middle of a character, and the character
- is a tab, leave it off and leave the outIndent short and it will get
- padded later. If it's a control character, insert it and adjust
- outIndent accordingly. */
- if (inIndent < rectStart && *linePtr != '\0') {
- if (*linePtr == '\t') {
- linePtr++;
- inIndent += len;
- } else {
- *outPtr++ = *linePtr++;
- outIndent += len;
- inIndent += len;
- }
- }
-
- /* skip the characters between rectStart and rectEnd */
- int postRectIndent = rectEnd;
- for (; *linePtr != '\0'; linePtr++) {
- inIndent += Fl_Text_Buffer::character_width(linePtr, inIndent, tabDist);
- if (inIndent >= rectEnd) {
- linePtr++;
- postRectIndent = inIndent;
- break;
- }
- }
-
- /* If there's no text after rectStart and no text to insert, that's all */
- if (*insLine == '\0' && *linePtr == '\0') {
- *outLen = *endOffset = outPtr - outStr;
- return;
- }
-
- /* pad out to rectStart if text is too short */
- if (outIndent < rectStart) {
- addPadding(outPtr, outIndent, rectStart, tabDist, useTabs, &len);
- outPtr += len;
- }
- outIndent = rectStart;
-
- /* Copy the text from "insLine" (if any), recalculating the tabs as if
- the inserted string began at column 0 to its new column destination */
- if (*insLine != '\0') {
- char *retabbedStr =
- realignTabs(insLine, 0, rectStart, tabDist, useTabs, &len);
- for (const char *c = retabbedStr; *c != '\0'; c++) {
- *outPtr++ = *c;
- len = Fl_Text_Buffer::character_width(c, outIndent, tabDist);
- outIndent += len;
- }
- free((void *) retabbedStr);
- }
-
- /* If the original line did not extend past "rectStart", that's all */
- if (*linePtr == '\0') {
- *outLen = *endOffset = outPtr - outStr;
- return;
- }
-
- /* Pad out to rectEnd + (additional original offset
- due to non-breaking character at right boundary) */
- addPadding(outPtr, outIndent, postRectIndent, tabDist, useTabs, &len);
- outPtr += len;
- outIndent = postRectIndent;
-
- /* copy the text beyond "rectEnd" */
- strcpy(outPtr, linePtr);
- *endOffset = outPtr - outStr;
- *outLen = (outPtr - outStr) + strlen(linePtr);
-}
-
-
-// simple setter
-// Unicode safe
void Fl_Text_Selection::set(int startpos, int endpos)
{
mSelected = startpos != endpos;
- mRectangular = 0;
mStart = min(startpos, endpos);
mEnd = max(startpos, endpos);
}
-// simple setter
-// Unicode safe
-void Fl_Text_Selection::set_rectangular(int startpos, int endpos,
- int rectStart, int rectEnd)
-{
- mSelected = rectStart < rectEnd;
- mRectangular = 1;
- mStart = startpos;
- mEnd = endpos;
- mRectStart = rectStart;
- mRectEnd = rectEnd;
-}
-
-
-// simple getter
-// Unicode safe
+/*
+ simple getter.
+ Unicode safe. Start and end will be at a character boundary.
+ */
int Fl_Text_Selection::position(int *startpos, int *endpos) const {
if (!mSelected)
return 0;
@@ -1816,83 +1240,65 @@ int Fl_Text_Selection::position(int *startpos, int *endpos) const {
}
-// simple getter
-// Unicode safe
-int Fl_Text_Selection::position(int *startpos, int *endpos,
- int *isRect, int *rectStart,
- int *rectEnd) const {
- if (!mSelected)
- return 0;
- *isRect = mRectangular;
- *startpos = mStart;
- *endpos = mEnd;
- if (mRectangular)
- {
- *rectStart = mRectStart;
- *rectEnd = mRectEnd;
+/*
+ Return if a position is inside the eselected area.
+ Unicode safe. Pos must be at a character boundary.
+ */
+int Fl_Text_Selection::includes(int pos) const {
+ return (selected() && pos >= start() && pos < end() );
}
- return 1;
-}
-
-// unicode safe
-int Fl_Text_Selection::includes(int pos, int lineStartPos, int dispIndex) const {
- return (selected()
- && ( (!rectangular() && pos >= start() && pos < end())
- || (rectangular() && pos >= start() && lineStartPos <= end()
- && dispIndex >= rect_start() && dispIndex < rect_end()
- )
- )
- );
-}
+/*
+ Return a duplicate of the selected text, or an empty string.
+ Unicode safe.
+ */
char *Fl_Text_Buffer::selection_text_(Fl_Text_Selection * sel) const {
- int start, end, isRect, rectStart, rectEnd;
+ int start, end;
/* If there's no selection, return an allocated empty string */
- if (!sel->position(&start, &end, &isRect, &rectStart, &rectEnd))
+ if (!sel->position(&start, &end))
{
char *s = (char *) malloc(1);
*s = '\0';
return s;
}
- /* If the selection is not rectangular, return the selected range */
- if (isRect)
- return text_in_rectangle(start, end, rectStart, rectEnd);
- else
+ /* Return the selected range */
return text_range(start, end);
}
+
+/*
+ Remove the selected text.
+ Unicode safe.
+ */
void Fl_Text_Buffer::remove_selection_(Fl_Text_Selection * sel)
{
- int start, end, isRect, rectStart, rectEnd;
+ int start, end;
- if (!sel->position(&start, &end, &isRect, &rectStart, &rectEnd))
+ if (!sel->position(&start, &end))
return;
- if (isRect)
- remove_rectangular(start, end, rectStart, rectEnd);
- else {
remove(start, end);
//undoyankcut = undocut;
}
-}
+/*
+ Replace selection with text.
+ Unicode safe.
+ */
void Fl_Text_Buffer::replace_selection_(Fl_Text_Selection * sel,
const char *text)
{
Fl_Text_Selection oldSelection = *sel;
/* If there's no selection, return */
- int start, end, isRect, rectStart, rectEnd;
- if (!sel->position(&start, &end, &isRect, &rectStart, &rectEnd))
+ int start, end;
+ if (!sel->position(&start, &end))
return;
/* Do the appropriate type of replace */
- if (isRect)
- replace_rectangular(start, end, rectStart, rectEnd, text);
- else
replace(start, end, text);
/* Unselect (happens automatically in BufReplace, but BufReplaceRect
@@ -1901,43 +1307,35 @@ void Fl_Text_Buffer::replace_selection_(Fl_Text_Selection * sel,
redisplay_selection(&oldSelection, sel);
}
-static void addPadding(char *string, int startIndent, int toIndent,
- int tabDist, int useTabs, int *charsAdded)
-{
- int indent = startIndent, len;
- char *outPtr = string;
-
- if (useTabs) {
- while (indent < toIndent) {
- //static char t = '\t';
- len = Fl_Text_Buffer::character_width("\t", indent, tabDist);
- if (len > 1 && indent + len <= toIndent) {
- *outPtr++ = '\t';
- indent += len;
- } else {
- *outPtr++ = ' ';
- indent++;
- }
- }
- } else {
- while (indent < toIndent) {
- *outPtr++ = ' ';
- indent++;
- }
- }
- *charsAdded = outPtr - string;
-}
-
+
+/*
+ Call all callbacks.
+ Unicode safe.
+ */
void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted,
int nInserted, int nRestyled,
const char *deletedText) const {
for (int i = 0; i < mNModifyProcs; i++)
(*mModifyProcs[i]) (pos, nInserted, nDeleted, nRestyled,
deletedText, mCbArgs[i]);
-} void Fl_Text_Buffer::call_predelete_callbacks(int pos, int nDeleted) const {
+}
+
+
+/*
+ Call all callbacks.
+ Unicode safe.
+ */
+void Fl_Text_Buffer::call_predelete_callbacks(int pos, int nDeleted) const {
for (int i = 0; i < mNPredeleteProcs; i++)
(*mPredeleteProcs[i]) (pos, nDeleted, mPredeleteCbArgs[i]);
-} void Fl_Text_Buffer::redisplay_selection(Fl_Text_Selection *
+}
+
+
+/*
+ Redisplay a new selected area.
+ Unicode safe.
+ */
+void Fl_Text_Buffer::redisplay_selection(Fl_Text_Selection *
oldSelection,
Fl_Text_Selection *
newSelection) const
@@ -1952,10 +1350,6 @@ void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted,
newStart = newSelection->mStart;
oldEnd = oldSelection->mEnd;
newEnd = newSelection->mEnd;
- if (oldSelection->mRectangular)
- oldEnd++;
- if (newSelection->mRectangular)
- newEnd++;
/* If the old or new selection is unselected, just redisplay the
single area that is (was) selected and return */
@@ -1971,20 +1365,6 @@ void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted,
return;
}
- /* If the selection changed from normal to rectangular or visa versa, or
- if a rectangular selection changed boundaries, redisplay everything */
- if ((oldSelection->mRectangular && !newSelection->mRectangular) ||
- (!oldSelection->mRectangular && newSelection->mRectangular) ||
- (oldSelection->mRectangular && ((oldSelection->mRectStart !=
- newSelection->mRectStart)
- || (oldSelection->mRectEnd !=
- newSelection->mRectEnd)))) {
- call_modify_callbacks(min(oldStart, newStart), 0, 0,
- max(oldEnd, newEnd) - min(oldStart,
- newStart), NULL);
- return;
- }
-
/* If the selections are non-contiguous, do two separate updates
and return */
if (oldEnd < newStart || newEnd < oldStart) {
@@ -2006,6 +1386,11 @@ void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted,
call_modify_callbacks(ch2Start, 0, 0, ch2End - ch2Start, NULL);
}
+
+/*
+ Move the gap around without changing buffer content.
+ Unicode safe. Pos must be at a character boundary.
+ */
void Fl_Text_Buffer::move_gap(int pos)
{
int gapLen = mGapEnd - mGapStart;
@@ -2018,6 +1403,11 @@ void Fl_Text_Buffer::move_gap(int pos)
mGapStart += pos - mGapStart;
}
+
+/*
+ Create a larger gap.
+ Unicode safe. Start must be at a character boundary.
+ */
void Fl_Text_Buffer::reallocate_with_gap(int newGapStart, int newGapLen)
{
char *newBuf = (char *) malloc(mLength + newGapLen);
@@ -2040,15 +1430,13 @@ void Fl_Text_Buffer::reallocate_with_gap(int newGapStart, int newGapLen)
mBuf = newBuf;
mGapStart = newGapStart;
mGapEnd = newGapEnd;
-#ifdef PURIFY
- {
- int i;
- for (i = mGapStart; i < mGapEnd; i++)
- mBuf[i] = '.';
}
-#endif
-}
+
+/*
+ Update selection range if characers were inserted.
+ Unicode safe. Pos must be at a character boundary.
+ */
void Fl_Text_Buffer::update_selections(int pos, int nDeleted,
int nInserted)
{
@@ -2061,6 +1449,7 @@ void Fl_Text_Buffer::update_selections(int pos, int nDeleted,
// unicode safe, assuming the arguments are on character boundaries
void Fl_Text_Selection::update(int pos, int nDeleted, int nInserted)
{
+ // FIXME: check if this is safe when seletion crosses selction boundaries
if (!mSelected || pos > mEnd)
return;
if (pos + nDeleted <= mStart) {
@@ -2080,283 +1469,69 @@ void Fl_Text_Selection::update(int pos, int nDeleted, int nInserted)
}
}
-int Fl_Text_Buffer::findchar_forward(int startPos, char searchChar,
- int *foundPos) const {
- if (startPos < 0 || startPos >= mLength)
+
+/*
+ Find a UCS-4 character.
+ Unicode safe. StartPos must be at a charcter boundary, searchChar is UCS-4 encoded.
+ */
+int Fl_Text_Buffer::findchar_forward(int startPos, unsigned searchChar,
+ int *foundPos) const
+ {
+ if (startPos >= mLength)
{
*foundPos = mLength;
return 0;
}
- int pos = startPos;
- while (pos < mGapStart) {
- if (mBuf[pos] == searchChar) {
- *foundPos = pos;
- return 1;
- }
- pos++;
- }
+ if (startPos<0)
+ startPos = 0;
- for (int gapLen = mGapEnd - mGapStart; pos < mLength; pos++) {
- if (mBuf[pos + gapLen] == searchChar) {
- *foundPos = pos;
+ // TODO: for performance reasons, we can re-insert the ASCII search here which is about three times as fast if searchChar is <128
+
+ for ( ; startPos<mLength; startPos = next_char(startPos)) {
+ if (searchChar == char_at(startPos)) {
+ *foundPos = startPos;
return 1;
}
}
+
*foundPos = mLength;
return 0;
}
-int Fl_Text_Buffer::findchar_backward(int startPos, char searchChar,
- int *foundPos) const {
- if (startPos <= 0 || startPos > mLength)
- {
+/*
+ Find a UCS-4 character.
+ Unicode safe. StartPos must be at a charcter boundary, searchChar is UCS-4 encoded.
+ */
+int Fl_Text_Buffer::findchar_backward(int startPos, unsigned searchChar,
+ int *foundPos) const {
+ if (startPos <= 0) {
*foundPos = 0;
return 0;
}
- int pos = startPos - 1;
- for (int gapLen = mGapEnd - mGapStart; pos >= mGapStart; pos--) {
- if (mBuf[pos + gapLen] == searchChar) {
- *foundPos = pos;
- return 1;
- }
- }
+ if (startPos > mLength)
+ startPos = mLength;
+
+ // TODO: for performance reasons, we can re-insert the ASCII search here which is about three times as fast if searchChar is <128
- for (; pos >= 0; pos--) {
- if (mBuf[pos] == searchChar) {
- *foundPos = pos;
+ for (startPos = prev_char(startPos); startPos>=0; startPos = prev_char(startPos)) {
+ if (searchChar == char_at(startPos)) {
+ *foundPos = startPos;
return 1;
}
}
+
*foundPos = 0;
return 0;
}
-/*
- Copies from \p text to end up to but not including newline (or end of \p text)
- and return the copy as the function value, and the length of the line in
- \p lineLen
- */
-static char *copyLine(const char *text, int *lineLen)
-{
- int len = 0;
-
- for (const char *c = text; *c != '\0' && *c != '\n'; c++)
- len++;
- char *outStr = (char *) malloc(len + 1);
- strlcpy(outStr, text, len + 1);
- *lineLen = len;
- return outStr;
-}
-
-/*
- Counts the number of newlines in a null-terminated text string.
- Unicode tested.
- */
-static int countLines(const char *string)
-{
- int lineCount = 0;
- for (const char *c = string; *c != '\0'; c++)
- if (*c == '\n')
- lineCount++;
- return lineCount;
-}
-
/*
- Measures the width in displayed characters of string \p text
+ Insert text from a file.
+ Unicode safe. Inout must be correct utf8!
*/
-static int textWidth(const char *text, int tabDist)
-{
- int width = 0, maxWidth = 0;
-
- // HUH? Why is "c" incremented? Shouldn't "text" be incrmented?
- // FIXME: increment is wrong!
- for (const char *c = text; *c != '\0'; c++) {
- if (*c == '\n') {
- if (width > maxWidth)
- maxWidth = width;
- width = 0;
- } else
- width += Fl_Text_Buffer::character_width(c, width, tabDist);
- }
- if (width > maxWidth)
- return width;
- return maxWidth;
-}
-
-void Fl_Text_Buffer::rectangular_selection_boundaries(int lineStartPos,
- int rectStart,
- int rectEnd,
- int *selStart,
- int *selEnd) const
-{
- int pos, width, indent = 0;
- char c;
-
- /* find the start of the selection */
- for (pos = lineStartPos; pos < mLength; pos++)
- {
- // FIXME: character is ucs-4
- c = character(pos);
- if (c == '\n')
- break;
- width =
- Fl_Text_Buffer::character_width(&c, indent, mTabDist); // FIXME: c is not unicode
- if (indent + width > rectStart) {
- if (indent != rectStart && c != '\t') {
- pos++;
- indent += width;
- }
- break;
- }
- indent += width;
- }
- *selStart = pos;
-
- /* find the end */
- for (; pos < mLength; pos++) {
- // FIXME: character is ucs-4
- c = character(pos);
- if (c == '\n')
- break;
- width =
- Fl_Text_Buffer::character_width(&c, indent, mTabDist); // FIXME: c is not unicode
- indent += width;
- if (indent > rectEnd) {
- if (indent - width != rectEnd && c != '\t')
- pos++;
- break;
- }
- }
- *selEnd = pos;
-}
-
-/*
- Adjust the space and tab characters from string \p text so that non-white
- characters remain stationary when the text is shifted from starting at
- \p origIndent to starting at \p newIndent. Returns an allocated string
- which must be freed by the caller with XtFree.
- */
-static char *realignTabs(const char *text, int origIndent, int newIndent,
- int tabDist, int useTabs, int *newLength)
-{
- /* If the tabs settings are the same, retain original tabs */
- int len;
- char *outStr;
- if (origIndent % tabDist == newIndent % tabDist) {
- len = strlen(text);
- outStr = (char *) malloc(len + 1);
- strcpy(outStr, text);
- *newLength = len;
- return outStr;
- }
-
- /* If the tab settings are not the same, brutally convert tabs to
- spaces, then back to tabs in the new position */
- char *expStr = expandTabs(text, origIndent, tabDist, &len);
- if (!useTabs) {
- *newLength = len;
- return expStr;
- }
- outStr =
- unexpandTabs(expStr, newIndent, tabDist, newLength);
- free((void *) expStr);
- return outStr;
-}
-
-/*
- Expand tabs to spaces for a block of text. The additional parameter
- \p startIndent if nonzero, indicates that the text is a rectangular selection
- beginning at column \p startIndent
- */
-static char *expandTabs(const char *text, int startIndent, int tabDist, int *newLen)
-{
- /* rehearse the expansion to figure out length for output string */
- int indent = startIndent, len, outLen = 0;
- const char *c;
- for (c = text; *c != '\0'; c++) {
- if (*c == '\t') {
- len =
- Fl_Text_Buffer::character_width(c, indent, tabDist);
- outLen += len;
- indent += len;
- } else if (*c == '\n') {
- indent = startIndent;
- outLen++;
- } else {
- // FIXME: character_width does not return number of bytes for UTF-8!
- indent +=
- Fl_Text_Buffer::character_width(c, indent, tabDist);
- outLen++;
- }
- }
-
- /* do the expansion */
- char *outStr = (char *) malloc(outLen + 1);
- char *outPtr = outStr;
- indent = startIndent;
- for (c = text; *c != '\0'; c++) {
- if (*c == '\t') {
- len =
- Fl_Text_Buffer::expand_character(c, indent, outPtr, tabDist);
- outPtr += len;
- indent += len;
- } else if (*c == '\n') {
- indent = startIndent;
- *outPtr++ = *c;
- } else {
- // FIXME: character_width does not return number of bytes for UTF-8!
- indent +=
- Fl_Text_Buffer::character_width(c, indent, tabDist);
- *outPtr++ = *c;
- }
- }
- outStr[outLen] = '\0';
- *newLen = outLen;
- return outStr;
-}
-
-/*
- Convert sequences of spaces into tabs. The threshold for conversion is
- when 3 or more spaces can be converted into a single tab, this avoids
- converting double spaces after a period withing a block of text.
- */
-static char *unexpandTabs(char *text, int startIndent, int tabDist, int *newLen)
-{
- char expandedChar[FL_TEXT_MAX_EXP_CHAR_LEN];
- char *outStr = (char *) malloc(strlen(text) + 1);
- char *outPtr = outStr;
- int indent = startIndent, len;
-
- for (const char *c = text; *c != '\0';) {
- if (*c == ' ') {
- static char tab = '\t';
- len =
- Fl_Text_Buffer::expand_character(&tab, indent, expandedChar, tabDist);
- if (len >= 3 && !strncmp(c, expandedChar, len)) {
- c += len;
- *outPtr++ = '\t';
- indent += len;
- } else {
- *outPtr++ = *c++;
- indent++;
- }
- } else if (*c == '\n') {
- indent = startIndent;
- *outPtr++ = *c++;
- } else {
- *outPtr++ = *c++;
- indent++;
- }
- }
- *outPtr = '\0';
- *newLen = outPtr - outStr;
- return outStr;
-}
-
int Fl_Text_Buffer::insertfile(const char *file, int pos, int buflen)
{
FILE *fp;
@@ -2374,6 +1549,11 @@ int Fl_Text_Buffer::insertfile(const char *file, int pos, int buflen)
return e;
}
+
+/*
+ Write text to file,
+ Unicode safe.
+ */
int Fl_Text_Buffer::outputfile(const char *file, int start, int end,
int buflen)
{
@@ -2394,6 +1574,39 @@ int Fl_Text_Buffer::outputfile(const char *file, int start, int end,
}
+/*
+ Return the previous character position.
+ Uncode safe.
+ */
+int Fl_Text_Buffer::prev_char(int pos) const
+{
+ if (pos<=0)
+ return 0;
+
+ char c;
+ do {
+ pos--;
+ if (pos==0)
+ return 0;
+ c = byte_at(pos);
+ } while ( (c&0xc0) == 0x80);
+
+ return pos;
+}
+
+/*
+ Return the next character position.
+ Uncode safe.
+ */
+int Fl_Text_Buffer::next_char(int pos) const
+{
+ int n = fl_utf8len(byte_at(pos));
+ pos += n;
+ if (pos>=mLength)
+ return mLength;
+ return pos;
+}
+
//
// End of "$Id$".
//