diff options
| author | Matthias Melcher <fltk@matthiasm.com> | 2010-11-03 22:01:43 +0000 |
|---|---|---|
| committer | Matthias Melcher <fltk@matthiasm.com> | 2010-11-03 22:01:43 +0000 |
| commit | cac40a9b024185cfa5f10d5e7845582ab30a5dd1 (patch) | |
| tree | 186b289e6429408c98c7a6b27e4eacd7a06d69b5 /src | |
| parent | ddd4bbff1b07daf5510b1c9aa233876cae332264 (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')
| -rw-r--r-- | src/Fl_Text_Buffer.cxx | 1695 | ||||
| -rw-r--r-- | src/Fl_Text_Display.cxx | 578 | ||||
| -rw-r--r-- | src/Fl_Text_Editor.cxx | 4 |
3 files changed, 660 insertions, 1617 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$". // diff --git a/src/Fl_Text_Display.cxx b/src/Fl_Text_Display.cxx index 88a2f084f..4fb6352ba 100644 --- a/src/Fl_Text_Display.cxx +++ b/src/Fl_Text_Display.cxx @@ -129,7 +129,6 @@ Fl_Text_Display::Fl_Text_Display(int X, int Y, int W, int H, const char* l) mCursor_color = FL_FOREGROUND_COLOR; - mFixedFontWidth = -1; mStyleBuffer = 0; mStyleTable = 0; mNStyles = 0; @@ -211,6 +210,8 @@ void Fl_Text_Display::buffer( Fl_Text_Buffer *buf ) { (see extendRangeForStyleMods for more information on this protocol). Style buffers, tables and their associated memory are managed by the caller. + + Styles are ranged from 65 ('A') to 126. */ void Fl_Text_Display::highlight_data(Fl_Text_Buffer *styleBuffer, const Style_Table_Entry *styleTable, @@ -429,7 +430,7 @@ void Fl_Text_Display::redisplay_range(int startpos, int endpos) { int ok = 0; while (!ok && startpos > 0) { // FIXME: character is ucs-4 - char c = buffer()->character( startpos ); + char c = buffer()->char_at( startpos ); if (!((c & 0x80) && !(c & 0x40))) { ok = 1; } else { @@ -438,7 +439,7 @@ void Fl_Text_Display::redisplay_range(int startpos, int endpos) { } while (!ok && endpos < buffer()->length()) { // FIXME: character is ucs-4 - char c = buffer()->character( endpos ); + char c = buffer()->char_at( endpos ); if (!((c & 0x80) && !(c & 0x40))) { ok = 1; } else { @@ -626,7 +627,7 @@ void Fl_Text_Display::overstrike(const char* text) { startIndent = mBuffer->count_displayed_characters( lineStart, startPos ); indent = startIndent; for ( c = text; *c != '\0'; c++ ) - indent += Fl_Text_Buffer::character_width( c, indent, buf->tab_distance() ); + indent++; endIndent = indent; /* find which characters to remove, and if necessary generate additional @@ -636,11 +637,10 @@ void Fl_Text_Display::overstrike(const char* text) { if ( p == buf->length() ) break; // FIXME: character is ucs-4 - ch = buf->character( p ); + ch = buf->char_at( p ); if ( ch == '\n' ) break; - const char *s = buf->address(p); - indent += Fl_Text_Buffer::character_width(s, indent, buf->tab_distance() ); // FIXME: not unicode + indent++; if ( indent == endIndent ) { p++; break; @@ -674,32 +674,19 @@ void Fl_Text_Display::overstrike(const char* text) { */ int Fl_Text_Display::position_to_xy( int pos, int* X, int* Y ) const { - int charIndex, lineStartPos, fontHeight, lineLen; - int visLineNum, charLen, outIndex, xStep, charStyle; - char expandedChar[ FL_TEXT_MAX_EXP_CHAR_LEN ]; - const char *lineStr; - -// printf("position_to_xy(pos=%d, X=%p, Y=%p)\n", pos, X, Y); + int lineStartPos, fontHeight, lineLen; + int visLineNum; /* If position is not displayed, return false */ if (pos < mFirstChar || (pos > mLastChar && !empty_vlines())) { -// printf(" returning 0\n" -// " mFirstChar=%d, mLastChar=%d, empty_vlines()=0\n", -// mFirstChar, mLastChar); return 0; } /* Calculate Y coordinate */ if (!position_to_line(pos, &visLineNum)) { -// puts(" returning 0\n" -// " position_to_line()=0"); return 0; } - if (visLineNum < 0 || visLineNum > mNBufferLines) { -// printf(" returning 0\n" -// " visLineNum=%d, mNBufferLines=%d\n", -// visLineNum, mNBufferLines); return 0; } @@ -715,25 +702,7 @@ int Fl_Text_Display::position_to_xy( int pos, int* X, int* Y ) const { return 1; } lineLen = vline_length( visLineNum ); - lineStr = mBuffer->text_range( lineStartPos, lineStartPos + lineLen ); - - /* Step through character positions from the beginning of the line - to "pos" to calculate the X coordinate */ - xStep = text_area.x - mHorizOffset; - outIndex = 0; - for (charIndex = 0; - charIndex < lineLen && charIndex < pos - lineStartPos; - charIndex += fl_utf8len(lineStr[charIndex]) ) - { - charLen = Fl_Text_Buffer::expand_character( lineStr+charIndex, outIndex, expandedChar, - mBuffer->tab_distance()); - charStyle = position_style( lineStartPos, lineLen, charIndex, - outIndex ); - xStep += string_width( expandedChar, charLen, charStyle ); - outIndex += charLen; - } - *X = xStep; - free((char *)lineStr); + *X = text_area.x + handle_vline(GET_WIDTH, lineStartPos, pos-lineStartPos, 0, 0, 0, 0, 0, 0) - mHorizOffset; return 1; } @@ -774,24 +743,10 @@ int Fl_Text_Display::position_to_linecol( int pos, int* lineNum, int* column ) c Return 1 if position (X, Y) is inside of the primary Fl_Text_Selection */ int Fl_Text_Display::in_selection( int X, int Y ) const { - int row, column, pos = xy_to_position( X, Y, CHARACTER_POS ); + int pos = xy_to_position( X, Y, CHARACTER_POS ); Fl_Text_Buffer *buf = mBuffer; - int ok = 0; - while (!ok) { - // FIXME: character is ucs-4 - char c = buffer()->character( pos ); - if (!((c & 0x80) && !(c & 0x40))) { - ok = 1; - } else { - pos++; + return buf->primary_selection()->includes(pos); } - } - - xy_to_rowcol( X, Y, &row, &column, CHARACTER_POS ); - if (range_touches_selection(buf->primary_selection(), mFirstChar, mLastChar)) - column = wrapped_column(row, column); - return buf->primary_selection()->includes(pos, buf->line_start( pos ), column); -} /** Correct a column number based on an unconstrained position (as returned by @@ -886,33 +841,30 @@ void Fl_Text_Display::show_insert_position() { /* Cursor movement functions */ -/** Moves the current insert position right one character.*/ + +/** + Moves the current insert position right one character. + \return 1 if the cursor moved, 0 if the end of the text was reached + */ int Fl_Text_Display::move_right() { - int ok = 0; - while (!ok) { if ( mCursorPos >= mBuffer->length() ) return 0; - insert_position( mCursorPos + 1 ); - int pos = insert_position(); - // FIXME: character is ucs-4 - char c = buffer()->character( pos ); - if (!((c & 0x80) && !(c & 0x40))) ok = 1; - } + int p = insert_position(); + int q = buffer()->next_char(p); + insert_position(q); return 1; } -/** Moves the current insert position left one character.*/ +/** + Moves the current insert position left one character. + \return 1 if the cursor moved, 0 if the beginning of the text was reached + */ int Fl_Text_Display::move_left() { - int ok = 0; - while (!ok) { if ( mCursorPos <= 0 ) return 0; - insert_position( mCursorPos - 1 ); - int pos = insert_position(); - // FIXME: character is ucs-4 - char c = buffer()->character( pos ); - if (!((c & 0x80) && !(c & 0x40))) ok = 1; - } + int p = insert_position(); + int q = buffer()->prev_char(p); + insert_position(q); return 1; } @@ -951,7 +903,7 @@ int Fl_Text_Display::move_up() { while (!ok) { int pos = insert_position(); // FIXME: character is ucs-4 - char c = buffer()->character( pos ); + char c = buffer()->char_at( pos ); if (!((c & 0x80) && !(c & 0x40))) { ok = 1; } else { @@ -988,7 +940,7 @@ int Fl_Text_Display::move_down() { while (!ok) { int pos = insert_position(); // FIXME: character is ucs-4 - char c = buffer()->character( pos ); + char c = buffer()->char_at( pos ); if (!((c & 0x80) && !(c & 0x40))) { ok = 1; } else { @@ -1138,11 +1090,11 @@ static inline int fl_isseparator(int c) { void Fl_Text_Display::next_word() { int pos = insert_position(); // FIXME: character is ucs-4 - while (pos < buffer()->length() && !fl_isseparator(buffer()->character(pos))) { + while (pos < buffer()->length() && !fl_isseparator(buffer()->char_at(pos))) { pos++; } // FIXME: character is ucs-4 - while (pos < buffer()->length() && fl_isseparator(buffer()->character(pos))) { + while (pos < buffer()->length() && fl_isseparator(buffer()->char_at(pos))) { pos++; } @@ -1155,15 +1107,15 @@ void Fl_Text_Display::previous_word() { if (pos==0) return; pos--; // FIXME: character is ucs-4 - while (pos && fl_isseparator(buffer()->character(pos))) { + while (pos && fl_isseparator(buffer()->char_at(pos))) { pos--; } // FIXME: character is ucs-4 - while (pos && !fl_isseparator(buffer()->character(pos))) { + while (pos && !fl_isseparator(buffer()->char_at(pos))) { pos--; } // FIXME: character is ucs-4 - if (fl_isseparator(buffer()->character(pos))) pos++; + if (fl_isseparator(buffer()->char_at(pos))) pos++; insert_position( pos ); } @@ -1174,8 +1126,7 @@ void Fl_Text_Display::previous_word() { */ void Fl_Text_Display::buffer_predelete_cb(int pos, int nDeleted, void *cbArg) { Fl_Text_Display *textD = (Fl_Text_Display *)cbArg; - if (textD->mContinuousWrap && - (textD->mFixedFontWidth == -1 || textD->mModifyingTabDistance)) + if (textD->mContinuousWrap) /* Note: we must perform this measurement, even if there is not a single character deleted; the number of "deleted" lines is the number of visual lines spanned by the real line in which the @@ -1401,6 +1352,125 @@ int Fl_Text_Display::position_to_line( int pos, int *lineNum ) const { return 0; /* probably never be reached */ } +// We use a single function that handles all line layout, measuring, and drawing +// - draw a text range +// - return the width of a text range in pixels +// - return the index of a char that is at a pixel position +// +// - we need to handle hidden hyphens and tabs here! +// - we handle all styles and selections +// +// needs: +// mode: draw, find width, find index +// index of first character +// size of string in bytes +// x and y drawing position +// width and height of drawing rectangle +// +// returns: +// width of drawing in pixels +// index of last character that fits into the box +// +// enum { DRAW_LINE, FIND_INDEX, GET_WIDTH }; +// +// +int Fl_Text_Display::handle_vline( + int mode, + int lineStartPos, int lineLen, int leftChar, int rightChar, + int Y, int bottomClip, + int leftClip, int rightClip) const +{ + // FIXME: we need to allow two modes for FIND_INDEX: one on the edge of the + // FIXME: character for selection, and one on the character center for cursors. + int i, X, startX, startIndex, style, charStyle; + char *lineStr; + + if ( lineStartPos == -1 ) { + lineStr = NULL; + } else { + lineStr = mBuffer->text_range( lineStartPos, lineStartPos + lineLen ); + } + + if (mode==GET_WIDTH) + X = 0; + else + X = text_area.x - mHorizOffset; + + startX = X; + startIndex = 0; + if (!lineStr) { + // just clear the background + if (mode==DRAW_LINE) { + style = position_style(lineStartPos, lineLen, -1); + draw_string( style|BG_ONLY_MASK, text_area.x, Y, text_area.x+text_area.w, lineStr, lineLen ); + } + if (mode==FIND_INDEX) + return lineStartPos; + return 0; + } + + // draw the line + style = position_style(lineStartPos, lineLen, i); + for (i=0; i<lineLen; ) { + int len = fl_utf8len(lineStr[i]); + charStyle = position_style(lineStartPos, lineLen, i); + // FIXME: if the character is a tab, we need to do the correct indenting + // FIXME: if the character is an optional hyphen, we need to ignore it unless we wrap the text + if (charStyle!=style) { + // draw a segment whenever the style changes + int w = string_width( lineStr+startIndex, i-startIndex, style ); + if (mode==DRAW_LINE) + draw_string( style, startX, Y, startX+w, lineStr+startIndex, i-startIndex ); + if (mode==FIND_INDEX && startX+w>rightClip) { + // find x pos inside block + int di = find_x(lineStr+startIndex, i-startIndex, style, rightClip-startX); + free(lineStr); + return lineStartPos + startIndex + di; + } + style = charStyle; + startX += w; + startIndex = i; + } + i += len; + } + int w = string_width( lineStr+startIndex, i-startIndex, style ); + if (mode==DRAW_LINE) + draw_string( style, startX, Y, startX+w, lineStr+startIndex, i-startIndex ); + if (mode==FIND_INDEX) { + // find x pos inside block + int di = find_x(lineStr+startIndex, i-startIndex, style, rightClip-startX); + free(lineStr); + return lineStartPos + startIndex + di; + } + if (mode==GET_WIDTH) { + free(lineStr); + return startX+w; + } + + // clear the rest of the line + startX += w; + style = position_style(lineStartPos, lineLen, i); + if (mode==DRAW_LINE) + draw_string( style|BG_ONLY_MASK, startX, Y, text_area.x+text_area.w, lineStr, lineLen ); + + free(lineStr); + return lineStartPos + lineLen; +} + +int Fl_Text_Display::find_x(const char *s, int len, int style, int x) const { + // FIXME: use binary search which is much quicker! + int i = 0; + while (i<len) { + int cl = fl_utf8len(s[i]); + int w = string_width(s, i+cl, style); + if (w>x) + return i; + i += cl; + } + return len; +} + + /** Draw the text on a single line represented by "visLineNum" (the number of lines down from the top of the display), limited by @@ -1410,14 +1480,8 @@ int Fl_Text_Display::position_to_line( int pos, int *lineNum ) const { */ void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip, int leftCharIndex, int rightCharIndex) { - Fl_Text_Buffer * buf = mBuffer; - int i, X, Y, startX, charIndex, lineStartPos, lineLen, fontHeight; - int stdCharWidth, charWidth, startIndex, charStyle, style; - int charLen, outStartIndex, outIndex; - int dispIndexOffset; - char expandedChar[ FL_TEXT_MAX_EXP_CHAR_LEN ], outStr[ MAX_DISP_LINE_LEN ]; - char *outPtr; - const char *lineStr; + int Y, lineStartPos, lineLen, fontHeight; + int stdCharWidth; // printf("draw_vline(visLineNum=%d, leftClip=%d, rightClip=%d, leftCharIndex=%d, rightCharIndex=%d)\n", // visLineNum, leftClip, rightClip, leftCharIndex, rightCharIndex); @@ -1436,10 +1500,8 @@ void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip, // printf("lineStartPos=%d\n", lineStartPos); if ( lineStartPos == -1 ) { lineLen = 0; - lineStr = NULL; } else { lineLen = vline_length( visLineNum ); - lineStr = buf->text_range( lineStartPos, lineStartPos + lineLen ); } /* Space beyond the end of the line is still counted in units of characters @@ -1450,7 +1512,6 @@ void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip, stdCharWidth = TMPFONTWIDTH; //mFontStruct->max_bounds.width; if ( stdCharWidth <= 0 ) { Fl::error("Fl_Text_Display::draw_vline(): bad font measurement"); - if (lineStr) free((void *)lineStr); return; } @@ -1458,167 +1519,11 @@ void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip, leftClip = max( text_area.x, leftClip ); rightClip = min( rightClip, text_area.x + text_area.w ); - /* Rectangular Fl_Text_Selections are based on "real" line starts (after - a newline or start of buffer). Calculate the difference between the - last newline position and the line start we're using. Since scanning - back to find a newline is expensive, only do so if there's actually a - rectangular Fl_Text_Selection which needs it */ - if (mContinuousWrap && - (range_touches_selection(buf->primary_selection(), - lineStartPos, lineStartPos + lineLen) || - range_touches_selection(buf->secondary_selection(), - lineStartPos, lineStartPos + lineLen) || - range_touches_selection(buf->highlight_selection(), - lineStartPos, lineStartPos + lineLen))) - { - dispIndexOffset = buf->count_displayed_characters( - buf->line_start(lineStartPos), lineStartPos); - } else - dispIndexOffset = 0; - - /* Step through character positions from the beginning of the line (even if - that's off the left edge of the displayed area) to find the first - character position that's not clipped, and the X coordinate for drawing - that character */ - X = text_area.x - mHorizOffset; - outIndex = 0; - for (charIndex = 0; - ; - charIndex += lineStr ? fl_utf8len(lineStr[charIndex]) : 1 ) - { - charLen = charIndex >= lineLen ? 1 : - Fl_Text_Buffer::expand_character( lineStr+charIndex, outIndex, - expandedChar, buf->tab_distance()); - style = position_style( lineStartPos, lineLen, charIndex, - outIndex + dispIndexOffset ); - charWidth = charIndex >= lineLen ? stdCharWidth : - string_width( expandedChar, charLen, style ); - if ( X + charWidth >= leftClip && charIndex >= leftCharIndex ) { - startIndex = charIndex; - outStartIndex = outIndex; - startX = X; - break; - } - X += charWidth; - outIndex += charLen; - } - - /* Scan character positions from the beginning of the clipping range, and - draw parts whenever the style changes (also note if the cursor is on - this line, and where it should be drawn to take advantage of the x - position which we've gone to so much trouble to calculate) */ - /* since characters between style may overlap, we draw the full - background first */ - int sX = startX; - outPtr = outStr; - outIndex = outStartIndex; - X = startX; - for (charIndex = startIndex; - charIndex < rightCharIndex; - charIndex += lineStr ? fl_utf8len(lineStr[charIndex]) : 1 ) - { - charLen = charIndex >= lineLen ? 1 : - Fl_Text_Buffer::expand_character( lineStr+charIndex, outIndex, expandedChar, - buf->tab_distance()); - charStyle = position_style( lineStartPos, lineLen, charIndex, - outIndex + dispIndexOffset ); - for ( i = 0; i < charLen; i++ ) { // FIXME: this rips apart the utf-8 sequneces - if ( i != 0 && charIndex < lineLen && lineStr[ charIndex ] == '\t' ) - charStyle = position_style( lineStartPos, lineLen, - charIndex, outIndex + dispIndexOffset ); - if ( charStyle != style ) { - draw_string( style|BG_ONLY_MASK, sX, Y, X, outStr, outPtr - outStr ); - outPtr = outStr; - sX = X; - style = charStyle; - } - if ( charIndex < lineLen ) { - *outPtr = expandedChar[ i ]; - int l = 1; - if (*outPtr & 0x80) { - l = fl_utf8len(*outPtr); - if (l<=0) l = 1; - } - charWidth = string_width( &expandedChar[ i ], l, charStyle ); - } else - charWidth = stdCharWidth; - outPtr++; - X += charWidth; - outIndex++; - } - if ( outPtr - outStr + FL_TEXT_MAX_EXP_CHAR_LEN >= MAX_DISP_LINE_LEN || X >= rightClip ) - break; - } - draw_string( style|BG_ONLY_MASK, sX, Y, X, outStr, outPtr - outStr ); - - /* now draw the text over the previously erased background */ - outPtr = outStr; - outIndex = outStartIndex; - X = startX; - for (charIndex = startIndex; - charIndex < rightCharIndex; - charIndex += lineStr ? fl_utf8len(lineStr[charIndex]) : 0) - { - charLen = charIndex >= lineLen ? 1 : - Fl_Text_Buffer::expand_character( lineStr+charIndex, outIndex, expandedChar, - buf->tab_distance()); - charStyle = position_style( lineStartPos, lineLen, charIndex, - outIndex + dispIndexOffset ); - for ( i = 0; i < charLen; i++ ) { // FIXME: this rips apart the utf-8 sequneces - if ( i != 0 && charIndex < lineLen && lineStr[ charIndex ] == '\t' ) - charStyle = position_style( lineStartPos, lineLen, - charIndex, outIndex + dispIndexOffset ); - if ( charStyle != style ) { - draw_string( style|TEXT_ONLY_MASK, startX, Y, X, outStr, outPtr - outStr ); - outPtr = outStr; - startX = X; - style = charStyle; - } - if ( charIndex < lineLen ) { - *outPtr = expandedChar[ i ]; - int l = 1; - if (*outPtr & 0x80) { - l = fl_utf8len(*outPtr); - if (l<=0) l = 1; - } - charWidth = string_width( &expandedChar[ i ], l, charStyle ); - } else - charWidth = stdCharWidth; - outPtr++; - X += charWidth; - outIndex++; - } - if ( outPtr - outStr + FL_TEXT_MAX_EXP_CHAR_LEN >= MAX_DISP_LINE_LEN || X >= rightClip ) - break; - } - - /* Draw the remaining style segment */ - draw_string( style|TEXT_ONLY_MASK, startX, Y, X, outStr, outPtr - outStr ); - - /* Draw the cursor if part of it appeared on the redisplayed part of - this line. Also check for the cases which are not caught as the - line is scanned above: when the cursor appears at the very end - of the redisplayed section. */ - /* CET - FIXME - if ( mCursorOn ) - { - if ( hasCursor ) - draw_cursor( cursorX, Y ); - else if ( charIndex < lineLen && ( lineStartPos + charIndex + 1 == cursorPos ) - && X == rightClip ) - { - if ( cursorPos >= buf->length() ) - draw_cursor( X - 1, Y ); - else - { - draw_cursor( X - 1, Y ); - } - } + handle_vline(DRAW_LINE, + lineStartPos, lineLen, leftCharIndex, rightCharIndex, + Y, Y+fontHeight, leftClip, rightClip); + return; } - */ - if ( lineStr != NULL ) - free((void *)lineStr); -} /** Draw a string or blank area according to parameter "style", using the @@ -1628,8 +1533,9 @@ void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip, rectangle where text would have drawn from X to toX and from Y to the maximum Y extent of the current font(s). */ -void Fl_Text_Display::draw_string( int style, int X, int Y, int toX, - const char *string, int nChars ) { +void Fl_Text_Display::draw_string(int style, + int X, int Y, int toX, + const char *string, int nChars) const { const Style_Table_Entry * styleRec; /* Draw blank area rather than text, if that was the request */ @@ -1712,8 +1618,9 @@ void Fl_Text_Display::draw_string( int style, int X, int Y, int toX, /** Clear a rectangle with the appropriate background color for "style" */ -void Fl_Text_Display::clear_rect( int style, int X, int Y, - int width, int height ) { +void Fl_Text_Display::clear_rect(int style, + int X, int Y, + int width, int height) const { /* A width of zero means "clear to end of window" to XClearArea */ if ( width == 0 ) return; @@ -1821,8 +1728,8 @@ void Fl_Text_Display::draw_cursor( int X, int Y ) { Note that style is a somewhat incorrect name, drawing method would be more appropriate. */ -int Fl_Text_Display::position_style( int lineStartPos, - int lineLen, int lineIndex, int dispIndex ) const { +int Fl_Text_Display::position_style( int lineStartPos, int lineLen, int lineIndex) const +{ Fl_Text_Buffer * buf = mBuffer; Fl_Text_Buffer *styleBuf = mStyleBuffer; int pos, style = 0; @@ -1835,20 +1742,18 @@ int Fl_Text_Display::position_style( int lineStartPos, if ( lineIndex >= lineLen ) style = FILL_MASK; else if ( styleBuf != NULL ) { - // FIXME: character is ucs-4 - style = ( unsigned char ) styleBuf->character( pos ); + style = ( unsigned char ) styleBuf->byte_at( pos ); if (style == mUnfinishedStyle && mUnfinishedHighlightCB) { /* encountered "unfinished" style, trigger parsing */ (mUnfinishedHighlightCB)( pos, mHighlightCBArg); - // FIXME: character is ucs-4 - style = (unsigned char) styleBuf->character( pos); + style = (unsigned char) styleBuf->byte_at( pos); } } - if (buf->primary_selection()->includes(pos, lineStartPos, dispIndex)) + if (buf->primary_selection()->includes(pos)) style |= PRIMARY_MASK; - if (buf->highlight_selection()->includes(pos, lineStartPos, dispIndex)) + if (buf->highlight_selection()->includes(pos)) style |= HIGHLIGHT_MASK; - if (buf->secondary_selection()->includes(pos, lineStartPos, dispIndex)) + if (buf->secondary_selection()->includes(pos)) style |= SECONDARY_MASK; return style; } @@ -1884,10 +1789,8 @@ int Fl_Text_Display::string_width( const char *string, int length, int style ) c closest to (X, Y). */ int Fl_Text_Display::xy_to_position( int X, int Y, int posType ) const { - int charIndex, lineStart, lineLen, fontHeight; - int charWidth, charLen, charStyle, visLineNum, xStep, outIndex; - char expandedChar[ FL_TEXT_MAX_EXP_CHAR_LEN ]; - const char *lineStr; + int lineStart, lineLen, fontHeight; + int visLineNum; /* Find the visible line number corresponding to the Y coordinate */ fontHeight = mMaxsize; @@ -1906,33 +1809,12 @@ int Fl_Text_Display::xy_to_position( int X, int Y, int posType ) const { /* Get the line text and its length */ lineLen = vline_length( visLineNum ); - lineStr = mBuffer->text_range( lineStart, lineStart + lineLen ); - - /* Step through character positions from the beginning of the line - to find the character position corresponding to the X coordinate */ - xStep = text_area.x - mHorizOffset; - outIndex = 0; - for (charIndex = 0; - charIndex < lineLen; - charIndex += fl_utf8len(lineStr[charIndex]) ) - { - charLen = Fl_Text_Buffer::expand_character( lineStr+charIndex, outIndex, expandedChar, - mBuffer->tab_distance()); - charStyle = position_style( lineStart, lineLen, charIndex, outIndex ); - charWidth = string_width( expandedChar, charLen, charStyle ); - if ( X < xStep + ( posType == CURSOR_POS ? charWidth / 2 : charWidth ) ) { - free((char *)lineStr); - return lineStart + charIndex; - } - xStep += charWidth; - outIndex += charLen; - } - /* If the X position was beyond the end of the line, return the position - of the newline at the end of the line */ - free((char *)lineStr); - return lineStart + lineLen; -} + return handle_vline(FIND_INDEX, + lineStart, lineLen, 0, 0, + 0, 0, + text_area.x, X); + } /** Translate window coordinates to the nearest row and column number for @@ -2341,42 +2223,12 @@ static int countlines( const char *string ) { Return the width in pixels of the displayed line pointed to by "visLineNum" */ int Fl_Text_Display::measure_vline( int visLineNum ) const { - int i, width = 0, len, style, lineLen = vline_length( visLineNum ); - int charCount = 0, lineStartPos = mLineStarts[ visLineNum ]; - char expandedChar[ FL_TEXT_MAX_EXP_CHAR_LEN ]; - + // FIXME: the horizontal scroll bar is still messed up. Clicking the right container is not possible. + int lineLen = vline_length( visLineNum ); + int lineStartPos = mLineStarts[ visLineNum ]; if (lineStartPos < 0 || lineLen == 0) return 0; - if ( mStyleBuffer == NULL ) { - for ( i = 0; i < lineLen; i++ ) { - len = mBuffer->expand_character( lineStartPos + i, - charCount, expandedChar ); - - fl_font( textfont(), textsize() ); - - width += ( int ) fl_width( expandedChar, len ); - - charCount += len; - } - } else { - for ( i = 0; i < lineLen; i++ ) { - len = mBuffer->expand_character( lineStartPos + i, - charCount, expandedChar ); - // FIXME: character is ucs-4 - style = ( unsigned char ) mStyleBuffer->character( - lineStartPos + i ) - 'A'; - - if (style < 0) style = 0; - else if (style >= mNStyles) style = mNStyles - 1; - - fl_font( mStyleTable[ style ].font, mStyleTable[ style ].size ); - - width += ( int ) fl_width( expandedChar, len ); - - charCount += len; + return handle_vline(GET_WIDTH, lineStartPos, lineLen, 0, 0, 0, 0, 0, 0); } - } - return width; -} /** Return true if there are lines visible with no corresponding buffer text @@ -2471,7 +2323,7 @@ void Fl_Text_Display::find_wrap_range(const char *deletedText, int pos, nLines++; if (lineStart > pos + nInserted && // FIXME: character is ucs-4 - buf->character(lineStart-1) == '\n') { + buf->char_at(lineStart-1) == '\n') { countTo = lineStart; *modRangeEnd = lineStart; break; @@ -2617,7 +2469,7 @@ void Fl_Text_Display::measure_deleted_lines(int pos, int nDeleted) { nLines++; if (lineStart > pos + nDeleted && // FIXME: character is ucs-4 - buf->character(lineStart-1) == '\n') { + buf->char_at(lineStart-1) == '\n') { break; } @@ -2662,16 +2514,17 @@ void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, int lineStart, newLineStart = 0, b, p, colNum, wrapMargin; int maxWidth, i, foundBreak, width; bool countPixels; - int nLines = 0, tabDist = buffer()->tab_distance(); + int nLines = 0; unsigned char c; /* If the font is fixed, or there's a wrap margin set, it's more efficient to measure in columns, than to count pixels. Determine if we can count in columns (countPixels == False) or must count pixels (countPixels == True), and set the wrap target for either pixels or columns */ - if (mFixedFontWidth != -1 || mWrapMargin != 0) { + if (mWrapMargin != 0) { + // FIXME: the warp margin is actually a pixel boundary, so we have to measure nevertheless! countPixels = false; - wrapMargin = mWrapMargin ? mWrapMargin : text_area.w / (mFixedFontWidth + 1); + wrapMargin = mWrapMargin; maxWidth = INT_MAX; } else { countPixels = true; @@ -2688,14 +2541,14 @@ void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, /* ** Loop until position exceeds maxPos or line count exceeds maxLines. - ** (actually, contines beyond maxPos to end of line containing maxPos, + ** (actually, continues beyond maxPos to end of line containing maxPos, ** in case later characters cause a word wrap back before maxPos) */ colNum = 0; width = 0; for (p=lineStart; p<buf->length(); p++) { // FIXME: character is ucs-4 - c = (unsigned char)buf->character(p); + c = (unsigned char)buf->char_at(p); /* If the character was a newline, count the line and start over, otherwise, add it to the width and column counts */ @@ -2720,7 +2573,7 @@ void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, width = 0; } else { const char *s = buf->address(p); - colNum += Fl_Text_Buffer::character_width(s, colNum, tabDist); // FIXME: unicode + colNum++; if (countPixels) width += measure_proportional_character(s, colNum, p+styleBufOffset); } @@ -2731,7 +2584,7 @@ void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, foundBreak = false; for (b=p; b>=lineStart; b--) { // FIXME: character is ucs-4 - c = (unsigned char)buf->character(b); + c = (unsigned char)buf->char_at(b); if (c == '\t' || c == ' ') { newLineStart = b + 1; if (countPixels) { @@ -2753,7 +2606,7 @@ void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, if (!foundBreak) { /* no whitespace, just break at margin */ newLineStart = max(p, lineStart+1); const char *s = buf->address(b); - colNum = Fl_Text_Buffer::character_width(s, colNum, tabDist); // FIXME: unicode + colNum++; if (countPixels) width = measure_proportional_character(s, colNum, p+styleBufOffset); } @@ -2800,27 +2653,14 @@ void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, insertion/deletion, though static display and wrapping and resizing should now be solid because they are now used for online help display. */ - int Fl_Text_Display::measure_proportional_character(const char *s, int colNum, int pos) const { - int charLen, style; - char expChar[ FL_TEXT_MAX_EXP_CHAR_LEN ]; - Fl_Text_Buffer *styleBuf = mStyleBuffer; - - charLen = Fl_Text_Buffer::expand_character(s, colNum, expChar, buffer()->tab_distance()); // FIXME: unicode - if (styleBuf == 0) { - style = 0; - } else { - // FIXME: character is ucs-4 - style = (unsigned char)styleBuf->character(pos); - if (style == mUnfinishedStyle && mUnfinishedHighlightCB) { - /* encountered "unfinished" style, trigger parsing */ - (mUnfinishedHighlightCB)(pos, mHighlightCBArg); - // FIXME: character is ucs-4 - style = (unsigned char)styleBuf->character(pos); + int charLen = fl_utf8len(*s), style = 0; + if (mStyleBuffer) { + const char *b = mStyleBuffer->address(pos); + style = *b; } + return string_width(s, charLen, style); } - return string_width(expChar, charLen, style); -} /** Finds both the end of the current line and the start of the next line. Why? @@ -2873,22 +2713,12 @@ int Fl_Text_Display::wrap_uses_character(int lineEndPos) const { return 1; // FIXME: character is ucs-4 - c = buffer()->character(lineEndPos); + c = buffer()->char_at(lineEndPos); return c == '\n' || ((c == '\t' || c == ' ') && lineEndPos + 1 != buffer()->length()); } /** - Return true if the selection "sel" is rectangular, and touches a - buffer position withing "rangeStart" to "rangeEnd" -*/ -int Fl_Text_Display::range_touches_selection(const Fl_Text_Selection *sel, - int rangeStart, int rangeEnd) const { - return sel->selected() && sel->rectangular() && sel->end() >= rangeStart && - sel->start() <= rangeEnd; -} - -/** Extend the range of a redraw request (from *start to *end) with additional redraw requests resulting from changes to the attached style buffer (which contains auxiliary information for coloring or styling text). @@ -2921,7 +2751,7 @@ void Fl_Text_Display::extend_range_for_styles( int *startpos, int *endpos ) { /* If the Fl_Text_Selection was extended due to a style change, and some of the fonts don't match in spacing, extend redraw area to end of line to redraw characters exposed by possible font size changes */ - if ( mFixedFontWidth == -1 && extended ) + if ( extended ) *endpos = mBuffer->line_end( *endpos ) + 1; } @@ -3153,7 +2983,7 @@ int Fl_Text_Display::handle(int event) { int ok = 0; while (!ok) { // FIXME: character is ucs-4 - char c = buffer()->character( pos ); + char c = buffer()->char_at( pos ); if (!((c & 0x80) && !(c & 0x40))) { ok = 1; } else { @@ -3219,7 +3049,7 @@ int Fl_Text_Display::handle(int event) { int ok = 0; while (!ok) { // FIXME: character is ucs-4 - char c = buffer()->character( pos ); + char c = buffer()->char_at( pos ); if (!((c & 0x80) && !(c & 0x40))) { ok = 1; } else { diff --git a/src/Fl_Text_Editor.cxx b/src/Fl_Text_Editor.cxx index 76ce92ddd..dc4730fe2 100644 --- a/src/Fl_Text_Editor.cxx +++ b/src/Fl_Text_Editor.cxx @@ -255,7 +255,7 @@ int Fl_Text_Editor::kf_backspace(int, Fl_Text_Editor* e) { if (!e->buffer()->selected() && e->move_left()) { int l = 1; // FIXME: character is ucs-4 - char c = e->buffer()->character(e->insert_position()); + char c = e->buffer()->char_at(e->insert_position()); if (c & 0x80 && c & 0x40) { l = fl_utf8len(c); } @@ -451,7 +451,7 @@ int Fl_Text_Editor::kf_delete(int, Fl_Text_Editor* e) { if (!e->buffer()->selected()) { int l = 1; // FIXME: character is ucs-4 - char c = e->buffer()->character(e->insert_position()); + char c = e->buffer()->char_at(e->insert_position()); if (c & 0x80 && c & 0x40) { l = fl_utf8len(c); } |
