summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael R Sweet <michael.r.sweet@gmail.com>2002-09-20 19:59:45 +0000
committerMichael R Sweet <michael.r.sweet@gmail.com>2002-09-20 19:59:45 +0000
commite04a3734e7280d2557a072b02dd1d2441cbfc744 (patch)
treeef261a912e03a9c06a88bc7d47e37980677c9456
parentf9f28ad12a1ca65c59bccd78d93df4e48ef8566b (diff)
Apply patch from George Garvey to make Fl_Text_Display/Buffer based on
NEdit 5.3... git-svn-id: file:///fltk/svn/fltk/branches/branch-1.1@2631 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
-rw-r--r--CHANGES2
-rw-r--r--CREDITS1
-rw-r--r--FL/Fl_Text_Buffer.H15
-rw-r--r--FL/Fl_Text_Display.H65
-rw-r--r--src/Fl_Text_Buffer.cxx112
-rw-r--r--src/Fl_Text_Display.cxx1121
-rw-r--r--test/editor.cxx6
7 files changed, 1260 insertions, 62 deletions
diff --git a/CHANGES b/CHANGES
index 22416fd1b..1df9b9bf2 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,7 @@
CHANGES IN FLTK 1.1.0rc7
+ - Updated the Fl_Text_Buffer and Fl_Text_Display classes
+ to be based on NEdit 5.3 (patch from George Garvey).
- Fixed a problem with Fl::wait(0.0) on MacOS X 10.2;
this affected the fractals demo and other OpenGL
applications.
diff --git a/CREDITS b/CREDITS
index 2447cccf6..b63553672 100644
--- a/CREDITS
+++ b/CREDITS
@@ -34,6 +34,7 @@ OTHER CONTRIBUTORS
Fabien Costantini
Stephen Davies
Greg Ercolano
+ George Garvey
Stuart Levy
Mike Lindner
Alexander Mai
diff --git a/FL/Fl_Text_Buffer.H b/FL/Fl_Text_Buffer.H
index e2dc584f0..92f4792ff 100644
--- a/FL/Fl_Text_Buffer.H
+++ b/FL/Fl_Text_Buffer.H
@@ -1,5 +1,5 @@
//
-// "$Id: Fl_Text_Buffer.H,v 1.3.2.4 2002/08/09 03:17:29 easysw Exp $"
+// "$Id: Fl_Text_Buffer.H,v 1.3.2.5 2002/09/20 19:59:45 easysw Exp $"
//
// Header file for Fl_Text_Buffer class.
//
@@ -65,6 +65,7 @@ class FL_EXPORT Fl_Text_Selection {
typedef void (*Fl_Text_Modify_Cb)(int pos, int nInserted, int nDeleted,
int nRestyled, const char* deletedText,
void* cbArg);
+typedef void (*Fl_Text_Predelete_Cb)(int pos, int nDeleted, void* cbArg);
class FL_EXPORT Fl_Text_Buffer {
public:
@@ -142,6 +143,11 @@ class FL_EXPORT Fl_Text_Buffer {
void call_modify_callbacks() { call_modify_callbacks(0, 0, 0, 0, 0); }
+ void add_predelete_callback(Fl_Text_Predelete_Cb bufPredelCB, void* cbArg);
+ void remove_predelete_callback(Fl_Text_Predelete_Cb predelCB, void* cbArg);
+
+ void call_predelete_callbacks() { call_predelete_callbacks(0, 0); }
+
char* line_text(int pos);
int line_start(int pos);
int line_end(int pos);
@@ -179,6 +185,7 @@ class FL_EXPORT Fl_Text_Buffer {
protected:
void call_modify_callbacks(int pos, int nDeleted, int nInserted,
int nRestyled, const char* deletedText);
+ void call_predelete_callbacks(int pos, int nDeleted);
int insert_(int pos, const char* text);
void remove_(int start, int end);
@@ -226,6 +233,10 @@ class FL_EXPORT Fl_Text_Buffer {
Fl_Text_Modify_Cb* /* procedures to call when buffer is */
mNodifyProcs; /* modified to redisplay contents */
void** mCbArgs; /* caller arguments for modifyProcs above */
+ int mNPredeleteProcs; /* number of pre-delete procs attached */
+ Fl_Text_Predelete_Cb* /* procedure to call before text is deleted */
+ mPredeleteProcs; /* from the buffer; at most one is supported. */
+ void **mPredeleteCbArgs; /* caller argument for pre-delete proc above */
int mCursorPosHint; /* hint for reasonable cursor position after
a buffer modification operation */
char mNullSubsChar; /* NEdit is based on C null-terminated strings,
@@ -238,5 +249,5 @@ class FL_EXPORT Fl_Text_Buffer {
#endif
//
-// End of "$Id: Fl_Text_Buffer.H,v 1.3.2.4 2002/08/09 03:17:29 easysw Exp $".
+// End of "$Id: Fl_Text_Buffer.H,v 1.3.2.5 2002/09/20 19:59:45 easysw Exp $".
//
diff --git a/FL/Fl_Text_Display.H b/FL/Fl_Text_Display.H
index 45a068c20..c46bbb14a 100644
--- a/FL/Fl_Text_Display.H
+++ b/FL/Fl_Text_Display.H
@@ -1,5 +1,5 @@
//
-// "$Id: Fl_Text_Display.H,v 1.4.2.11 2002/08/14 16:49:38 easysw Exp $"
+// "$Id: Fl_Text_Display.H,v 1.4.2.12 2002/09/20 19:59:45 easysw Exp $"
//
// Header file for Fl_Text_Display class.
//
@@ -53,7 +53,7 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
};
friend void fl_text_drag_me(int pos, Fl_Text_Display* d);
- typedef void (*Unfinished_Style_Cb)();
+ typedef void (*Unfinished_Style_Cb)(int, void *);
// style attributes - currently not implemented!
enum {
@@ -88,6 +88,11 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
int move_left();
int move_up();
int move_down();
+ int count_lines(int start, int end, bool start_pos_is_line_start);
+ int line_start(int pos);
+ int line_end(int pos, bool start_pos_is_line_start);
+ int skip_lines(int startPos, int nLines, bool startPosIsLineStart);
+ int rewind_lines(int startPos, int nLines);
void next_word(void);
void previous_word(void);
void show_cursor(int b = 1);
@@ -117,6 +122,10 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
Fl_Color textcolor() const {return (Fl_Color)textcolor_;}
void textcolor(unsigned n) {textcolor_ = n;}
+ int wrapped_column(int row, int column);
+ int wrapped_row(int row);
+ void wrap_mode(int wrap, int wrap_margin);
+
virtual void resize(int X, int Y, int W, int H);
protected:
@@ -136,6 +145,8 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
void draw_vline(int visLineNum, int leftClip, int rightClip,
int leftCharIndex, int rightCharIndex);
+ void draw_line_numbers(bool clearAll);
+
void clear_rect(int style, int x, int y, int width, int height);
void display_insert();
@@ -151,6 +162,7 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
int position_to_line( int pos, int* lineNum );
int string_width(const char* string, int length, int style);
+ static void buffer_predelete_cb(int pos, int nDeleted, void* cbArg);
static void buffer_modified_cb(int pos, int nInserted, int nDeleted,
int nRestyled, const char* deletedText,
void* cbArg);
@@ -169,11 +181,31 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
int PosType = CHARACTER_POS);
int position_to_xy(int pos, int* x, int* y);
+ void maintain_absolute_top_line_number(int state);
+ int get_absolute_top_line_number();
+ void absolute_top_line_number(int oldFirstChar);
+ int maintaining_absolute_top_line_number();
+ void reset_absolute_top_line_number();
int position_to_linecol(int pos, int* lineNum, int* column);
void scroll_(int topLineNum, int horizOffset);
void extend_range_for_styles(int* start, int* end);
+ void find_wrap_range(const char *deletedText, int pos, int nInserted,
+ int nDeleted, int *modRangeStart, int *modRangeEnd,
+ int *linesInserted, int *linesDeleted);
+ void measure_deleted_lines(int pos, int nDeleted);
+ void wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, int maxPos,
+ int maxLines, bool startPosIsLineStart,
+ int styleBufOffset, int *retPos, int *retLines,
+ int *retLineStart, int *retLineEnd,
+ bool countLastLineMissingNewLine = true);
+ void find_line_end(int pos, bool start_pos_is_line_start, int *lineEnd,
+ int *nextLineStart);
+ int measure_proportional_character(char c, int colNum, int pos);
+ int wrap_uses_character(int lineEndPos);
+ int range_touches_selection(Fl_Text_Selection *sel, int rangeStart,
+ int rangeEnd);
int damage_range1_start, damage_range1_end;
int damage_range2_start, damage_range2_end;
@@ -194,9 +226,19 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
displayed character (lastChar points
either to a newline or one character
beyond the end of the buffer) */
+ int mContinuousWrap; /* Wrap long lines when displaying */
+ int mWrapMargin; /* Margin in # of char positions for
+ wrapping in continuousWrap mode */
int* mLineStarts;
int mTopLineNum; /* Line number of top displayed line
of file (first line of file is 1) */
+ int mAbsTopLineNum; /* In continuous wrap mode, the line
+ number of the top line if the text
+ were not wrapped (note that this is
+ only maintained as needed). */
+ int mNeedAbsTopLineNum; /* Externally settable flag to continue
+ maintaining absTopLineNum even if
+ it isn't needed for line # display */
int mHorizOffset; /* Horizontal scroll pos. in pixels */
int mTopLineNumHint; /* Line number of top displayed line
of file (first line of file is 1) */
@@ -214,6 +256,13 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
int mFixedFontWidth; /* Font width if all current fonts are
fixed and match in width, else -1 */
+ int mSuppressResync; /* Suppress resynchronization of line
+ starts during buffer updates */
+ int mNLinesDeleted; /* Number of lines deleted during
+ buffer modification (only used
+ when resynchronization is suppressed) */
+ int mModifyingTabDistance; /* Whether tab distance is being
+ modified */
Fl_Color mCursor_color;
@@ -229,19 +278,15 @@ class FL_EXPORT Fl_Text_Display: public Fl_Group {
uchar textsize_;
unsigned textcolor_;
- // The following are not presently used from the original NEdit
- // code, but are being put here so that future versions of
- // Fl_Text_Display can implement wrapping and line numbers
- // without breaking binary compatibility.
+ // The following are not presently used from the original NEdit code,
+ // but are being put here so that future versions of Fl_Text_Display
+ // can implement line numbers without breaking binary compatibility.
int mLineNumLeft, mLineNumWidth;
/* Line number margin and width */
- int mContinuousWrap; /* Wrap long lines when displaying */
- int mWrapMargin; /* Margin in # of char positions for
- wrapping in continuousWrap mode */
};
#endif
//
-// End of "$Id: Fl_Text_Display.H,v 1.4.2.11 2002/08/14 16:49:38 easysw Exp $".
+// End of "$Id: Fl_Text_Display.H,v 1.4.2.12 2002/09/20 19:59:45 easysw Exp $".
//
diff --git a/src/Fl_Text_Buffer.cxx b/src/Fl_Text_Buffer.cxx
index 9dadec060..50cf59723 100644
--- a/src/Fl_Text_Buffer.cxx
+++ b/src/Fl_Text_Buffer.cxx
@@ -1,5 +1,5 @@
//
-// "$Id: Fl_Text_Buffer.cxx,v 1.9.2.11 2002/08/09 03:17:30 easysw Exp $"
+// "$Id: Fl_Text_Buffer.cxx,v 1.9.2.12 2002/09/20 19:59:45 easysw Exp $"
//
// Copyright 2001-2002 by Bill Spitzak and others.
// Original code Copyright Mark Edel. Permission to distribute under
@@ -93,6 +93,9 @@ Fl_Text_Buffer::Fl_Text_Buffer( int requestedSize ) {
mNodifyProcs = NULL;
mCbArgs = NULL;
mNModifyProcs = 0;
+ mNPredeleteProcs = 0;
+ mPredeleteProcs = NULL;
+ mPredeleteCbArgs = NULL;
mCursorPosHint = 0;
mNullSubsChar = '\0';
#ifdef PURIFY
@@ -109,6 +112,10 @@ Fl_Text_Buffer::~Fl_Text_Buffer() {
free( ( void * ) mNodifyProcs );
free( ( void * ) mCbArgs );
}
+ if ( mNPredeleteProcs != 0 ) {
+ free( ( void * ) mPredeleteProcs );
+ free( ( void * ) mPredeleteCbArgs );
+ }
}
/*
@@ -133,6 +140,8 @@ void Fl_Text_Buffer::text( const char *t ) {
int insertedLength, deletedLength;
const char *deletedText;
+ call_predelete_callbacks(0, length());
+
/* Save information for redisplay, and get rid of the old buffer */
deletedText = text();
deletedLength = mLength;
@@ -220,6 +229,9 @@ void Fl_Text_Buffer::insert( int pos, const char *s ) {
if ( pos > mLength ) pos = mLength;
if ( pos < 0 ) pos = 0;
+ /* Even if nothing is deleted, we must call these callbacks */
+ call_predelete_callbacks( pos, 0 );
+
/* insert and redisplay */
nInserted = insert_( pos, s );
mCursorPosHint = pos + nInserted;
@@ -234,6 +246,7 @@ void Fl_Text_Buffer::replace( int start, int end, const char *s ) {
const char * deletedText;
int nInserted;
+ call_predelete_callbacks( start, end-start );
deletedText = text_range( start, end );
remove_( start, end );
nInserted = insert_( start, s );
@@ -256,6 +269,7 @@ void Fl_Text_Buffer::remove( int start, int end ) {
if ( end > mLength ) end = mLength;
if ( end < 0 ) end = 0;
+ call_predelete_callbacks( start, end-start );
/* Remove and redisplay */
deletedText = text_range( start, end );
remove_( start, end );
@@ -314,6 +328,7 @@ void Fl_Text_Buffer::insert_column( int column, int startPos, const char *s,
lineStartPos = line_start( startPos );
nDeleted = line_end( skip_lines( startPos, nLines ) ) -
lineStartPos;
+ call_predelete_callbacks( lineStartPos, nDeleted );
deletedText = text_range( lineStartPos, lineStartPos + nDeleted );
insert_column_( column, lineStartPos, s, &insertDeleted, &nInserted,
&mCursorPosHint );
@@ -342,6 +357,7 @@ void Fl_Text_Buffer::overlay_rectangular( int startPos, int rectStart,
lineStartPos = line_start( startPos );
nDeleted = line_end( skip_lines( startPos, nLines ) ) -
lineStartPos;
+ call_predelete_callbacks( lineStartPos, nDeleted );
deletedText = text_range( lineStartPos, lineStartPos + nDeleted );
overlay_rectangular_( lineStartPos, rectStart, rectEnd, s, &insertDeleted,
&nInserted, &mCursorPosHint );
@@ -374,6 +390,8 @@ void Fl_Text_Buffer::replace_rectangular( int start, int end, int rectStart,
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
@@ -424,6 +442,7 @@ void Fl_Text_Buffer::remove_rectangular( int start, int end, int rectStart,
start = line_start( start );
end = line_end( end );
+ call_predelete_callbacks( start, end-start );
deletedText = text_range( start, end );
remove_rectangular_( start, end, rectStart, rectEnd, &nInserted,
&mCursorPosHint );
@@ -492,6 +511,10 @@ char * Fl_Text_Buffer::text_in_rectangle( int start, int end,
void Fl_Text_Buffer::tab_distance( int tabDist ) {
const char * deletedText;
+ /* First call the pre-delete callbacks with the previous tab setting
+ still active. */
+ call_predelete_callbacks( 0, mLength );
+
/* Change the tab setting */
mTabDist = tabDist;
@@ -693,6 +716,80 @@ void Fl_Text_Buffer::remove_modify_callback( Fl_Text_Modify_Cb bufModifiedCB,
}
/*
+** Add a callback routine to be called before text is deleted from the buffer.
+*/
+void Fl_Text_Buffer::add_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB,
+ void *cbArg) {
+ Fl_Text_Predelete_Cb *newPreDeleteProcs;
+ void **newCBArgs;
+ int i;
+
+ newPreDeleteProcs = new Fl_Text_Predelete_Cb[ mNPredeleteProcs + 1 ];
+ newCBArgs = new void * [ mNPredeleteProcs + 1 ];
+ for ( i = 0; i < mNPredeleteProcs; i++ ) {
+ newPreDeleteProcs[i + 1] = mPredeleteProcs[i];
+ newCBArgs[i + 1] = mPredeleteCbArgs[i];
+ }
+ if (! mNPredeleteProcs != 0) {
+ delete [] mPredeleteProcs;
+ delete [] mPredeleteCbArgs;
+ }
+ newPreDeleteProcs[0] = bufPreDeleteCB;
+ newCBArgs[0] = cbArg;
+ mNPredeleteProcs++;
+ mPredeleteProcs = newPreDeleteProcs;
+ mPredeleteCbArgs = newCBArgs;
+}
+
+void Fl_Text_Buffer::remove_predelete_callback(
+ Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg) {
+ int i, toRemove = -1;
+ Fl_Text_Predelete_Cb *newPreDeleteProcs;
+ void **newCBArgs;
+
+ /* find the matching callback to remove */
+ for ( i = 0; i < mNPredeleteProcs; i++) {
+ if (mPredeleteProcs[i] == bufPreDeleteCB &&
+ mPredeleteCbArgs[i] == cbArg) {
+ toRemove = i;
+ break;
+ }
+ }
+ if (toRemove == -1) {
+ fprintf(stderr, "Internal Error: Can't find pre-delete CB to remove\n");
+ return;
+ }
+
+ /* Allocate new lists for remaining callback procs and args (if
+ any are left) */
+ mNPredeleteProcs--;
+ if (mNPredeleteProcs == 0) {
+ mNPredeleteProcs = 0;
+ delete[] mPredeleteProcs;
+ mPredeleteProcs = NULL;
+ delete[] mPredeleteCbArgs;
+ mPredeleteCbArgs = NULL;
+ return;
+ }
+ newPreDeleteProcs = new Fl_Text_Predelete_Cb [ mNPredeleteProcs ];
+ newCBArgs = new void * [ mNPredeleteProcs ];
+
+ /* copy out the remaining members and free the old lists */
+ for ( i = 0; i < toRemove; i++) {
+ newPreDeleteProcs[i] = mPredeleteProcs[i];
+ newCBArgs[i] = mPredeleteCbArgs[i];
+ }
+ for ( ; i < mNPredeleteProcs; i++) {
+ newPreDeleteProcs[i] = mPredeleteProcs[i+1];
+ newCBArgs[i] = mPredeleteCbArgs[i+1];
+ }
+ delete[] mPredeleteProcs;
+ delete[] mPredeleteCbArgs;
+ mPredeleteProcs = newPreDeleteProcs;
+ mPredeleteCbArgs = newCBArgs;
+}
+
+/*
** Return the text from the entire line containing position "pos"
*/
char * Fl_Text_Buffer::line_text( int pos ) {
@@ -1811,6 +1908,17 @@ void Fl_Text_Buffer::call_modify_callbacks( int pos, int nDeleted,
}
/*
+** Call the stored pre-delete callback procedure(s) for this buffer to update
+** the changed area(s) on the screen and any other listeners.
+*/
+void Fl_Text_Buffer::call_predelete_callbacks(int pos, int nDeleted) {
+ int i;
+
+ for (i=0; i<mNPredeleteProcs; i++)
+ (*mPredeleteProcs[i])(pos, nDeleted, mPredeleteCbArgs[i]);
+}
+
+/*
** Call the stored redisplay procedure(s) for this buffer to update the
** screen for a change in a selection.
*/
@@ -2289,5 +2397,5 @@ Fl_Text_Buffer::outputfile(const char *file, int start, int end, int buflen) {
//
-// End of "$Id: Fl_Text_Buffer.cxx,v 1.9.2.11 2002/08/09 03:17:30 easysw Exp $".
+// End of "$Id: Fl_Text_Buffer.cxx,v 1.9.2.12 2002/09/20 19:59:45 easysw Exp $".
//
diff --git a/src/Fl_Text_Display.cxx b/src/Fl_Text_Display.cxx
index c72b44b8f..1a96582a8 100644
--- a/src/Fl_Text_Display.cxx
+++ b/src/Fl_Text_Display.cxx
@@ -1,5 +1,5 @@
//
-// "$Id: Fl_Text_Display.cxx,v 1.12.2.27 2002/08/20 17:12:19 easysw Exp $"
+// "$Id: Fl_Text_Display.cxx,v 1.12.2.28 2002/09/20 19:59:45 easysw Exp $"
//
// Copyright 2001-2002 by Bill Spitzak and others.
// Original code Copyright Mark Edel. Permission to distribute under
@@ -66,6 +66,8 @@ static int countlines( const char *string );
Fl_Text_Display::Fl_Text_Display(int X, int Y, int W, int H, const char* l)
: Fl_Group(X, Y, W, H, l) {
+ int i;
+
mMaxsize = 0;
damage_range1_start = damage_range1_end = -1;
damage_range2_start = damage_range2_end = -1;
@@ -105,6 +107,8 @@ Fl_Text_Display::Fl_Text_Display(int X, int Y, int W, int H, const char* l)
mLastChar = 0;
mNBufferLines = 0;
mTopLineNum = mTopLineNumHint = 1;
+ mAbsTopLineNum = 1;
+ mNeedAbsTopLineNum = 0;
mHorizOffset = mHorizOffsetHint = 0;
mCursor_color = FL_BLACK;
@@ -116,10 +120,20 @@ Fl_Text_Display::Fl_Text_Display(int X, int Y, int W, int H, const char* l)
mNVisibleLines = 1;
mLineStarts = new int[mNVisibleLines];
mLineStarts[0] = 0;
+ for (i=1; i<mNVisibleLines; i++)
+ mLineStarts[i] = -1;
+ mSuppressResync = 0;
+ mNLinesDeleted = 0;
+ mModifyingTabDistance = 0;
mUnfinishedStyle = 0;
mUnfinishedHighlightCB = 0;
mHighlightCBArg = 0;
+
+ mLineNumLeft = mLineNumWidth = 0;
+ mContinuousWrap = 0;
+ mWrapMargin = 0;
+ mSuppressResync = mNLinesDeleted = mModifyingTabDistance = 0;
}
/*
@@ -141,12 +155,14 @@ void Fl_Text_Display::buffer( Fl_Text_Buffer *buf ) {
if ( mBuffer != 0 ) {
buffer_modified_cb( 0, 0, mBuffer->length(), 0, 0, this );
mBuffer->remove_modify_callback( buffer_modified_cb, this );
+ mBuffer->remove_predelete_callback( buffer_predelete_cb, this );
}
/* Add the buffer to the display, and attach a callback to the buffer for
receiving modification information when the buffer contents change */
mBuffer = buf;
mBuffer->add_modify_callback( buffer_modified_cb, this );
+ mBuffer->add_predelete_callback( buffer_predelete_cb, this );
/* Update the display */
buffer_modified_cb( 0, buf->length(), 0, 0, 0, this );
@@ -181,9 +197,133 @@ Fl_Text_Display::highlight_data(Fl_Text_Buffer *styleBuffer,
mUnfinishedHighlightCB = unfinishedHighlightCB;
mHighlightCBArg = cbArg;
+#if 0
+ // FIXME: this is in nedit code -- is it needed?
+ /* Call TextDSetFont to combine font information from style table and
+ primary font, adjust font-related parameters, and then redisplay */
+ TextDSetFont(textD, textD->fontStruct);
+#endif
damage(FL_DAMAGE_EXPOSE);
}
+#if 0
+ // FIXME: this is in nedit code -- is it needed?
+/*
+** Change the (non highlight) font
+*/
+void TextDSetFont(textDisp *textD, XFontStruct *fontStruct) {
+ Display *display = XtDisplay(textD->w);
+ int i, maxAscent = fontStruct->ascent, maxDescent = fontStruct->descent;
+ int width, height, fontWidth;
+ Pixel bgPixel, fgPixel, selectFGPixel, selectBGPixel;
+ Pixel highlightFGPixel, highlightBGPixel;
+ XGCValues values;
+ XFontStruct *styleFont;
+
+ /* If font size changes, cursor will be redrawn in a new position */
+ blankCursorProtrusions(textD);
+
+ /* If there is a (syntax highlighting) style table in use, find the new
+ maximum font height for this text display */
+ for (i=0; i<textD->nStyles; i++) {
+ styleFont = textD->styleTable[i].font;
+ if (styleFont != NULL && styleFont->ascent > maxAscent)
+ maxAscent = styleFont->ascent;
+ if (styleFont != NULL && styleFont->descent > maxDescent)
+ maxDescent = styleFont->descent;
+ }
+ textD->ascent = maxAscent;
+ textD->descent = maxDescent;
+
+ /* If all of the current fonts are fixed and match in width, compute */
+ fontWidth = fontStruct->max_bounds.width;
+ if (fontWidth != fontStruct->min_bounds.width)
+ fontWidth = -1;
+ else {
+ for (i=0; i<textD->nStyles; i++) {
+ styleFont = textD->styleTable[i].font;
+ if (styleFont != NULL && (styleFont->max_bounds.width != fontWidth ||
+ styleFont->max_bounds.width != styleFont->min_bounds.width))
+ fontWidth = -1;
+ }
+ }
+ textD->fixedFontWidth = fontWidth;
+
+ /* Don't let the height dip below one line, or bad things can happen */
+ if (textD->height < maxAscent + maxDescent)
+ textD->height = maxAscent + maxDescent;
+
+ /* Change the font. In most cases, this means re-allocating the
+ affected GCs (they are shared with other widgets, and if the primary
+ font changes, must be re-allocated to change it). Unfortunately,
+ this requres recovering all of the colors from the existing GCs */
+ textD->fontStruct = fontStruct;
+ XGetGCValues(display, textD->gc, GCForeground|GCBackground, &values);
+ fgPixel = values.foreground;
+ bgPixel = values.background;
+ XGetGCValues(display, textD->selectGC, GCForeground|GCBackground, &values);
+ selectFGPixel = values.foreground;
+ selectBGPixel = values.background;
+ XGetGCValues(display, textD->highlightGC,GCForeground|GCBackground,&values);
+ highlightFGPixel = values.foreground;
+ highlightBGPixel = values.background;
+ releaseGC(textD->w, textD->gc);
+ releaseGC(textD->w, textD->selectGC);
+ releaseGC(textD->w, textD->highlightGC);
+ releaseGC(textD->w, textD->selectBGGC);
+ releaseGC(textD->w, textD->highlightBGGC);
+ if (textD->lineNumGC != NULL)
+ releaseGC(textD->w, textD->lineNumGC);
+ textD->lineNumGC = NULL;
+ allocateFixedFontGCs(textD, fontStruct, bgPixel, fgPixel, selectFGPixel,
+ selectBGPixel, highlightFGPixel, highlightBGPixel);
+ XSetFont(display, textD->styleGC, fontStruct->fid);
+
+ /* Do a full resize to force recalculation of font related parameters */
+ width = textD->width;
+ height = textD->height;
+ textD->width = textD->height = 0;
+ TextDResize(textD, width, height);
+
+ /* Redisplay */
+ TextDRedisplayRect(textD, textD->left, textD->top, textD->width,
+ textD->height);
+
+ /* Clean up line number area in case spacing has changed */
+ draw_line_numbers(textD, True);
+}
+
+int TextDMinFontWidth(textDisp *textD, Boolean considerStyles) {
+ int fontWidth = textD->fontStruct->max_bounds.width;
+ int i;
+
+ if (considerStyles) {
+ for (i = 0; i < textD->nStyles; ++i) {
+ int thisWidth = (textD->styleTable[i].font)->min_bounds.width;
+ if (thisWidth < fontWidth) {
+ fontWidth = thisWidth;
+ }
+ }
+ }
+ return(fontWidth);
+}
+
+int TextDMaxFontWidth(textDisp *textD, Boolean considerStyles) {
+ int fontWidth = textD->fontStruct->max_bounds.width;
+ int i;
+
+ if (considerStyles) {
+ for (i = 0; i < textD->nStyles; ++i) {
+ int thisWidth = (textD->styleTable[i].font)->max_bounds.width;
+ if (thisWidth > fontWidth) {
+ fontWidth = thisWidth;
+ }
+ }
+ }
+ return(fontWidth);
+}
+#endif
+
int Fl_Text_Display::longest_vline() {
int longest = 0;
for (int i = 0; i < mNVisibleLines; i++)
@@ -195,6 +335,7 @@ int Fl_Text_Display::longest_vline() {
** Change the size of the displayed text area
*/
void Fl_Text_Display::resize(int X, int Y, int W, int H) {
+ const int oldWidth = w();
Fl_Widget::resize(X,Y,W,H);
if (!buffer()) return;
X += Fl::box_dx(box());
@@ -222,6 +363,17 @@ void Fl_Text_Display::resize(int X, int Y, int W, int H) {
for (int again = 1; again;) {
again = 0;
+ /* In continuous wrap mode, a change in width affects the total number of
+ lines in the buffer, and can leave the top line number incorrect, and
+ the top character no longer pointing at a valid line start */
+ if (mContinuousWrap && !mWrapMargin && W!=oldWidth) {
+ int oldFirstChar = mFirstChar;
+ mNBufferLines = count_lines(0, buffer()->length(), true);
+ mFirstChar = line_start(mFirstChar);
+ mTopLineNum = count_lines(0, mFirstChar, true)+1;
+ absolute_top_line_number(oldFirstChar);
+ }
+
/* reallocate and update the line starts array, which may have changed
size and / or contents. */
int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize;
@@ -321,7 +473,8 @@ void Fl_Text_Display::resize(int X, int Y, int W, int H) {
mHorizOffsetHint = mHorizOffset;
display_insert_position_hint = 0;
- if (hscrollbarvisible != mHScrollBar->visible() ||
+ if (mContinuousWrap ||
+ hscrollbarvisible != mHScrollBar->visible() ||
vscrollbarvisible != mVScrollBar->visible())
redraw();
@@ -347,6 +500,10 @@ void Fl_Text_Display::draw_text( int left, int top, int width, int height ) {
for ( line = firstLine; line <= lastLine; line++ )
draw_vline( line, left, left + width, 0, INT_MAX );
+ /* draw the line numbers if exposed area includes them */
+ if (mLineNumWidth != 0 && left <= mLineNumLeft + mLineNumWidth)
+ draw_line_numbers(false);
+
fl_pop_clip();
}
@@ -461,6 +618,42 @@ void Fl_Text_Display::cursor_style(int style) {
if (mCursorOn) show_cursor();
}
+void Fl_Text_Display::wrap_mode(int wrap, int wrapMargin) {
+ mWrapMargin = wrapMargin;
+ mContinuousWrap = wrap;
+
+ /* wrapping can change change the total number of lines, re-count */
+ mNBufferLines = count_lines(0, buffer()->length(), true);
+
+ /* changing wrap margins wrap or changing from wrapped mode to non-wrapped
+ can leave the character at the top no longer at a line start, and/or
+ change the line number */
+ mFirstChar = line_start(mFirstChar);
+ mTopLineNum = count_lines(0, mFirstChar, true) + 1;
+ reset_absolute_top_line_number();
+
+ /* update the line starts array */
+ calc_line_starts(0, mNVisibleLines);
+ calc_last_char();
+
+#if 0
+ // FIXME!
+ /* Update the scroll bar page increment size (as well as other scroll
+ bar parameters) */
+ updateVScrollBarRange(textD);
+ updateHScrollBarRange(textD);
+
+ /* Decide if the horizontal scroll bar needs to be visible */
+ hideOrShowHScrollBar(textD);
+
+ /* Do a full redraw */
+ TextDRedisplayRect(textD, 0, textD->top, textD->width + textD->left,
+ textD->height);
+#else
+ resize(x(), y(), w(), h());
+#endif
+}
+
/*
** Insert "text" at the current cursor location. This has the same
** effect as inserting the text into the buffer using BufInsert and
@@ -583,11 +776,26 @@ int Fl_Text_Display::position_to_xy( int pos, int* X, int* Y ) {
/*
** Find the line number of position "pos". Note: this only works for
** displayed lines. If the line is not displayed, the function returns
-** 0 (without the lineStarts array it could turn in to very long
+** 0 (without the mLineStarts array it could turn in to very long
** calculation involving scanning large amounts of text in the buffer).
+** If continuous wrap mode is on, returns the absolute line number (as opposed
+** to the wrapped line number which is used for scrolling).
*/
int Fl_Text_Display::position_to_linecol( int pos, int* lineNum, int* column ) {
int retVal;
+
+ /* In continuous wrap mode, the absolute (non-wrapped) line count is
+ maintained separately, as needed. Only return it if we're actually
+ keeping track of it and pos is in the displayed text */
+ if (mContinuousWrap) {
+ if (!maintaining_absolute_top_line_number() ||
+ pos < mFirstChar || pos > mLastChar)
+ return 0;
+ *lineNum = mAbsTopLineNum + buffer()->count_lines(mFirstChar, pos);
+ *column
+ = buffer()->count_displayed_characters(buffer()->line_start(pos), pos);
+ return 1;
+ }
retVal = position_to_line( pos, lineNum );
if ( retVal ) {
@@ -606,10 +814,48 @@ int Fl_Text_Display::in_selection( int X, int Y ) {
Fl_Text_Buffer *buf = mBuffer;
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
+** TextDXYToUnconstrainedPosition) to be relative to the last actual newline
+** in the buffer before the row and column position given, rather than the
+** last line start created by line wrapping. This is an adapter
+** for rectangular selections and code written before continuous wrap mode,
+** which thinks that the unconstrained column is the number of characters
+** from the last newline. Obviously this is time consuming, because it
+** invloves character re-counting.
+*/
+int Fl_Text_Display::wrapped_column(int row, int column) {
+ int lineStart, dispLineStart;
+
+ if (!mContinuousWrap || row < 0 || row > mNVisibleLines)
+ return column;
+ dispLineStart = mLineStarts[row];
+ if (dispLineStart == -1)
+ return column;
+ lineStart = buffer()->line_start(dispLineStart);
+ return column
+ + buffer()->count_displayed_characters(lineStart, dispLineStart);
+}
+
+/*
+** Correct a row number from an unconstrained position (as returned by
+** TextDXYToUnconstrainedPosition) to a straight number of newlines from the
+** top line of the display. Because rectangular selections are based on
+** newlines, rather than display wrapping, and anywhere a rectangular selection
+** needs a row, it needs it in terms of un-wrapped lines.
+*/
+int Fl_Text_Display::wrapped_row(int row) {
+ if (!mContinuousWrap || row < 0 || row > mNVisibleLines)
+ return row;
+ return buffer()->count_lines(mFirstChar, mLineStarts[row]);
+}
+
+/*
** Scroll the display to bring insertion cursor into view.
**
** Note: it would be nice to be able to do this without counting lines twice
@@ -622,13 +868,18 @@ void Fl_Text_Display::display_insert() {
hOffset = mHorizOffset;
topLine = mTopLineNum;
+// FIXME: I don't understand this well enough to know if it is correct
+// it is different than nedit 5.3
if (insert_position() < mFirstChar) {
- topLine -= buffer()->count_lines(insert_position(), mFirstChar);
+ topLine -= count_lines(insert_position(), mFirstChar, false);
} else if (mLineStarts[mNVisibleLines-2] != -1) {
int lastChar = buffer()->line_end(mLineStarts[mNVisibleLines-2]);
- if (insert_position() > lastChar)
- topLine += buffer()->count_lines(lastChar, insert_position());
+ if (insert_position() >= lastChar)
+ topLine
+ += count_lines(lastChar - (wrap_uses_character(mLastChar) ? 0 : 1),
+ insert_position(), false);
}
+
/* Find the new setting for horizontal offset (this is a bit ungraceful).
If the line is visible, just use PositionToXY to get the position
to scroll to, otherwise, do the vertical scrolling first, then the
@@ -694,6 +945,8 @@ int Fl_Text_Display::move_up() {
else
prevLineStartPos = buffer()->rewind_lines( lineStartPos, 1 );
newPos = mBuffer->skip_displayed_characters( prevLineStartPos, column );
+ if (mContinuousWrap)
+ newPos = min(newPos, line_end(prevLineStartPos, true));
/* move the cursor */
insert_position( newPos );
@@ -702,6 +955,7 @@ int Fl_Text_Display::move_up() {
mCursorPreferredCol = column;
return 1;
}
+
int Fl_Text_Display::move_down() {
int lineStartPos, column, nextLineStartPos, newPos, visLineNum;
@@ -715,14 +969,136 @@ int Fl_Text_Display::move_down() {
}
column = mCursorPreferredCol >= 0 ? mCursorPreferredCol :
mBuffer->count_displayed_characters( lineStartPos, mCursorPos );
- nextLineStartPos = buffer()->skip_lines( lineStartPos, 1 );
+ nextLineStartPos = skip_lines( lineStartPos, 1, true );
newPos = mBuffer->skip_displayed_characters( nextLineStartPos, column );
+ if (mContinuousWrap)
+ newPos = min(newPos, line_end(nextLineStartPos, true));
insert_position( newPos );
mCursorPreferredCol = column;
return 1;
}
+/*
+** Same as BufCountLines, but takes in to account wrapping if wrapping is
+** turned on. If the caller knows that startPos is at a line start, it
+** can pass "startPosIsLineStart" as True to make the call more efficient
+** by avoiding the additional step of scanning back to the last newline.
+*/
+int Fl_Text_Display::count_lines(int startPos, int endPos,
+ bool startPosIsLineStart) {
+ int retLines, retPos, retLineStart, retLineEnd;
+
+ /* If we're not wrapping use simple (and more efficient) BufCountLines */
+ if (!mContinuousWrap)
+ return buffer()->count_lines(startPos, endPos);
+
+ wrapped_line_counter(buffer(), startPos, endPos, INT_MAX,
+ startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
+ &retLineEnd);
+ return retLines;
+}
+
+/*
+** Same as BufCountForwardNLines, but takes in to account line breaks when
+** wrapping is turned on. If the caller knows that startPos is at a line start,
+** it can pass "startPosIsLineStart" as True to make the call more efficient
+** by avoiding the additional step of scanning back to the last newline.
+*/
+int Fl_Text_Display::skip_lines(int startPos, int nLines,
+ bool startPosIsLineStart) {
+ int retLines, retPos, retLineStart, retLineEnd;
+
+ /* if we're not wrapping use more efficient BufCountForwardNLines */
+ if (!mContinuousWrap)
+ return buffer()->skip_lines(startPos, nLines);
+
+ /* wrappedLineCounter can't handle the 0 lines case */
+ if (nLines == 0)
+ return startPos;
+
+ /* use the common line counting routine to count forward */
+ wrapped_line_counter(buffer(), startPos, buffer()->length(),
+ nLines, startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
+ &retLineEnd);
+ return retPos;
+}
+
+/*
+** Same as BufEndOfLine, but takes in to account line breaks when wrapping
+** is turned on. If the caller knows that startPos is at a line start, it
+** can pass "startPosIsLineStart" as True to make the call more efficient
+** by avoiding the additional step of scanning back to the last newline.
+**
+** Note that the definition of the end of a line is less clear when continuous
+** wrap is on. With continuous wrap off, it's just a pointer to the newline
+** that ends the line. When it's on, it's the character beyond the last
+** DISPLAYABLE character on the line, where a whitespace character which has
+** been "converted" to a newline for wrapping is not considered displayable.
+** Also note that, a line can be wrapped at a non-whitespace character if the
+** line had no whitespace. In this case, this routine returns a pointer to
+** the start of the next line. This is also consistent with the model used by
+** visLineLength.
+*/
+int Fl_Text_Display::line_end(int pos, bool startPosIsLineStart) {
+ int retLines, retPos, retLineStart, retLineEnd;
+
+ /* If we're not wrapping use more efficien BufEndOfLine */
+ if (!mContinuousWrap)
+ return buffer()->line_end(pos);
+
+ if (pos == buffer()->length())
+ return pos;
+ wrapped_line_counter(buffer(), pos, buffer()->length(), 1,
+ startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
+ &retLineEnd);
+ return retLineEnd;
+}
+
+/*
+** Same as BufStartOfLine, but returns the character after last wrap point
+** rather than the last newline.
+*/
+int Fl_Text_Display::line_start(int pos) {
+ int retLines, retPos, retLineStart, retLineEnd;
+
+ /* If we're not wrapping, use the more efficient BufStartOfLine */
+ if (!mContinuousWrap)
+ return buffer()->line_start(pos);
+
+ wrapped_line_counter(buffer(), buffer()->line_start(pos), pos, INT_MAX, true, 0,
+ &retPos, &retLines, &retLineStart, &retLineEnd);
+ return retLineStart;
+}
+
+/*
+** Same as BufCountBackwardNLines, but takes in to account line breaks when
+** wrapping is turned on.
+*/
+int Fl_Text_Display::rewind_lines(int startPos, int nLines) {
+ Fl_Text_Buffer *buf = buffer();
+ int pos, lineStart, retLines, retPos, retLineStart, retLineEnd;
+
+ /* If we're not wrapping, use the more efficient BufCountBackwardNLines */
+ if (!mContinuousWrap)
+ return buf->rewind_lines(startPos, nLines);
+
+ pos = startPos;
+ while (true) {
+ lineStart = buf->line_start(pos);
+ wrapped_line_counter(buf, lineStart, pos, INT_MAX,
+ true, 0, &retPos, &retLines, &retLineStart, &retLineEnd);
+ if (retLines > nLines)
+ return skip_lines(lineStart, retLines-nLines,
+ true);
+ nLines -= retLines;
+ pos = lineStart - 1;
+ if (pos < 0)
+ return 0;
+ nLines -= 1;
+ }
+}
+
void Fl_Text_Display::next_word() {
int pos = insert_position();
while ( pos < buffer()->length() && (
@@ -751,6 +1127,27 @@ void Fl_Text_Display::previous_word() {
}
/*
+** Callback attached to the text buffer to receive delete information before
+** the modifications are actually made.
+*/
+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))
+ /* 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
+ modification takes place.
+ Also, a modification of the tab distance requires the same
+ kind of calculations in advance, even if the font width is "fixed",
+ because when the width of the tab characters changes, the layout
+ of the text may be completely different. */
+ textD->measure_deleted_lines(pos, nDeleted);
+ else
+ textD->mSuppressResync = 0; /* Probably not needed, but just in case */
+}
+
+/*
** Callback attached to the text buffer to receive modification information
*/
void Fl_Text_Display::buffer_modified_cb( int pos, int nInserted, int nDeleted,
@@ -758,24 +1155,49 @@ void Fl_Text_Display::buffer_modified_cb( int pos, int nInserted, int nDeleted,
int linesInserted, linesDeleted, startDispPos, endDispPos;
Fl_Text_Display *textD = ( Fl_Text_Display * ) cbArg;
Fl_Text_Buffer *buf = textD->mBuffer;
+ int oldFirstChar = textD->mFirstChar;
int scrolled, origCursorPos = textD->mCursorPos;
+ int wrapModStart, wrapModEnd;
/* buffer modification cancels vertical cursor motion column */
if ( nInserted != 0 || nDeleted != 0 )
textD->mCursorPreferredCol = -1;
- /* Count the number of lines inserted and deleted */
- linesInserted = nInserted == 0 ? 0 :
- textD->buffer()->count_lines( pos, pos + nInserted );
- linesDeleted = nDeleted == 0 ? 0 : countlines( deletedText );
+ /* Count the number of lines inserted and deleted, and in the case
+ of continuous wrap mode, how much has changed */
+ if (textD->mContinuousWrap) {
+ textD->find_wrap_range(deletedText, pos, nInserted, nDeleted,
+ &wrapModStart, &wrapModEnd, &linesInserted, &linesDeleted);
+ } else {
+ linesInserted = nInserted == 0 ? 0 :
+ buf->count_lines( pos, pos + nInserted );
+ linesDeleted = nDeleted == 0 ? 0 : countlines( deletedText );
+ }
- /* Update the line starts and topLineNum */
+ /* Update the line starts and mTopLineNum */
if ( nInserted != 0 || nDeleted != 0 ) {
- textD->update_line_starts( pos, nInserted, nDeleted, linesInserted,
+ if (textD->mContinuousWrap) {
+ textD->update_line_starts( wrapModStart, wrapModEnd-wrapModStart,
+ nDeleted + pos-wrapModStart + (wrapModEnd-(pos+nInserted)),
+ linesInserted, linesDeleted, &scrolled );
+ } else {
+ textD->update_line_starts( pos, nInserted, nDeleted, linesInserted,
linesDeleted, &scrolled );
+ }
} else
scrolled = 0;
+ /* If we're counting non-wrapped lines as well, maintain the absolute
+ (non-wrapped) line number of the text displayed */
+ if (textD->maintaining_absolute_top_line_number() &&
+ (nInserted != 0 || nDeleted != 0)) {
+ if (pos + nDeleted < oldFirstChar)
+ textD->mAbsTopLineNum += buf->count_lines(pos, pos + nInserted) -
+ countlines(deletedText);
+ else if (pos < oldFirstChar)
+ textD->reset_absolute_top_line_number();
+ }
+
/* Update the line count for the whole buffer */
textD->mNBufferLines += linesInserted - linesDeleted;
@@ -809,21 +1231,29 @@ void Fl_Text_Display::buffer_modified_cb( int pos, int nInserted, int nDeleted,
sure that the redisplay range covers the old cursor position so the
old cursor gets erased, and erase the bits of the cursor which extend
beyond the left and right edges of the text. */
- startDispPos = pos;
+ startDispPos = textD->mContinuousWrap ? wrapModStart : pos;
if ( origCursorPos == startDispPos && textD->mCursorPos != startDispPos )
startDispPos = min( startDispPos, origCursorPos - 1 );
if ( linesInserted == linesDeleted ) {
if ( nInserted == 0 && nDeleted == 0 )
endDispPos = pos + nRestyled;
else {
- endDispPos = buf->line_end( pos + nInserted ) + 1;
+ endDispPos = textD->mContinuousWrap ? wrapModEnd :
+ buf->line_end( pos + nInserted ) + 1;
// CET - FIXME if ( origCursorPos >= startDispPos &&
// ( origCursorPos <= endDispPos || endDispPos == buf->length() ) )
}
+ if (linesInserted > 1) textD->draw_line_numbers(false);
} else {
endDispPos = textD->mLastChar + 1;
// CET - FIXME if ( origCursorPos >= pos )
+ /* If more than one line is inserted/deleted, a line break may have
+ been inserted or removed in between, and the line numbers may
+ have changed. If only one line is altered, line numbers cannot
+ be affected (the insertion or removal of a line break always
+ results in at least two lines being redrawn). */
+ textD->draw_line_numbers(false);
}
/* If there is a style buffer, check if the modification caused additional
@@ -838,6 +1268,64 @@ void Fl_Text_Display::buffer_modified_cb( int pos, int nInserted, int nDeleted,
}
/*
+** In continuous wrap mode, internal line numbers are calculated after
+** wrapping. A separate non-wrapped line count is maintained when line
+** numbering is turned on. There is some performance cost to maintaining this
+** line count, so normally absolute line numbers are not tracked if line
+** numbering is off. This routine allows callers to specify that they still
+** want this line count maintained (for use via TextDPosToLineAndCol).
+** More specifically, this allows the line number reported in the statistics
+** line to be calibrated in absolute lines, rather than post-wrapped lines.
+*/
+void Fl_Text_Display::maintain_absolute_top_line_number(int state) {
+ mNeedAbsTopLineNum = state;
+ reset_absolute_top_line_number();
+}
+
+/*
+** Returns the absolute (non-wrapped) line number of the first line displayed.
+** Returns 0 if the absolute top line number is not being maintained.
+*/
+int Fl_Text_Display::get_absolute_top_line_number() {
+ if (!mContinuousWrap)
+ return mTopLineNum;
+ if (maintaining_absolute_top_line_number())
+ return mAbsTopLineNum;
+ return 0;
+}
+
+/*
+** Re-calculate absolute top line number for a change in scroll position.
+*/
+void Fl_Text_Display::absolute_top_line_number(int oldFirstChar) {
+ if (maintaining_absolute_top_line_number()) {
+ if (mFirstChar < oldFirstChar)
+ mAbsTopLineNum -= buffer()->count_lines(mFirstChar, oldFirstChar);
+ else
+ mAbsTopLineNum += buffer()->count_lines(oldFirstChar, mFirstChar);
+ }
+}
+
+/*
+** Return true if a separate absolute top line number is being maintained
+** (for displaying line numbers or showing in the statistics line).
+*/
+int Fl_Text_Display::maintaining_absolute_top_line_number() {
+ return mContinuousWrap &&
+ (mLineNumWidth != 0 || mNeedAbsTopLineNum);
+}
+
+/*
+** Count lines from the beginning of the buffer to reestablish the
+** absolute (non-wrapped) top line number. If mode is not continuous wrap,
+** or the number is not being maintained, does nothing.
+*/
+void Fl_Text_Display::reset_absolute_top_line_number() {
+ mAbsTopLineNum = 1;
+ absolute_top_line_number(0);
+}
+
+/*
** Find the line number of position "pos" relative to the first line of
** displayed text. Returns 0 if the line is not displayed.
*/
@@ -915,8 +1403,8 @@ void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip,
/* Space beyond the end of the line is still counted in units of characters
of a standardized character width (this is done mostly because style
changes based on character position can still occur in this region due
- to rectangular Fl_Text_Selections). stdCharWidth must be non-zero to prevent a
- potential infinite loop if X does not advance */
+ to rectangular Fl_Text_Selections). stdCharWidth must be non-zero to
+ prevent a potential infinite loop if X does not advance */
stdCharWidth = TMPFONTWIDTH; //mFontStruct->max_bounds.width;
if ( stdCharWidth <= 0 ) {
fprintf( stderr, "Internal Error, bad font measurement\n" );
@@ -928,12 +1416,20 @@ 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 */
- dispIndexOffset = 0;
+ /* 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
@@ -1221,12 +1717,11 @@ int Fl_Text_Display::position_style( int lineStartPos,
style = FILL_MASK;
else if ( styleBuf != NULL ) {
style = ( unsigned char ) styleBuf->character( pos );
- /*!!! if (style == mUnfinishedStyle) {
- // encountered "unfinished" style, trigger parsing
- (mUnfinishedHighlightCB)( pos, mHighlightCBArg);
- style = (unsigned char) styleBuf->character( pos);
- }
- */
+ if (style == mUnfinishedStyle) {
+ /* encountered "unfinished" style, trigger parsing */
+ (mUnfinishedHighlightCB)( pos, mHighlightCBArg);
+ style = (unsigned char) styleBuf->character( pos);
+ }
}
if (buf->primary_selection()->includes(pos, lineStartPos, dispIndex))
style |= PRIMARY_MASK;
@@ -1338,14 +1833,15 @@ void Fl_Text_Display::xy_to_rowcol( int X, int Y, int *row,
}
/*
-** Offset the line starts array, topLineNum, firstChar and lastChar, for a new
+** Offset the line starts array, mTopLineNum, mFirstChar and lastChar, for a new
** vertical scroll position given by newTopLineNum. If any currently displayed
** lines will still be visible, salvage the line starts values, otherwise,
** count lines from the nearest known line start (start or end of buffer, or
-** the closest value in the lineStarts array)
+** the closest value in the mLineStarts array)
*/
void Fl_Text_Display::offset_line_starts( int newTopLineNum ) {
int oldTopLineNum = mTopLineNum;
+ int oldFirstChar = mFirstChar;
int lineDelta = newTopLineNum - oldTopLineNum;
int nVisLines = mNVisibleLines;
int *lineStarts = mLineStarts;
@@ -1356,19 +1852,19 @@ void Fl_Text_Display::offset_line_starts( int newTopLineNum ) {
if ( lineDelta == 0 )
return;
- /* Find the new value for firstChar by counting lines from the nearest
+ /* Find the new value for mFirstChar by counting lines from the nearest
known line start (start or end of buffer, or the closest value in the
lineStarts array) */
lastLineNum = oldTopLineNum + nVisLines - 1;
if ( newTopLineNum < oldTopLineNum && newTopLineNum < -lineDelta ) {
- mFirstChar = buffer()->skip_lines( 0, newTopLineNum - 1 );
+ mFirstChar = skip_lines( 0, newTopLineNum - 1, true );
} else if ( newTopLineNum < oldTopLineNum ) {
mFirstChar = buffer()->rewind_lines( mFirstChar, -lineDelta );
} else if ( newTopLineNum < lastLineNum ) {
mFirstChar = lineStarts[ newTopLineNum - oldTopLineNum ];
} else if ( newTopLineNum - lastLineNum < mNBufferLines - newTopLineNum ) {
- mFirstChar = buffer()->skip_lines( lineStarts[ nVisLines - 1 ],
- newTopLineNum - lastLineNum );
+ mFirstChar = skip_lines( lineStarts[ nVisLines - 1 ],
+ newTopLineNum - lastLineNum, true );
} else {
mFirstChar = buffer()->rewind_lines( buf->length(), mNBufferLines - newTopLineNum + 1 );
}
@@ -1385,13 +1881,17 @@ void Fl_Text_Display::offset_line_starts( int newTopLineNum ) {
} else
calc_line_starts( 0, nVisLines );
- /* Set lastChar and topLineNum */
+ /* Set lastChar and mTopLineNum */
calc_last_char();
mTopLineNum = newTopLineNum;
+
+ /* If we're numbering lines or being asked to maintain an absolute line
+ number, re-calculate the absolute line number */
+ absolute_top_line_number(oldFirstChar);
}
/*
-** Update the line starts array, topLineNum, firstChar and lastChar for text
+** Update the line starts array, mTopLineNum, mFirstChar and lastChar for text
** display "textD" after a modification to the text buffer, given by the
** position where the change began "pos", and the nmubers of characters
** and lines inserted and deleted.
@@ -1408,7 +1908,7 @@ void Fl_Text_Display::update_line_starts( int pos, int charsInserted,
start entries and first and last characters */
if ( pos + charsDeleted < mFirstChar ) {
mTopLineNum += lineDelta;
- for ( i = 0; i < nVisLines; i++ )
+ for ( i = 0; i < nVisLines && lineStarts[i] != -1; i++ )
lineStarts[ i ] += charDelta;
mFirstChar += charDelta;
mLastChar += charDelta;
@@ -1423,14 +1923,15 @@ void Fl_Text_Display::update_line_starts( int pos, int charsInserted,
if ( position_to_line( pos + charsDeleted, &lineOfEnd ) &&
++lineOfEnd < nVisLines && lineStarts[ lineOfEnd ] != -1 ) {
mTopLineNum = max( 1, mTopLineNum + lineDelta );
- mFirstChar = buffer()->rewind_lines( lineStarts[ lineOfEnd ] + charDelta, lineOfEnd );
+ mFirstChar = buffer()->rewind_lines(
+ lineStarts[ lineOfEnd ] + charDelta, lineOfEnd );
/* Otherwise anchor on original line number and recount everything */
} else {
if ( mTopLineNum > mNBufferLines + lineDelta ) {
mTopLineNum = 1;
mFirstChar = 0;
} else
- mFirstChar = buffer()->skip_lines( 0, mTopLineNum - 1 );
+ mFirstChar = skip_lines( 0, mTopLineNum - 1, true );
}
calc_line_starts( 0, nVisLines - 1 );
/* calculate lastChar by finding the end of the last displayed line */
@@ -1524,8 +2025,7 @@ void Fl_Text_Display::calc_line_starts( int startLine, int endLine ) {
/* Loop searching for ends of lines and storing the positions of the
start of the next line in lineStarts */
for ( line = startLine; line <= endLine; line++ ) {
- lineEnd = buffer()->line_end(startPos);
- nextLineStart = min(bufLen, lineEnd + 1);
+ find_line_end(startPos, true, &lineEnd, &nextLineStart);
startPos = nextLineStart;
if ( startPos >= bufLen ) {
/* If the buffer ends with a newline or line break, put
@@ -1554,7 +2054,7 @@ void Fl_Text_Display::calc_line_starts( int startLine, int endLine ) {
void Fl_Text_Display::calc_last_char() {
int i;
for (i = mNVisibleLines - 1; i >= 0 && mLineStarts[i] == -1; i--) ;
- mLastChar = i < 0 ? 0 : buffer()->line_end(mLineStarts[i]);
+ mLastChar = i < 0 ? 0 : line_end(mLineStarts[i], true);
}
void Fl_Text_Display::scroll(int topLineNum, int horizOffset) {
@@ -1624,6 +2124,66 @@ void Fl_Text_Display::h_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) {
textD->scroll(textD->mTopLineNum, b->value());
}
+/*
+** Refresh the line number area. If clearAll is False, writes only over
+** the character cell areas. Setting clearAll to True will clear out any
+** stray marks outside of the character cell area, which might have been
+** left from before a resize or font change.
+*/
+void Fl_Text_Display::draw_line_numbers(bool clearAll) {
+#if 0
+ // FIXME: don't want this yet, so will leave for another time
+
+ int y, line, visLine, nCols, lineStart;
+ char lineNumString[12];
+ int lineHeight = mMaxsize ? mMaxsize : textsize_;
+ int charWidth = TMPFONTWIDTH; //mFontStruct->max_bounds.width;
+
+ /* Don't draw if mLineNumWidth == 0 (line numbers are hidden), or widget is
+ not yet realized */
+ if (mLineNumWidth == 0 || visible_r())
+ return;
+
+ /* GC is allocated on demand, since not everyone will use line numbering */
+ if (textD->lineNumGC == NULL) {
+ XGCValues values;
+ values.foreground = textD->lineNumFGPixel;
+ values.background = textD->bgPixel;
+ values.font = textD->fontStruct->fid;
+ textD->lineNumGC = XtGetGC(textD->w,
+ GCFont| GCForeground | GCBackground, &values);
+ }
+
+ /* Erase the previous contents of the line number area, if requested */
+ if (clearAll)
+ XClearArea(XtDisplay(textD->w), XtWindow(textD->w), textD->lineNumLeft,
+ textD->top, textD->lineNumWidth, textD->height, False);
+
+ /* Draw the line numbers, aligned to the text */
+ nCols = min(11, textD->lineNumWidth / charWidth);
+ y = textD->top;
+ line = getAbsTopLineNum(textD);
+ for (visLine=0; visLine < textD->nVisibleLines; visLine++) {
+ lineStart = textD->lineStarts[visLine];
+ if (lineStart != -1 && (lineStart==0 ||
+ BufGetCharacter(textD->buffer, lineStart-1)=='\n')) {
+ sprintf(lineNumString, "%*d", nCols, line);
+ XDrawImageString(XtDisplay(textD->w), XtWindow(textD->w),
+ textD->lineNumGC, textD->lineNumLeft, y + textD->ascent,
+ lineNumString, strlen(lineNumString));
+ line++;
+ } else {
+ XClearArea(XtDisplay(textD->w), XtWindow(textD->w),
+ textD->lineNumLeft, y, textD->lineNumWidth,
+ textD->ascent + textD->descent, False);
+ if (visLine == 0)
+ line++;
+ }
+ y += lineHeight;
+ }
+#endif
+}
+
static int max( int i1, int i2 ) {
return i1 >= i2 ? i1 : i2;
}
@@ -1706,7 +2266,478 @@ int Fl_Text_Display::vline_length( int visLineNum ) {
nextLineStart = mLineStarts[ visLineNum + 1 ];
if ( nextLineStart == -1 )
return mLastChar - lineStartPos;
- return nextLineStart - 1 - lineStartPos;
+ if (wrap_uses_character(nextLineStart-1))
+ return nextLineStart-1 - lineStartPos;
+ return nextLineStart - lineStartPos;
+}
+
+/*
+** When continuous wrap is on, and the user inserts or deletes characters,
+** wrapping can happen before and beyond the changed position. This routine
+** finds the extent of the changes, and counts the deleted and inserted lines
+** over that range. It also attempts to minimize the size of the range to
+** what has to be counted and re-displayed, so the results can be useful
+** both for delimiting where the line starts need to be recalculated, and
+** for deciding what part of the text to redisplay.
+*/
+void Fl_Text_Display::find_wrap_range(const char *deletedText, int pos,
+ int nInserted, int nDeleted, int *modRangeStart, int *modRangeEnd,
+ int *linesInserted, int *linesDeleted) {
+ int length, retPos, retLines, retLineStart, retLineEnd;
+ Fl_Text_Buffer *deletedTextBuf, *buf = buffer();
+ int nVisLines = mNVisibleLines;
+ int *lineStarts = mLineStarts;
+ int countFrom, countTo, lineStart, adjLineStart, i;
+ int visLineNum = 0, nLines = 0;
+
+ /*
+ ** Determine where to begin searching: either the previous newline, or
+ ** if possible, limit to the start of the (original) previous displayed
+ ** line, using information from the existing line starts array
+ */
+ if (pos >= mFirstChar && pos <= mLastChar) {
+ for (i=nVisLines-1; i>0; i--)
+ if (lineStarts[i] != -1 && pos >= lineStarts[i])
+ break;
+ if (i > 0) {
+ countFrom = lineStarts[i-1];
+ visLineNum = i-1;
+ } else
+ countFrom = buf->line_start(pos);
+ } else
+ countFrom = buf->line_start(pos);
+
+
+ /*
+ ** Move forward through the (new) text one line at a time, counting
+ ** displayed lines, and looking for either a real newline, or for the
+ ** line starts to re-sync with the original line starts array
+ */
+ lineStart = countFrom;
+ *modRangeStart = countFrom;
+ while (true) {
+
+ /* advance to the next line. If the line ended in a real newline
+ or the end of the buffer, that's far enough */
+ wrapped_line_counter(buf, lineStart, buf->length(), 1, true, 0,
+ &retPos, &retLines, &retLineStart, &retLineEnd);
+ if (retPos >= buf->length()) {
+ countTo = buf->length();
+ *modRangeEnd = countTo;
+ if (retPos != retLineEnd)
+ nLines++;
+ break;
+ } else
+ lineStart = retPos;
+ nLines++;
+ if (lineStart > pos + nInserted &&
+ buf->character(lineStart-1) == '\n') {
+ countTo = lineStart;
+ *modRangeEnd = lineStart;
+ break;
+ }
+
+ /* Don't try to resync in continuous wrap mode with non-fixed font
+ sizes; it would result in a chicken-and-egg dependency between
+ the calculations for the inserted and the deleted lines.
+ If we're in that mode, the number of deleted lines is calculated in
+ advance, without resynchronization, so we shouldn't resynchronize
+ for the inserted lines either. */
+ if (mSuppressResync)
+ continue;
+
+ /* check for synchronization with the original line starts array
+ before pos, if so, the modified range can begin later */
+ if (lineStart <= pos) {
+ while (visLineNum<nVisLines && lineStarts[visLineNum] < lineStart)
+ visLineNum++;
+ if (visLineNum < nVisLines && lineStarts[visLineNum] == lineStart) {
+ countFrom = lineStart;
+ nLines = 0;
+ if (visLineNum+1 < nVisLines && lineStarts[visLineNum+1] != -1)
+ *modRangeStart = min(pos, lineStarts[visLineNum+1]-1);
+ else
+ *modRangeStart = countFrom;
+ } else
+ *modRangeStart = min(*modRangeStart, lineStart-1);
+ }
+
+ /* check for synchronization with the original line starts array
+ after pos, if so, the modified range can end early */
+ else if (lineStart > pos + nInserted) {
+ adjLineStart = lineStart - nInserted + nDeleted;
+ while (visLineNum<nVisLines && lineStarts[visLineNum]<adjLineStart)
+ visLineNum++;
+ if (visLineNum < nVisLines && lineStarts[visLineNum] != -1 &&
+ lineStarts[visLineNum] == adjLineStart) {
+ countTo = line_end(lineStart, true);
+ *modRangeEnd = lineStart;
+ break;
+ }
+ }
+ }
+ *linesInserted = nLines;
+
+
+ /* Count deleted lines between countFrom and countTo as the text existed
+ before the modification (that is, as if the text between pos and
+ pos+nInserted were replaced by "deletedText"). This extra context is
+ necessary because wrapping can occur outside of the modified region
+ as a result of adding or deleting text in the region. This is done by
+ creating a textBuffer containing the deleted text and the necessary
+ additional context, and calling the wrappedLineCounter on it.
+
+ NOTE: This must not be done in continuous wrap mode when the font
+ width is not fixed. In that case, the calculation would try
+ to access style information that is no longer available (deleted
+ text), or out of date (updated highlighting), possibly leading
+ to completely wrong calculations and/or even crashes eventually.
+ (This is not theoretical; it really happened.)
+
+ In that case, the calculation of the number of deleted lines
+ has happened before the buffer was modified (only in that case,
+ because resynchronization of the line starts is impossible
+ in that case, which makes the whole calculation less efficient).
+ */
+ if (mSuppressResync) {
+ *linesDeleted = mNLinesDeleted;
+ mSuppressResync = 0;
+ return;
+ }
+
+ length = (pos-countFrom) + nDeleted +(countTo-(pos+nInserted));
+ deletedTextBuf = new Fl_Text_Buffer(length);
+ deletedTextBuf->copy(buffer(), countFrom, pos, 0);
+ if (nDeleted != 0)
+ deletedTextBuf->insert(pos-countFrom, deletedText);
+ deletedTextBuf->copy(buffer(),
+ pos+nInserted, countTo, pos-countFrom+nDeleted);
+ /* Note that we need to take into account an offset for the style buffer:
+ the deletedTextBuf can be out of sync with the style buffer. */
+ wrapped_line_counter(deletedTextBuf, 0, length, INT_MAX, true,
+ countFrom, &retPos, &retLines, &retLineStart, &retLineEnd, false);
+ delete deletedTextBuf;
+ *linesDeleted = retLines;
+ mSuppressResync = 0;
+}
+
+/*
+** This is a stripped-down version of the findWrapRange() function above,
+** intended to be used to calculate the number of "deleted" lines during
+** a buffer modification. It is called _before_ the modification takes place.
+**
+** This function should only be called in continuous wrap mode with a
+** non-fixed font width. In that case, it is impossible to calculate
+** the number of deleted lines, because the necessary style information
+** is no longer available _after_ the modification. In other cases, we
+** can still perform the calculation afterwards (possibly even more
+** efficiently).
+*/
+void Fl_Text_Display::measure_deleted_lines(int pos, int nDeleted) {
+ int retPos, retLines, retLineStart, retLineEnd;
+ Fl_Text_Buffer *buf = buffer();
+ int nVisLines = mNVisibleLines;
+ int *lineStarts = mLineStarts;
+ int countFrom, lineStart;
+ int visLineNum = 0, nLines = 0, i;
+ /*
+ ** Determine where to begin searching: either the previous newline, or
+ ** if possible, limit to the start of the (original) previous displayed
+ ** line, using information from the existing line starts array
+ */
+ if (pos >= mFirstChar && pos <= mLastChar) {
+ for (i=nVisLines-1; i>0; i--)
+ if (lineStarts[i] != -1 && pos >= lineStarts[i])
+ break;
+ if (i > 0) {
+ countFrom = lineStarts[i-1];
+ visLineNum = i-1;
+ } else
+ countFrom = buf->line_start(pos);
+ } else
+ countFrom = buf->line_start(pos);
+
+ /*
+ ** Move forward through the (new) text one line at a time, counting
+ ** displayed lines, and looking for either a real newline, or for the
+ ** line starts to re-sync with the original line starts array
+ */
+ lineStart = countFrom;
+ while (true) {
+ /* advance to the next line. If the line ended in a real newline
+ or the end of the buffer, that's far enough */
+ wrapped_line_counter(buf, lineStart, buf->length(), 1, true, 0,
+ &retPos, &retLines, &retLineStart, &retLineEnd);
+ if (retPos >= buf->length()) {
+ if (retPos != retLineEnd)
+ nLines++;
+ break;
+ } else
+ lineStart = retPos;
+ nLines++;
+ if (lineStart > pos + nDeleted &&
+ buf->character(lineStart-1) == '\n') {
+ break;
+ }
+
+ /* Unlike in the findWrapRange() function above, we don't try to
+ resync with the line starts, because we don't know the length
+ of the inserted text yet, nor the updated style information.
+
+ Because of that, we also shouldn't resync with the line starts
+ after the modification either, because we must perform the
+ calculations for the deleted and inserted lines in the same way.
+
+ This can result in some unnecessary recalculation and redrawing
+ overhead, and therefore we should only use this two-phase mode
+ of calculation when it's really needed (continuous wrap + variable
+ font width). */
+ }
+ mNLinesDeleted = nLines;
+ mSuppressResync = 1;
+}
+
+/*
+** Count forward from startPos to either maxPos or maxLines (whichever is
+** reached first), and return all relevant positions and line count.
+** The provided textBuffer may differ from the actual text buffer of the
+** widget. In that case it must be a (partial) copy of the actual text buffer
+** and the styleBufOffset argument must indicate the starting position of the
+** copy, to take into account the correct style information.
+**
+** Returned values:
+**
+** retPos: Position where counting ended. When counting lines, the
+** position returned is the start of the line "maxLines"
+** lines beyond "startPos".
+** retLines: Number of line breaks counted
+** retLineStart: Start of the line where counting ended
+** retLineEnd: End position of the last line traversed
+*/
+void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos,
+ int maxPos, int maxLines, bool startPosIsLineStart, int styleBufOffset,
+ int *retPos, int *retLines, int *retLineStart, int *retLineEnd,
+ bool countLastLineMissingNewLine) {
+ int lineStart, newLineStart = 0, b, p, colNum, wrapMargin;
+ int maxWidth, i, foundBreak, width;
+ bool countPixels;
+ int nLines = 0, tabDist = buffer()->tab_distance();
+ unsigned char c;
+ char nullSubsChar = buffer()->null_substitution_character();
+
+ /* 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) {
+ countPixels = false;
+ wrapMargin = mWrapMargin ? mWrapMargin : text_area.w / (mFixedFontWidth + 1);
+ maxWidth = INT_MAX;
+ } else {
+ countPixels = true;
+ wrapMargin = INT_MAX;
+ maxWidth = text_area.w;
+ }
+
+ /* Find the start of the line if the start pos is not marked as a
+ line start. */
+ if (startPosIsLineStart)
+ lineStart = startPos;
+ else
+ lineStart = line_start(startPos);
+
+ /*
+ ** Loop until position exceeds maxPos or line count exceeds maxLines.
+ ** (actually, contines 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++) {
+ c = buf->character(p);
+
+ /* If the character was a newline, count the line and start over,
+ otherwise, add it to the width and column counts */
+ if (c == '\n') {
+ if (p >= maxPos) {
+ *retPos = maxPos;
+ *retLines = nLines;
+ *retLineStart = lineStart;
+ *retLineEnd = maxPos;
+ return;
+ }
+ nLines++;
+ if (nLines >= maxLines) {
+ *retPos = p + 1;
+ *retLines = nLines;
+ *retLineStart = p + 1;
+ *retLineEnd = p;
+ return;
+ }
+ lineStart = p + 1;
+ colNum = 0;
+ width = 0;
+ } else {
+ colNum += Fl_Text_Buffer::character_width(c, colNum, tabDist, nullSubsChar);
+ if (countPixels)
+ width += measure_proportional_character(c, colNum, p+styleBufOffset);
+ }
+
+ /* If character exceeded wrap margin, find the break point
+ and wrap there */
+ if (colNum > wrapMargin || width > maxWidth) {
+ foundBreak = false;
+ for (b=p; b>=lineStart; b--) {
+ c = buf->character(b);
+ if (c == '\t' || c == ' ') {
+ newLineStart = b + 1;
+ if (countPixels) {
+ colNum = 0;
+ width = 0;
+ for (i=b+1; i<p+1; i++) {
+ width += measure_proportional_character(
+ buf->character(i), colNum,
+ i+styleBufOffset);
+ colNum++;
+ }
+ } else
+ colNum = buf->count_displayed_characters(b+1, p+1);
+ foundBreak = true;
+ break;
+ }
+ }
+ if (!foundBreak) { /* no whitespace, just break at margin */
+ newLineStart = max(p, lineStart+1);
+ colNum = Fl_Text_Buffer::character_width(c, colNum, tabDist, nullSubsChar);
+ if (countPixels)
+ width = measure_proportional_character(c, colNum, p+styleBufOffset);
+ }
+ if (p >= maxPos) {
+ *retPos = maxPos;
+ *retLines = maxPos < newLineStart ? nLines : nLines + 1;
+ *retLineStart = maxPos < newLineStart ? lineStart :
+ newLineStart;
+ *retLineEnd = maxPos;
+ return;
+ }
+ nLines++;
+ if (nLines >= maxLines) {
+ *retPos = foundBreak ? b + 1 : max(p, lineStart+1);
+ *retLines = nLines;
+ *retLineStart = lineStart;
+ *retLineEnd = foundBreak ? b : p;
+ return;
+ }
+ lineStart = newLineStart;
+ }
+ }
+
+ /* reached end of buffer before reaching pos or line target */
+ *retPos = buf->length();
+ *retLines = nLines;
+ if (countLastLineMissingNewLine && colNum > 0)
+ ++(*retLines);
+ *retLineStart = lineStart;
+ *retLineEnd = buf->length();
+}
+
+/*
+** Measure the width in pixels of a character "c" at a particular column
+** "colNum" and buffer position "pos". This is for measuring characters in
+** proportional or mixed-width highlighting fonts.
+**
+** A note about proportional and mixed-width fonts: the mixed width and
+** proportional font code in nedit does not get much use in general editing,
+** because nedit doesn't allow per-language-mode fonts, and editing programs
+** in a proportional font is usually a bad idea, so very few users would
+** choose a proportional font as a default. There are still probably mixed-
+** width syntax highlighting cases where things don't redraw properly for
+** 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(char c, int colNum, int pos) {
+ int charLen, style;
+ char expChar[ FL_TEXT_MAX_EXP_CHAR_LEN ];
+ Fl_Text_Buffer *styleBuf = mStyleBuffer;
+
+ charLen = Fl_Text_Buffer::expand_character(c, colNum, expChar,
+ buffer()->tab_distance(), buffer()->null_substitution_character());
+ if (styleBuf == 0) {
+ style = 0;
+ } else {
+ style = (unsigned char)styleBuf->character(pos);
+ if (style == mUnfinishedStyle) {
+ /* encountered "unfinished" style, trigger parsing */
+ (mUnfinishedHighlightCB)(pos, mHighlightCBArg);
+ style = (unsigned char)styleBuf->character(pos);
+ }
+ }
+ return string_width(expChar, charLen, style);
+}
+
+/*
+** Finds both the end of the current line and the start of the next line. Why?
+** In continuous wrap mode, if you need to know both, figuring out one from the
+** other can be expensive or error prone. The problem comes when there's a
+** trailing space or tab just before the end of the buffer. To translate an
+** end of line value to or from the next lines start value, you need to know
+** whether the trailing space or tab is being used as a line break or just a
+** normal character, and to find that out would otherwise require counting all
+** the way back to the beginning of the line.
+*/
+void Fl_Text_Display::find_line_end(int startPos, bool startPosIsLineStart,
+ int *lineEnd, int *nextLineStart) {
+ int retLines, retLineStart;
+
+ /* if we're not wrapping use more efficient BufEndOfLine */
+ if (!mContinuousWrap) {
+ *lineEnd = buffer()->line_end(startPos);
+ *nextLineStart = min(buffer()->length(), *lineEnd + 1);
+ return;
+ }
+
+ /* use the wrapped line counter routine to count forward one line */
+ wrapped_line_counter(buffer(), startPos, buffer()->length(),
+ 1, startPosIsLineStart, 0, nextLineStart, &retLines,
+ &retLineStart, lineEnd);
+ return;
+}
+
+/*
+** Line breaks in continuous wrap mode usually happen at newlines or
+** whitespace. This line-terminating character is not included in line
+** width measurements and has a special status as a non-visible character.
+** However, lines with no whitespace are wrapped without the benefit of a
+** line terminating character, and this distinction causes endless trouble
+** with all of the text display code which was originally written without
+** continuous wrap mode and always expects to wrap at a newline character.
+**
+** Given the position of the end of the line, as returned by TextDEndOfLine
+** or BufEndOfLine, this returns true if there is a line terminating
+** character, and false if there's not. On the last character in the
+** buffer, this function can't tell for certain whether a trailing space was
+** used as a wrap point, and just guesses that it wasn't. So if an exact
+** accounting is necessary, don't use this function.
+*/
+int Fl_Text_Display::wrap_uses_character(int lineEndPos) {
+ char c;
+
+ if (!mContinuousWrap || lineEndPos == buffer()->length())
+ return 1;
+
+ c = buffer()->character(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(Fl_Text_Selection *sel,
+ int rangeStart, int rangeEnd) {
+ return sel->selected() && sel->rectangular() && sel->end() >= rangeStart &&
+ sel->start() <= rangeEnd;
}
/*
@@ -1976,5 +3007,5 @@ int Fl_Text_Display::handle(int event) {
//
-// End of "$Id: Fl_Text_Display.cxx,v 1.12.2.27 2002/08/20 17:12:19 easysw Exp $".
+// End of "$Id: Fl_Text_Display.cxx,v 1.12.2.28 2002/09/20 19:59:45 easysw Exp $".
//
diff --git a/test/editor.cxx b/test/editor.cxx
index 4367f1cf8..3000980b2 100644
--- a/test/editor.cxx
+++ b/test/editor.cxx
@@ -1,5 +1,5 @@
//
-// "$Id: editor.cxx,v 1.2.2.3.2.13 2002/07/14 21:25:39 easysw Exp $"
+// "$Id: editor.cxx,v 1.2.2.3.2.14 2002/09/20 19:59:45 easysw Exp $"
//
// A simple text editor program for the Fast Light Tool Kit (FLTK).
//
@@ -306,7 +306,7 @@ style_init(void) {
//
void
-style_unfinished_cb() {
+style_unfinished_cb(int, void*) {
}
@@ -762,5 +762,5 @@ int main(int argc, char **argv) {
}
//
-// End of "$Id: editor.cxx,v 1.2.2.3.2.13 2002/07/14 21:25:39 easysw Exp $".
+// End of "$Id: editor.cxx,v 1.2.2.3.2.14 2002/09/20 19:59:45 easysw Exp $".
//