#include <stdio.h>
#include <stdlib.h>
#include <FL/fl_utf8.h>
#include <FL/fl_string_functions.h>
#include "flstring.h"
#include <limits.h>
#include <ctype.h>
#include <string.h>
#include <FL/Fl.H>
#include <FL/platform.H>
#include <FL/Fl_Text_Buffer.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Input.H>
#include "Fl_Screen_Driver.H"
#undef min
#undef max
#define LINENUM_LEFT_OF_VSCROLL
#define TOP_MARGIN 1
#define BOTTOM_MARGIN 1
#define LEFT_MARGIN 3
#define RIGHT_MARGIN 3
#define NO_HINT -1
#define FILL_MASK 0x0100
#define SECONDARY_MASK 0x0200
#define PRIMARY_MASK 0x0400
#define HIGHLIGHT_MASK 0x0800
#define BG_ONLY_MASK 0x1000
#define TEXT_ONLY_MASK 0x2000
#define STYLE_LOOKUP_MASK 0xff
#define MAX_DISP_LINE_LEN 1000
static int max( int i1, int i2 );
static int min( int i1, int i2 );
static int countlines( const char *string );
static int scroll_direction = 0;
static int scroll_amount = 0;
static int scroll_y = 0;
static int scroll_x = 0;
static Fl_Menu_Item rmb_menu[] = {
{ NULL, 0, NULL, (void*)1 },
{ NULL, 0, NULL, (void*)2 },
{ NULL, 0, NULL, (void*)3 },
{ NULL }
};
#define TMPFONTWIDTH 6
Fl_Text_Display::Fl_Text_Display(int X, int Y, int W, int H, const char* l)
: Fl_Group(X, Y, W, H, l) {
#define VISIBLE_LINES_INIT 1
damage_range1_start = damage_range1_end = -1;
damage_range2_start = damage_range2_end = -1;
mCursorPos = 0;
mCursorOn = 0;
mCursorOldY = -100;
mCursorToHint = NO_HINT;
mCursorStyle = NORMAL_CURSOR;
mCursorPreferredXPos = -1;
mNVisibleLines = VISIBLE_LINES_INIT;
mNBufferLines = 0;
mBuffer = NULL;
mStyleBuffer = NULL;
mFirstChar = 0;
mLastChar = 0;
mContinuousWrap = 0;
mWrapMarginPix = 0;
mLineStarts = new int[mNVisibleLines];
#if VISIBLE_LINES_INIT > 1
{ for (int i=1; i<mNVisibleLines; i++) mLineStarts[i] = -1;
}
#endif
mLineStarts[0] = 0;
mTopLineNum = 1;
mAbsTopLineNum = 1;
mNeedAbsTopLineNum = 0;
mHorizOffset = 0;
mTopLineNumHint = 1;
mHorizOffsetHint = 0;
mNStyles = 0;
mStyleTable = NULL;
mUnfinishedStyle = 0;
mUnfinishedHighlightCB = 0;
mHighlightCBArg = 0;
mMaxsize = 0;
mSuppressResync = 0;
mNLinesDeleted = 0;
mModifyingTabDistance = 0; mColumnScale = 0;
mCursor_color = FL_FOREGROUND_COLOR;
mHScrollBar = new Fl_Scrollbar(0,0,1,1);
mHScrollBar->callback((Fl_Callback*)h_scrollbar_cb, this);
mHScrollBar->type(FL_HORIZONTAL);
mVScrollBar = new Fl_Scrollbar(0,0,1,1);
mVScrollBar->callback((Fl_Callback*)v_scrollbar_cb, this);
display_needs_recalc_ = false;
scrollbar_width_ = 0; scrollbar_align_ = FL_ALIGN_BOTTOM_RIGHT;
dragPos = 0;
dragType = DRAG_CHAR;
dragging = 0;
display_insert_position_hint = 0;
text_area.x = 0;
text_area.y = 0;
text_area.w = 0;
text_area.h = 0;
shortcut_ = 0;
textfont_ = FL_HELVETICA; textsize_ = FL_NORMAL_SIZE; textcolor_ = FL_FOREGROUND_COLOR; grammar_underline_color_ = FL_BLUE;
spelling_underline_color_ = FL_RED;
secondary_selection_color_ = FL_GRAY;
mLineNumLeft = 0; mLineNumWidth = 0;
linenumber_font_ = FL_HELVETICA;
linenumber_size_ = FL_NORMAL_SIZE;
linenumber_fgcolor_ = FL_INACTIVE_COLOR;
linenumber_bgcolor_ = 53; linenumber_align_ = FL_ALIGN_RIGHT;
linenumber_format_ = fl_strdup("%d");
color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR);
box(FL_DOWN_FRAME);
set_flag(SHORTCUT_LABEL);
clear_flag(NEEDS_KEYBOARD);
end();
}
Fl_Text_Display::~Fl_Text_Display() {
if (scroll_direction) {
Fl::remove_timeout(scroll_timer_cb, this);
scroll_direction = 0;
}
if (mBuffer) {
mBuffer->remove_modify_callback(buffer_modified_cb, this);
mBuffer->remove_predelete_callback(buffer_predelete_cb, this);
}
if (mLineStarts) delete[] mLineStarts;
if (linenumber_format_) {
free((void*)linenumber_format_);
linenumber_format_ = 0;
}
}
void Fl_Text_Display::linenumber_width(int width) {
if (width < 0) return;
mLineNumWidth = width;
display_needs_recalc(); if (width > 0) reset_absolute_top_line_number();
}
int Fl_Text_Display::linenumber_width() const {
return mLineNumWidth;
}
void Fl_Text_Display::linenumber_font(Fl_Font val) {
linenumber_font_ = val;
}
Fl_Font Fl_Text_Display::linenumber_font() const {
return linenumber_font_;
}
void Fl_Text_Display::linenumber_size(Fl_Fontsize val) {
linenumber_size_ = val;
}
Fl_Fontsize Fl_Text_Display::linenumber_size() const {
return linenumber_size_;
}
void Fl_Text_Display::linenumber_fgcolor(Fl_Color val) {
linenumber_fgcolor_ = val;
}
Fl_Color Fl_Text_Display::linenumber_fgcolor() const {
return linenumber_fgcolor_;
}
void Fl_Text_Display::linenumber_bgcolor(Fl_Color val) {
linenumber_bgcolor_ = val;
}
Fl_Color Fl_Text_Display::linenumber_bgcolor() const {
return linenumber_bgcolor_;
}
void Fl_Text_Display::linenumber_align(Fl_Align val) {
linenumber_align_ = val;
}
Fl_Align Fl_Text_Display::linenumber_align() const {
return linenumber_align_;
}
void Fl_Text_Display::linenumber_format(const char* val) {
if ( linenumber_format_ ) free((void*)linenumber_format_);
linenumber_format_ = val ? fl_strdup(val) : 0;
}
const char* Fl_Text_Display::linenumber_format() const {
return linenumber_format_;
}
void Fl_Text_Display::buffer( Fl_Text_Buffer *buf ) {
if ( buf == mBuffer) return;
if ( mBuffer != 0 ) {
char *deletedText = mBuffer->text();
buffer_modified_cb( 0, 0, mBuffer->length(), 0, deletedText, this );
free(deletedText);
mNBufferLines = 0;
mBuffer->remove_modify_callback( buffer_modified_cb, this );
mBuffer->remove_predelete_callback( buffer_predelete_cb, this );
}
mBuffer = buf;
if (mBuffer) {
mBuffer->add_modify_callback( buffer_modified_cb, this );
mBuffer->add_predelete_callback( buffer_predelete_cb, this );
buffer_modified_cb( 0, buf->length(), 0, 0, 0, this );
}
display_needs_recalc(); }
void Fl_Text_Display::highlight_data(Fl_Text_Buffer *styleBuffer,
const Style_Table_Entry *styleTable,
int nStyles, char unfinishedStyle,
Unfinished_Style_Cb unfinishedHighlightCB,
void *cbArg ) {
mStyleBuffer = styleBuffer;
mStyleTable = styleTable;
mNStyles = nStyles;
mUnfinishedStyle = unfinishedStyle;
mUnfinishedHighlightCB = unfinishedHighlightCB;
mHighlightCBArg = cbArg;
mColumnScale = 0;
if (mStyleBuffer)
mStyleBuffer->canUndo(0);
damage(FL_DAMAGE_EXPOSE);
}
int Fl_Text_Display::longest_vline() const {
int longest = 0;
for (int i = 0; i < mNVisibleLines; i++)
longest = max(longest, measure_vline(i));
return longest;
}
void Fl_Text_Display::resize(int X, int Y, int W, int H) {
#ifdef DEBUG2
printf("\n");
printf("Fl_Text_Display::resize(X=%d, Y=%d, W=%d, H=%d)\n", X, Y, W, H);
printf(" current size(x=%d, y=%d, w=%d, h=%d)\n", x(), y(), w(), h());
printf(" box_d* size(x=%d, y=%d, w=%d, h=%d)\n",
Fl::box_dx(box()),Fl::box_dy(box()),Fl::box_dw(box()),Fl::box_dh(box()));
printf(" text_area size(x=%d, y=%d, w=%d, h=%d)\n",
text_area.x, text_area.y, text_area.w, text_area.h);
printf(" mContinuousWrap=%d, mWrapMarginPix=%d\n",
mContinuousWrap, mWrapMarginPix);
fflush(stdout);
#endif
Fl_Widget::resize(X,Y,W,H);
mColumnScale = 0; display_needs_recalc();
}
void Fl_Text_Display::display_needs_recalc() {
display_needs_recalc_ = true;
redraw(); }
void Fl_Text_Display::recalc_display() {
if (!buffer()) return;
Fl_Display_Device::display_device();
unsigned int hscrollbarvisible = mHScrollBar->visible();
unsigned int vscrollbarvisible = mVScrollBar->visible();
int scrollsize = scrollbar_width_ ? scrollbar_width_ : Fl::scrollbar_size();
int X = x() + Fl::box_dx(box());
int Y = y() + Fl::box_dy(box());
int W = w() - Fl::box_dw(box());
int H = h() - Fl::box_dh(box());
text_area.x = X + LEFT_MARGIN + mLineNumWidth;
text_area.y = Y + TOP_MARGIN;
text_area.w = W - LEFT_MARGIN - RIGHT_MARGIN - mLineNumWidth;
text_area.h = H - TOP_MARGIN - BOTTOM_MARGIN;
int i;
for (i = 0, mMaxsize = fl_height(textfont(), textsize()); i < mNStyles; i++)
mMaxsize = max(mMaxsize, fl_height(mStyleTable[i].font, mStyleTable[i].size));
mVScrollBar->clear_visible();
mHScrollBar->clear_visible();
int oldTAWidth = -1;
if (mContinuousWrap && !mWrapMarginPix) {
int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize;
int nlines = buffer()->count_lines(0,buffer()->length());
if (nvlines < 1) nvlines = 1;
if (nlines >= nvlines-1) {
mVScrollBar->set_visible(); text_area.w -= scrollsize;
}
}
for (int again = 1; again;) {
again = 0;
#ifdef DEBUG2
printf("*** again ... text_area.w = %d, oldTAWidth = %d, diff = %d\n",
text_area.w, oldTAWidth, text_area.w - oldTAWidth);
#endif
if (mContinuousWrap && !mWrapMarginPix && text_area.w != oldTAWidth) {
int oldFirstChar = mFirstChar;
mFirstChar = line_start(mFirstChar);
mTopLineNum = count_lines(0, mFirstChar, true)+1;
mNBufferLines = mTopLineNum-1 + count_lines(mFirstChar, buffer()->length(), true);
absolute_top_line_number(oldFirstChar);
#ifdef DEBUG2
printf(" mNBufferLines=%d\n", mNBufferLines);
#endif
}
oldTAWidth = text_area.w;
int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize;
if (nvlines < 1) nvlines = 1;
if (mNVisibleLines != nvlines) {
mNVisibleLines = nvlines;
if (mLineStarts) delete[] mLineStarts;
mLineStarts = new int [mNVisibleLines];
}
calc_line_starts(0, mNVisibleLines);
calc_last_char();
if (scrollsize) {
if (!mVScrollBar->visible() &&
scrollbar_align() & (FL_ALIGN_LEFT|FL_ALIGN_RIGHT) &&
mNBufferLines >= mNVisibleLines - ((mContinuousWrap && mWrapMarginPix) ? 0 : 1))
{
mVScrollBar->set_visible();
text_area.w -= scrollsize;
again = 1;
}
if (!mHScrollBar->visible() &&
scrollbar_align() & (FL_ALIGN_TOP|FL_ALIGN_BOTTOM) &&
(mVScrollBar->visible() || longest_vline() > text_area.w))
{
char wrap_at_bounds = mContinuousWrap && (mWrapMarginPix<text_area.w);
if (!wrap_at_bounds) {
mHScrollBar->set_visible();
text_area.h -= scrollsize;
again = 1; }
}
}
}
text_area.x = X + mLineNumWidth + LEFT_MARGIN;
if (mVScrollBar->visible() && scrollbar_align() & FL_ALIGN_LEFT)
text_area.x += scrollsize;
text_area.y = Y + TOP_MARGIN;
if (mHScrollBar->visible() &&
scrollbar_align() & FL_ALIGN_TOP)
text_area.y += scrollsize;
if (mVScrollBar->visible()) {
if (scrollbar_align() & FL_ALIGN_LEFT) {
#ifdef LINENUM_LEFT_OF_VSCROLL
mVScrollBar->resize(text_area.x - LEFT_MARGIN - scrollsize,
text_area.y - TOP_MARGIN,
scrollsize,
text_area.h + TOP_MARGIN + BOTTOM_MARGIN);
#else
mVScrollBar->resize(X,
text_area.y - TOP_MARGIN,
scrollsize,
text_area.h + TOP_MARGIN + BOTTOM_MARGIN);
#endif
} else {
mVScrollBar->resize(X+W-scrollsize,
text_area.y - TOP_MARGIN,
scrollsize,
text_area.h + TOP_MARGIN + BOTTOM_MARGIN);
}
}
if (mHScrollBar->visible()) {
if (scrollbar_align() & FL_ALIGN_TOP) {
mHScrollBar->resize(text_area.x - LEFT_MARGIN,
Y,
text_area.w + LEFT_MARGIN + RIGHT_MARGIN,
scrollsize);
} else {
mHScrollBar->resize(text_area.x - LEFT_MARGIN,
Y + H - scrollsize,
text_area.w + LEFT_MARGIN + RIGHT_MARGIN,
scrollsize);
}
}
if (mTopLineNumHint != mTopLineNum || mHorizOffsetHint != mHorizOffset)
scroll_(mTopLineNumHint, mHorizOffsetHint);
if ((mNBufferLines+1 < mNVisibleLines) || (mBuffer == NULL) || (mBuffer->length() == 0)) {
scroll_(1, mHorizOffset);
} else {
while ( mNVisibleLines>=2
&& (mLineStarts[mNVisibleLines-2]==-1)
&& scroll_(mTopLineNum-1, mHorizOffset))
{ }
}
if (display_insert_position_hint)
display_insert();
int maxhoffset = max(0, longest_vline()-text_area.w);
if (mHorizOffset > maxhoffset)
scroll_(mTopLineNumHint, maxhoffset);
mTopLineNumHint = mTopLineNum;
mHorizOffsetHint = mHorizOffset;
display_insert_position_hint = 0;
if (mContinuousWrap ||
hscrollbarvisible != mHScrollBar->visible() ||
vscrollbarvisible != mVScrollBar->visible())
redraw();
update_v_scrollbar();
update_h_scrollbar();
}
void Fl_Text_Display::draw_text( int left, int top, int width, int height ) {
int fontHeight, firstLine, lastLine, line;
fontHeight = mMaxsize ? mMaxsize : textsize_;
firstLine = ( top - text_area.y - fontHeight + 1 ) / fontHeight;
lastLine = ( top + height - text_area.y ) / fontHeight + 1;
fl_push_clip( left, top, width, height );
for ( line = firstLine; line <= lastLine; line++ )
draw_vline( line, left, left + width, 0, INT_MAX );
fl_pop_clip();
}
void Fl_Text_Display::redisplay_range(int startpos, int endpos) {
IS_UTF8_ALIGNED2(buffer(), startpos)
IS_UTF8_ALIGNED2(buffer(), endpos)
if (damage_range1_start == -1 && damage_range1_end == -1) {
damage_range1_start = startpos;
damage_range1_end = endpos;
} else if ((startpos >= damage_range1_start && startpos <= damage_range1_end) ||
(endpos >= damage_range1_start && endpos <= damage_range1_end)) {
damage_range1_start = min(damage_range1_start, startpos);
damage_range1_end = max(damage_range1_end, endpos);
} else if (damage_range2_start == -1 && damage_range2_end == -1) {
damage_range2_start = startpos;
damage_range2_end = endpos;
} else {
damage_range2_start = min(damage_range2_start, startpos);
damage_range2_end = max(damage_range2_end, endpos);
}
damage(FL_DAMAGE_SCROLL);
}
void Fl_Text_Display::draw_range(int startpos, int endpos) {
startpos = buffer()->utf8_align(startpos);
endpos = buffer()->utf8_align(endpos);
int i, startLine, lastLine, startIndex, endIndex;
if ( endpos < mFirstChar || ( startpos > mLastChar && !empty_vlines() ) )
return;
if ( startpos < 0 ) startpos = 0;
if ( startpos > mBuffer->length() ) startpos = mBuffer->length();
if ( endpos < 0 ) endpos = 0;
if ( endpos > mBuffer->length() ) endpos = mBuffer->length();
if ( startpos < mFirstChar )
startpos = mFirstChar;
if ( !position_to_line( startpos, &startLine ) )
startLine = mNVisibleLines - 1;
if ( endpos >= mLastChar ) {
lastLine = mNVisibleLines - 1;
} else {
if ( !position_to_line( endpos, &lastLine ) ) {
lastLine = mNVisibleLines - 1;
}
}
startIndex = mLineStarts[ startLine ] == -1 ? 0 : startpos - mLineStarts[ startLine ];
if ( endpos >= mLastChar )
endIndex = INT_MAX;
else if ( mLineStarts[ lastLine ] == -1 )
endIndex = 0;
else
endIndex = endpos - mLineStarts[ lastLine ];
if ( startLine == lastLine ) {
draw_vline( startLine, 0, INT_MAX, startIndex, endIndex );
return;
}
draw_vline( startLine, 0, INT_MAX, startIndex, INT_MAX );
for ( i = startLine + 1; i < lastLine; i++ )
draw_vline( i, 0, INT_MAX, 0, INT_MAX );
draw_vline( lastLine, 0, INT_MAX, 0, endIndex );
}
void Fl_Text_Display::insert_position( int newPos ) {
IS_UTF8_ALIGNED2(buffer(), newPos)
if ( newPos == mCursorPos ) return;
if ( newPos < 0 ) newPos = 0;
if ( newPos > mBuffer->length() ) newPos = mBuffer->length();
mCursorPreferredXPos = -1;
redisplay_range(buffer()->prev_char_clipped(mCursorPos), buffer()->next_char(mCursorPos));
mCursorPos = newPos;
redisplay_range(buffer()->prev_char_clipped(mCursorPos), buffer()->next_char(mCursorPos));
}
void Fl_Text_Display::show_cursor(int b) {
mCursorOn = b;
if (!buffer()) return;
redisplay_range(buffer()->prev_char_clipped(mCursorPos), buffer()->next_char(mCursorPos));
}
void Fl_Text_Display::cursor_style(int style) {
mCursorStyle = style;
if (mCursorOn) show_cursor();
}
void Fl_Text_Display::wrap_mode(int wrap, int wrapMargin) {
switch (wrap) {
case WRAP_NONE:
mWrapMarginPix = 0;
mContinuousWrap = 0;
break;
case WRAP_AT_COLUMN:
default:
mWrapMarginPix = int(col_to_x(wrapMargin));
mContinuousWrap = 1;
break;
case WRAP_AT_PIXEL:
mWrapMarginPix = wrapMargin;
mContinuousWrap = 1;
break;
case WRAP_AT_BOUNDS:
mWrapMarginPix = 0;
mContinuousWrap = 1;
break;
}
if (buffer()) {
mNBufferLines = count_lines(0, buffer()->length(), true);
mFirstChar = line_start(mFirstChar);
mTopLineNum = count_lines(0, mFirstChar, true) + 1;
reset_absolute_top_line_number();
calc_line_starts(0, mNVisibleLines);
calc_last_char();
} else {
mNBufferLines = 0;
mFirstChar = 0;
mTopLineNum = 1;
mAbsTopLineNum = 1; }
display_needs_recalc(); }
void Fl_Text_Display::insert(const char* text) {
IS_UTF8_ALIGNED2(buffer(), mCursorPos)
IS_UTF8_ALIGNED(text)
int pos = mCursorPos;
mCursorToHint = (int) (pos + strlen( text ));
mBuffer->insert( pos, text );
mCursorToHint = NO_HINT;
}
void Fl_Text_Display::overstrike(const char* text) {
IS_UTF8_ALIGNED2(buffer(), mCursorPos)
IS_UTF8_ALIGNED(text)
int startPos = mCursorPos;
Fl_Text_Buffer *buf = mBuffer;
int lineStart = buf->line_start( startPos );
int textLen = (int) strlen( text );
int i, p, endPos, indent, startIndent, endIndent;
const char *c;
unsigned int ch;
char *paddedText = NULL;
startIndent = mBuffer->count_displayed_characters( lineStart, startPos );
indent = startIndent;
for ( c = text; *c != '\0'; c += fl_utf8len1(*c) )
indent++;
endIndent = indent;
indent = startIndent;
for ( p = startPos; ; p = buf->next_char(p) ) {
if ( p == buf->length() )
break;
ch = buf->char_at( p );
if ( ch == '\n' )
break;
indent++;
if ( indent == endIndent ) {
p = buf->next_char(p);
break;
} else if ( indent > endIndent ) {
if ( ch != '\t' ) {
p = buf->next_char(p);
paddedText = new char [ textLen + FL_TEXT_MAX_EXP_CHAR_LEN + 1 ];
strcpy( paddedText, text );
for ( i = 0; i < indent - endIndent; i++ )
paddedText[ textLen + i ] = ' ';
paddedText[ textLen + i ] = '\0';
}
break;
}
}
endPos = p;
mCursorToHint = startPos + textLen;
buf->replace( startPos, endPos, paddedText == NULL ? text : paddedText );
mCursorToHint = NO_HINT;
if ( paddedText != NULL )
delete [] paddedText;
}
int Fl_Text_Display::position_to_xy( int pos, int* X, int* Y ) const {
IS_UTF8_ALIGNED2(buffer(), pos)
int lineStartPos, fontHeight;
int visLineNum;
if ((pos < mFirstChar) ||
(pos > mLastChar && !empty_vlines()) ||
(pos > buffer()->length()) ) { return (*X=*Y=0); }
if (!position_to_line(pos, &visLineNum) || visLineNum < 0 || visLineNum > mNBufferLines) {
return (*X=*Y=0); }
fontHeight = mMaxsize;
*Y = text_area.y + visLineNum * fontHeight;
lineStartPos = mLineStarts[visLineNum];
if ( lineStartPos == -1 ) {
*X = text_area.x - mHorizOffset;
return 1;
}
*X = text_area.x + handle_vline(GET_WIDTH, lineStartPos, pos-lineStartPos, 0, 0, 0, 0, 0, 0) - mHorizOffset;
return 1;
}
int Fl_Text_Display::position_to_linecol( int pos, int* lineNum, int* column ) const {
IS_UTF8_ALIGNED2(buffer(), pos)
int retVal;
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 ) {
*column = mBuffer->count_displayed_characters( mLineStarts[ *lineNum ], pos );
*lineNum += mTopLineNum;
}
return retVal;
}
int Fl_Text_Display::in_selection( int X, int Y ) const {
int pos = xy_to_position( X, Y, CHARACTER_POS );
IS_UTF8_ALIGNED2(buffer(), pos)
Fl_Text_Buffer *buf = mBuffer;
return buf->primary_selection()->includes(pos);
}
int Fl_Text_Display::wrapped_column(int row, int column) const {
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);
}
int Fl_Text_Display::wrapped_row(int row) const {
if (!mContinuousWrap || row < 0 || row > mNVisibleLines)
return row;
return buffer()->count_lines(mFirstChar, mLineStarts[row]);
}
void Fl_Text_Display::display_insert() {
int hOffset, topLine, X, Y;
hOffset = mHorizOffset;
topLine = mTopLineNum;
if (insert_position() < mFirstChar) {
topLine -= count_lines(insert_position(), mFirstChar, false);
} else if (mNVisibleLines>=2 && mLineStarts[mNVisibleLines-2] != -1) {
int lastChar = line_end(mLineStarts[mNVisibleLines-2],true);
if (insert_position() >= lastChar)
topLine += count_lines(lastChar - (wrap_uses_character(mLastChar) ? 0 : 1),
insert_position(), false);
}
if (!position_to_xy( mCursorPos, &X, &Y )) {
scroll_(topLine, hOffset);
if (!position_to_xy( mCursorPos, &X, &Y )) {
#ifdef DEBUG
printf ("*** display_insert/position_to_xy # GIVE UP !\n"); fflush(stdout);
#endif return;
}
}
if (X > text_area.x + text_area.w)
hOffset += X-(text_area.x + text_area.w);
else if (X < text_area.x)
hOffset += X-text_area.x;
if (topLine != mTopLineNum || hOffset != mHorizOffset)
scroll_(topLine, hOffset);
}
void Fl_Text_Display::show_insert_position() {
display_insert_position_hint = 1;
display_needs_recalc(); }
int Fl_Text_Display::move_right() {
if ( mCursorPos >= mBuffer->length() )
return 0;
int p = insert_position();
int q = buffer()->next_char(p);
insert_position(q);
return 1;
}
int Fl_Text_Display::move_left() {
if ( mCursorPos <= 0 )
return 0;
int p = insert_position();
int q = buffer()->prev_char_clipped(p);
insert_position(q);
return 1;
}
int Fl_Text_Display::move_up() {
int lineStartPos, xPos, prevLineStartPos, newPos, visLineNum;
if ( position_to_line( mCursorPos, &visLineNum ) )
lineStartPos = mLineStarts[ visLineNum ];
else {
lineStartPos = line_start( mCursorPos );
visLineNum = -1;
}
if ( lineStartPos == 0 )
return 0;
if (mCursorPreferredXPos >= 0)
xPos = mCursorPreferredXPos;
else
xPos = handle_vline(GET_WIDTH, lineStartPos, mCursorPos-lineStartPos,
0, 0, 0, 0, 0, INT_MAX);
if ( visLineNum != -1 && visLineNum != 0 )
prevLineStartPos = mLineStarts[ visLineNum - 1 ];
else
prevLineStartPos = rewind_lines( lineStartPos, 1 );
int lineEnd = line_end(prevLineStartPos, true);
newPos = handle_vline(FIND_INDEX_FROM_ZERO, prevLineStartPos, lineEnd-prevLineStartPos,
0, 0, 0, 0, 0, xPos);
insert_position( newPos );
mCursorPreferredXPos = xPos;
return 1;
}
int Fl_Text_Display::move_down() {
int lineStartPos, xPos, newPos, visLineNum;
if ( mCursorPos == mBuffer->length() )
return 0;
if ( position_to_line( mCursorPos, &visLineNum ) )
lineStartPos = mLineStarts[ visLineNum ];
else {
lineStartPos = line_start( mCursorPos );
visLineNum = -1;
}
if (mCursorPreferredXPos >= 0) {
xPos = mCursorPreferredXPos;
} else {
xPos = handle_vline(GET_WIDTH, lineStartPos, mCursorPos-lineStartPos,
0, 0, 0, 0, 0, INT_MAX);
}
int nextLineStartPos = skip_lines( lineStartPos, 1, true );
int lineEnd = line_end(nextLineStartPos, true);
newPos = handle_vline(FIND_INDEX_FROM_ZERO, nextLineStartPos, lineEnd-nextLineStartPos,
0, 0, 0, 0, 0, xPos);
insert_position( newPos );
mCursorPreferredXPos = xPos;
return 1;
}
int Fl_Text_Display::count_lines(int startPos, int endPos,
bool startPosIsLineStart) const {
IS_UTF8_ALIGNED2(buffer(), startPos)
IS_UTF8_ALIGNED2(buffer(), endPos)
int retLines, retPos, retLineStart, retLineEnd;
#ifdef DEBUG
printf("Fl_Text_Display::count_lines(startPos=%d, endPos=%d, startPosIsLineStart=%d\n",
startPos, endPos, startPosIsLineStart);
#endif
if (!mContinuousWrap)
return buffer()->count_lines(startPos, endPos);
if (buffer()->length() > 16384) {
int nLines = 0;
int firstVisibleChar = buffer()->rewind_lines(mFirstChar, 3);
int lastVisibleChar = buffer()->skip_lines(mLastChar, 3);
if (mColumnScale==0.0) x_to_col(1.0);
int avgCharsPerLine = mWrapMarginPix;
if (!avgCharsPerLine) avgCharsPerLine = text_area.w;
avgCharsPerLine = (int)(avgCharsPerLine / mColumnScale) + 1;
if (startPos < firstVisibleChar) {
int tmpEnd = endPos<firstVisibleChar ? endPos : firstVisibleChar;
nLines += buffer()->estimate_lines(startPos, tmpEnd, avgCharsPerLine);
startPos = tmpEnd;
}
if (startPos < endPos && startPos < mLastChar) {
int tmpEnd = endPos<lastVisibleChar ? endPos : lastVisibleChar;
wrapped_line_counter(buffer(), startPos, tmpEnd, INT_MAX,
startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
&retLineEnd);
nLines += retLines;
startPos = tmpEnd;
}
if (startPos < endPos && startPos >= lastVisibleChar) {
nLines += buffer()->estimate_lines(startPos, endPos, avgCharsPerLine);
}
return nLines;
} else {
wrapped_line_counter(buffer(), startPos, endPos, INT_MAX,
startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
&retLineEnd);
#ifdef DEBUG
printf(" # after WLC: retPos=%d, retLines=%d, retLineStart=%d, retLineEnd=%d\n",
retPos, retLines, retLineStart, retLineEnd);
#endif return retLines;
}
}
int Fl_Text_Display::skip_lines(int startPos, int nLines,
bool startPosIsLineStart) {
IS_UTF8_ALIGNED2(buffer(), startPos)
int retLines, retPos, retLineStart, retLineEnd;
if (!mContinuousWrap)
return buffer()->skip_lines(startPos, nLines);
if (nLines == 0)
return startPos;
wrapped_line_counter(buffer(), startPos, buffer()->length(),
nLines, startPosIsLineStart, 0,
&retPos, &retLines, &retLineStart, &retLineEnd);
IS_UTF8_ALIGNED2(buffer(), retPos)
return retPos;
}
int Fl_Text_Display::line_end(int startPos, bool startPosIsLineStart) const {
IS_UTF8_ALIGNED2(buffer(), startPos)
int retLines, retPos, retLineStart, retLineEnd;
if (!mContinuousWrap)
return buffer()->line_end(startPos);
if (startPos == buffer()->length())
return startPos;
wrapped_line_counter(buffer(), startPos, buffer()->length(), 1,
startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
&retLineEnd);
IS_UTF8_ALIGNED2(buffer(), retLineEnd)
return retLineEnd;
}
int Fl_Text_Display::line_start(int pos) const {
IS_UTF8_ALIGNED2(buffer(), pos)
int retLines, retPos, retLineStart, retLineEnd;
if (!mContinuousWrap)
return buffer()->line_start(pos);
wrapped_line_counter(buffer(), buffer()->line_start(pos), pos, INT_MAX, true, 0,
&retPos, &retLines, &retLineStart, &retLineEnd);
IS_UTF8_ALIGNED2(buffer(), retLineStart)
return retLineStart;
}
int Fl_Text_Display::rewind_lines(int startPos, int nLines) {
IS_UTF8_ALIGNED2(buffer(), startPos)
Fl_Text_Buffer *buf = buffer();
int pos, lineStart, retLines, retPos, retLineStart, retLineEnd;
if (!mContinuousWrap)
return buf->rewind_lines(startPos, nLines);
pos = startPos;
for (;;) {
lineStart = buf->line_start(pos);
wrapped_line_counter(buf, lineStart, pos, INT_MAX, true, 0,
&retPos, &retLines, &retLineStart, &retLineEnd, false);
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() && !buffer()->is_word_separator(pos)) {
pos = buffer()->next_char(pos);
}
while (pos < buffer()->length() && buffer()->is_word_separator(pos)) {
pos = buffer()->next_char(pos);
}
insert_position( pos );
}
void Fl_Text_Display::previous_word() {
int pos = insert_position();
if (pos==0) return;
pos = buffer()->prev_char(pos);
while (pos && buffer()->is_word_separator(pos)) {
pos = buffer()->prev_char(pos);
}
while (pos && !buffer()->is_word_separator(pos)) {
pos = buffer()->prev_char(pos);
}
if (buffer()->is_word_separator(pos)) {
pos = buffer()->next_char(pos);
}
insert_position( pos );
}
void Fl_Text_Display::buffer_predelete_cb(int pos, int nDeleted, void *cbArg) {
Fl_Text_Display *textD = (Fl_Text_Display *)cbArg;
if (textD->mContinuousWrap) {
IS_UTF8_ALIGNED2(textD->buffer(), pos)
textD->measure_deleted_lines(pos, nDeleted);
} else {
textD->mSuppressResync = 0;
}
}
void Fl_Text_Display::buffer_modified_cb( int pos, int nInserted, int nDeleted,
int nRestyled, const char *deletedText, void *cbArg ) {
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 = 0, wrapModEnd = 0;
IS_UTF8_ALIGNED2(buf, pos)
IS_UTF8_ALIGNED2(buf, oldFirstChar)
if ( nInserted != 0 || nDeleted != 0 )
textD->mCursorPreferredXPos = -1;
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 );
}
if ( nInserted != 0 || nDeleted != 0 ) {
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 (textD->maintaining_absolute_top_line_number() &&
(nInserted != 0 || nDeleted != 0)) {
if (deletedText && (pos + nDeleted < oldFirstChar))
textD->mAbsTopLineNum += buf->count_lines(pos, pos + nInserted) -
countlines(deletedText);
else if (pos < oldFirstChar)
textD->reset_absolute_top_line_number();
}
textD->mNBufferLines += linesInserted - linesDeleted;
if ( textD->mCursorToHint != NO_HINT ) {
textD->mCursorPos = textD->mCursorToHint;
textD->mCursorToHint = NO_HINT;
} else if ( textD->mCursorPos > pos ) {
if ( textD->mCursorPos < pos + nDeleted )
textD->mCursorPos = pos;
else
textD->mCursorPos += nInserted - nDeleted;
}
textD->display_needs_recalc();
if (!textD->visible_r()) return;
if ( scrolled ) {
textD->damage(FL_DAMAGE_EXPOSE);
if ( textD->mStyleBuffer )
textD->mStyleBuffer->primary_selection()->selected(0);
return;
}
startDispPos = textD->mContinuousWrap ? wrapModStart : pos;
IS_UTF8_ALIGNED2(buf, startDispPos)
if ( origCursorPos == startDispPos && textD->mCursorPos != startDispPos )
startDispPos = min( startDispPos, buf->prev_char_clipped(origCursorPos) );
IS_UTF8_ALIGNED2(buf, startDispPos)
if ( linesInserted == linesDeleted ) {
if ( nInserted == 0 && nDeleted == 0 )
endDispPos = pos + nRestyled;
else {
if (textD->mContinuousWrap)
endDispPos = wrapModEnd;
else
endDispPos = buf->next_char(buf->line_end( pos + nInserted ));
}
if (linesInserted > 1) {
textD->damage(FL_DAMAGE_EXPOSE);
}
} else {
endDispPos = buf->next_char(textD->mLastChar);
}
IS_UTF8_ALIGNED2(buf, startDispPos)
IS_UTF8_ALIGNED2(buf, endDispPos)
if ( textD->mStyleBuffer )
textD->extend_range_for_styles( &startDispPos, &endDispPos );
IS_UTF8_ALIGNED2(buf, startDispPos)
IS_UTF8_ALIGNED2(buf, endDispPos)
textD->redisplay_range( startDispPos, endDispPos );
}
void Fl_Text_Display::maintain_absolute_top_line_number(int state) {
mNeedAbsTopLineNum = state;
reset_absolute_top_line_number();
}
int Fl_Text_Display::get_absolute_top_line_number() const {
if (!mContinuousWrap)
return mTopLineNum;
if (maintaining_absolute_top_line_number())
return mAbsTopLineNum;
return 0;
}
void Fl_Text_Display::absolute_top_line_number(int oldFirstChar) {
if (maintaining_absolute_top_line_number() && buffer()) {
if (mFirstChar < oldFirstChar)
mAbsTopLineNum -= buffer()->count_lines(mFirstChar, oldFirstChar);
else
mAbsTopLineNum += buffer()->count_lines(oldFirstChar, mFirstChar);
}
}
int Fl_Text_Display::maintaining_absolute_top_line_number() const {
return mContinuousWrap &&
(mLineNumWidth != 0 || mNeedAbsTopLineNum);
}
void Fl_Text_Display::reset_absolute_top_line_number() {
mAbsTopLineNum = 1;
absolute_top_line_number(0);
}
int Fl_Text_Display::position_to_line( int pos, int *lineNum ) const {
IS_UTF8_ALIGNED2(buffer(), pos)
int i;
*lineNum = 0;
if ( pos < mFirstChar ) return 0;
if ( pos > mLastChar ) {
if ( empty_vlines() ) {
if ( mLastChar < mBuffer->length() ) {
if ( !position_to_line( mLastChar, lineNum ) ) {
Fl::error("Fl_Text_Display::position_to_line(): Consistency check ptvl failed");
return 0;
}
return ++( *lineNum ) <= mNVisibleLines - 1;
} else {
position_to_line( buffer()->prev_char_clipped(mLastChar), lineNum );
return 1;
}
}
return 0;
}
for ( i = mNVisibleLines - 1; i >= 0; i-- ) {
if ( mLineStarts[ i ] != -1 && pos >= mLineStarts[ i ] ) {
*lineNum = i;
return 1;
}
}
return 0;
}
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
{
IS_UTF8_ALIGNED2(buffer(), lineStartPos)
int i, X, startIndex, startStyle, style, charStyle;
char *lineStr;
double startX, styleX;
if ( lineStartPos == -1 ) {
lineStr = NULL;
} else {
lineStr = mBuffer->text_range( lineStartPos, lineStartPos + lineLen );
}
int cursor_pos = 0;
if (mode==FIND_CURSOR_INDEX) {
mode = FIND_INDEX;
cursor_pos = 1;
}
if (mode==GET_WIDTH) {
X = 0;
} else if (mode==FIND_INDEX_FROM_ZERO) {
X = 0;
mode = FIND_INDEX;
} else {
X = text_area.x - mHorizOffset;
}
for (int loop=1; loop<=2; loop++) {
int mask = (loop==1) ? BG_ONLY_MASK : TEXT_ONLY_MASK;
startX = X;
startIndex = 0;
if (!lineStr) {
if (mode==DRAW_LINE) {
style = position_style(lineStartPos, lineLen, -1);
if (loop==1)
draw_string( style|BG_ONLY_MASK, text_area.x, Y, text_area.x+text_area.w, lineStr, lineLen );
}
if (mode==FIND_INDEX) {
IS_UTF8_ALIGNED2(buffer(), lineStartPos)
return lineStartPos;
}
return 0;
}
char currChar = 0, prevChar = 0;
styleX = startX; startStyle = startIndex;
style = position_style(lineStartPos, lineLen, 0);
for (i=0; i<lineLen; ) {
currChar = lineStr[i]; int len = fl_utf8len1(currChar);
if (len<=0) len = 1; charStyle = position_style(lineStartPos, lineLen, i);
if (charStyle!=style || currChar=='\t' || prevChar=='\t') {
double w = 0;
if (prevChar=='\t') {
double tab = col_to_x(mBuffer->tab_distance());
double xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x;
w = ((int(xAbs/tab)+1)*tab) - xAbs;
styleX = startX+w; startStyle = i;
if (mode==DRAW_LINE && loop==1)
draw_string( style|BG_ONLY_MASK, int(startX), Y, int(startX+w), 0, 0 );
if (mode==FIND_INDEX && startX+w>rightClip) {
free(lineStr);
if (cursor_pos && (startX+w/2<rightClip)) return lineStartPos + startIndex + 1; return lineStartPos + startIndex;
}
} else {
if ( (style&0xff)==(charStyle&0xff)) {
w = string_width( lineStr+startStyle, i-startStyle, style ) - startX + styleX;
} else {
w = string_width( lineStr+startIndex, i-startIndex, style );
}
if (mode==DRAW_LINE) {
if (startIndex!=startStyle) {
fl_push_clip(int(startX), Y, int(w)+1, mMaxsize);
draw_string( style|mask, int(styleX), Y, int(startX+w), lineStr+startStyle, i-startStyle );
fl_pop_clip();
} else {
draw_string( style|mask, int(startX), Y, int(startX+w), lineStr+startIndex, i-startIndex );
}
}
if (mode==FIND_INDEX && startX+w>rightClip) {
int di;
if (startIndex!=startStyle) {
di = find_x(lineStr+startStyle, i-startStyle, style, -int(rightClip-styleX)); di = lineStartPos + startStyle + di;
} else {
di = find_x(lineStr+startIndex, i-startIndex, style, -int(rightClip-startX)); di = lineStartPos + startIndex + di;
}
free(lineStr);
IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di))
return di;
}
if ( (style&0xff)!=(charStyle&0xff)) {
startStyle = i;
styleX = startX+w;
}
}
style = charStyle;
startX += w;
startIndex = i;
}
i += len;
prevChar = currChar;
}
double w = 0;
if (currChar=='\t') {
double tab = col_to_x(mBuffer->tab_distance());
double xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x;
w = ((int(xAbs/tab)+1)*tab) - xAbs;
if (mode==DRAW_LINE && loop==1)
draw_string( style|BG_ONLY_MASK, int(startX), Y, int(startX+w), 0, 0 );
if (mode==FIND_INDEX) {
free(lineStr);
if (cursor_pos) return lineStartPos + startIndex + ( rightClip-startX>w/2 ? 1 : 0 ); return lineStartPos + startIndex + ( rightClip-startX>w ? 1 : 0 );
}
} else {
w = string_width( lineStr+startIndex, i-startIndex, style );
if (mode==DRAW_LINE) {
if (startIndex!=startStyle) {
fl_push_clip(int(startX), Y, int(w)+1, mMaxsize);
draw_string( style|mask, int(styleX), Y, int(startX+w), lineStr+startStyle, i-startStyle );
fl_pop_clip();
} else {
draw_string( style|mask, int(startX), Y, int(startX+w), lineStr+startIndex, i-startIndex );
}
}
if (mode==FIND_INDEX) {
int di;
if (startIndex!=startStyle) {
di = find_x(lineStr+startStyle, i-startStyle, style, -int(rightClip-styleX)); di = lineStartPos + startStyle + di;
} else {
di = find_x(lineStr+startIndex, i-startIndex, style, -int(rightClip-startX)); di = lineStartPos + startIndex + di;
}
free(lineStr);
IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di))
return di;
}
}
if (mode==GET_WIDTH) {
free(lineStr);
return int(startX+w);
}
startX += w;
style = position_style(lineStartPos, lineLen, i);
if (mode==DRAW_LINE && loop==1)
draw_string( style|BG_ONLY_MASK, int(startX), Y, text_area.x+text_area.w, lineStr, lineLen );
}
free(lineStr);
IS_UTF8_ALIGNED2(buffer(), (lineStartPos+lineLen))
return lineStartPos + lineLen;
}
int Fl_Text_Display::find_x(const char *s, int len, int style, int x) const {
IS_UTF8_ALIGNED(s)
int cursor_pos = x<0; x = x<0 ? -x : x;
int i = 0;
int last_w = 0; while (i<len) {
int cl = fl_utf8len1(s[i]);
int w = int( string_width(s, i+cl, style) );
if (w>x) {
if (cursor_pos && (w-x < x-last_w)) return i+cl; return i;
}
last_w = w; i += cl;
}
return len;
}
void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip,
int leftCharIndex, int rightCharIndex) {
int Y, lineStartPos, lineLen, fontHeight;
if ( visLineNum < 0 || visLineNum >= mNVisibleLines )
return;
fontHeight = mMaxsize;
Y = text_area.y + visLineNum * fontHeight;
lineStartPos = mLineStarts[ visLineNum ];
if ( lineStartPos == -1 ) {
lineLen = 0;
} else {
lineLen = vline_length( visLineNum );
}
leftClip = max( text_area.x, leftClip );
rightClip = min( rightClip, text_area.x + text_area.w );
handle_vline(DRAW_LINE,
lineStartPos, lineLen, leftCharIndex, rightCharIndex,
Y, Y+fontHeight, leftClip, rightClip);
return;
}
void Fl_Text_Display::draw_string(int style,
int X, int Y, int toX,
const char *string, int nChars) const {
IS_UTF8_ALIGNED(string)
const Style_Table_Entry *styleRec = NULL;
if ( style & FILL_MASK ) {
if (style & TEXT_ONLY_MASK) return;
clear_rect( style, X, Y, toX - X, mMaxsize );
return;
}
Fl_Font font = textfont();
int fsize = textsize();
Fl_Color foreground;
Fl_Color background;
Fl_Color bgbasecolor;
if ( style & STYLE_LOOKUP_MASK ) {
int si = (style & STYLE_LOOKUP_MASK) - 'A';
if (si < 0) si = 0;
else if (si >= mNStyles) si = mNStyles - 1;
styleRec = mStyleTable + si;
font = styleRec->font;
fsize = styleRec->size;
bgbasecolor = (styleRec->attr&ATTR_BGCOLOR) ? styleRec->bgcolor : color();
if (style & PRIMARY_MASK) {
if (Fl::focus() == (Fl_Widget*)this) {
if (Fl::screen_driver()->has_marked_text() && Fl::compose_state) {
background = bgbasecolor; } else {
background = selection_color();
}
} else {
background = fl_color_average(bgbasecolor, selection_color(), 0.4f);
}
} else if (style & HIGHLIGHT_MASK) {
if (Fl::focus() == (Fl_Widget*)this) {
background = fl_color_average(bgbasecolor, selection_color(), 0.5f);
} else {
background = fl_color_average(bgbasecolor, selection_color(), 0.6f);
}
} else if (style & SECONDARY_MASK) {
if (Fl::focus() == (Fl_Widget*)this) {
background = fl_color_average(bgbasecolor, secondary_selection_color(), 0.5f);
} else {
background = fl_color_average(bgbasecolor, secondary_selection_color(), 0.6f);
}
} else {
background = bgbasecolor;
}
foreground = (style & PRIMARY_MASK) ? fl_contrast(styleRec->color, background) : styleRec->color;
} else if (style & PRIMARY_MASK) {
if (Fl::focus() == (Fl_Widget*)this) {
background = selection_color();
} else {
background = fl_color_average(color(), selection_color(), 0.4f);
}
foreground = fl_contrast(textcolor(), background);
} else if (style & HIGHLIGHT_MASK) {
if (Fl::focus() == (Fl_Widget*)this) {
background = fl_color_average(color(), selection_color(), 0.5f);
} else {
background = fl_color_average(color(), selection_color(), 0.6f);
}
foreground = fl_contrast(textcolor(), background);
} else if (style & SECONDARY_MASK) {
if (Fl::focus() == (Fl_Widget*)this) {
background = secondary_selection_color();
} else {
background = fl_color_average(color(), secondary_selection_color(), 0.4f);
}
foreground = fl_contrast(textcolor(), background);
} else {
foreground = textcolor();
background = color();
}
if ( !active_r() ) {
foreground = fl_inactive(foreground);
background = fl_inactive(background);
}
if (!(style & TEXT_ONLY_MASK)) {
fl_color( background );
fl_rectf( X, Y, toX - X, mMaxsize );
}
if (!(style & BG_ONLY_MASK)) {
fl_color( foreground );
fl_font( font, fsize );
int baseline = Y + mMaxsize - fl_descent();
static int can_leak = Fl::screen_driver()->text_display_can_leak();
if (can_leak) fl_push_clip(x(), Y, w(), mMaxsize);
fl_draw( string, nChars, X, baseline);
if (styleRec) {
if (styleRec->attr & ATTR_LINES_MASK) {
int pitch = fsize/7;
int prevAA = fl_antialias();
fl_antialias(1);
switch (styleRec->attr & ATTR_LINES_MASK) {
case ATTR_UNDERLINE:
fl_color(foreground);
fl_line_style(FL_SOLID, pitch);
goto DRAW_UNDERLINE;
break;
case ATTR_GRAMMAR:
fl_color(grammar_underline_color());
goto DRAW_DOTTED_UNDERLINE;
case ATTR_SPELLING:
fl_color(spelling_underline_color());
DRAW_DOTTED_UNDERLINE:
fl_line_style(FL_DOT, pitch);
DRAW_UNDERLINE:
fl_xyline(X, baseline + fl_descent()/2, toX);
break;
case ATTR_STRIKE_THROUGH:
fl_color(foreground);
fl_line_style(FL_SOLID, pitch);
fl_xyline(X, baseline - (fl_height()-fl_descent())/3, toX);
break;
}
fl_line_style(FL_SOLID, 1);
fl_antialias(prevAA);
}
}
if (Fl::screen_driver()->has_marked_text() && Fl::compose_state && (style & PRIMARY_MASK)) {
fl_color( fl_color_average(foreground, background, 0.6f) );
fl_line(X, Y + mMaxsize - 1, X + (int)fl_width(string, nChars), Y + mMaxsize - 1);
}
if (can_leak) fl_pop_clip();
}
}
void Fl_Text_Display::clear_rect(int style,
int X, int Y,
int width, int height) const {
if ( width == 0 )
return;
Fl_Color bgbasecolor = color();
if ( style & STYLE_LOOKUP_MASK ) {
int si = (style & STYLE_LOOKUP_MASK) - 'A';
if (si < 0) si = 0;
else if (si >= mNStyles) si = mNStyles - 1;
const Style_Table_Entry *styleRec = mStyleTable + si;
if (styleRec->attr&ATTR_BGCOLOR_EXT_)
bgbasecolor = styleRec->bgcolor;
}
Fl_Color c;
if (style & PRIMARY_MASK) {
if (Fl::focus()==(Fl_Widget*)this) {
c = selection_color();
} else {
c = fl_color_average(bgbasecolor, selection_color(), 0.4f);
}
} else if (style & HIGHLIGHT_MASK) {
if (Fl::focus()==(Fl_Widget*)this) {
c = fl_color_average(bgbasecolor, selection_color(), 0.5f);
} else {
c = fl_color_average(bgbasecolor, selection_color(), 0.6f);
}
} else {
c = bgbasecolor;
}
fl_color(active_r() ? c : fl_inactive(c));
fl_rectf( X, Y, width, height );
}
void Fl_Text_Display::draw_cursor( int X, int Y ) {
typedef struct {
int x1, y1, x2, y2;
}
Segment;
Segment segs[ 5 ];
int left, right, cursorWidth, midY;
int fontWidth = TMPFONTWIDTH; int nSegs = 0;
int fontHeight = mMaxsize;
int bot = Y + fontHeight - 1;
if ( X < text_area.x - 1 || X > text_area.x + text_area.w )
return;
cursorWidth = 4; left = X - cursorWidth / 2;
right = left + cursorWidth;
if ( mCursorStyle == CARET_CURSOR ) {
midY = bot - fontHeight / 5;
segs[ 0 ].x1 = left; segs[ 0 ].y1 = bot; segs[ 0 ].x2 = X; segs[ 0 ].y2 = midY;
segs[ 1 ].x1 = X; segs[ 1 ].y1 = midY; segs[ 1 ].x2 = right; segs[ 1 ].y2 = bot;
segs[ 2 ].x1 = left; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = midY - 1;
segs[ 3 ].x1 = X; segs[ 3 ].y1 = midY - 1; segs[ 3 ].x2 = right; segs[ 3 ].y2 = bot;
nSegs = 4;
} else if ( mCursorStyle == NORMAL_CURSOR ) {
segs[ 0 ].x1 = left; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = right; segs[ 0 ].y2 = Y;
segs[ 1 ].x1 = X; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = X; segs[ 1 ].y2 = bot;
segs[ 2 ].x1 = left; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = right; segs[ 2 ].y2 = bot;
nSegs = 3;
} else if ( mCursorStyle == HEAVY_CURSOR ) {
segs[ 0 ].x1 = X - 1; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = X - 1; segs[ 0 ].y2 = bot;
segs[ 1 ].x1 = X; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = X; segs[ 1 ].y2 = bot;
segs[ 2 ].x1 = X + 1; segs[ 2 ].y1 = Y; segs[ 2 ].x2 = X + 1; segs[ 2 ].y2 = bot;
segs[ 3 ].x1 = left; segs[ 3 ].y1 = Y; segs[ 3 ].x2 = right; segs[ 3 ].y2 = Y;
segs[ 4 ].x1 = left; segs[ 4 ].y1 = bot; segs[ 4 ].x2 = right; segs[ 4 ].y2 = bot;
nSegs = 5;
} else if ( mCursorStyle == DIM_CURSOR ) {
midY = Y + fontHeight / 2;
segs[ 0 ].x1 = X; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = X; segs[ 0 ].y2 = Y;
segs[ 1 ].x1 = X; segs[ 1 ].y1 = midY; segs[ 1 ].x2 = X; segs[ 1 ].y2 = midY;
segs[ 2 ].x1 = X; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = bot;
nSegs = 3;
} else if ( mCursorStyle == BLOCK_CURSOR ) {
right = X + fontWidth;
segs[ 0 ].x1 = X; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = right; segs[ 0 ].y2 = Y;
segs[ 1 ].x1 = right; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = right; segs[ 1 ].y2 = bot;
segs[ 2 ].x1 = right; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = bot;
segs[ 3 ].x1 = X; segs[ 3 ].y1 = bot; segs[ 3 ].x2 = X; segs[ 3 ].y2 = Y;
nSegs = 4;
} else if ( mCursorStyle == SIMPLE_CURSOR ){
segs[ 0 ].x1 = X; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = X; segs[ 0 ].y2 = bot;
segs[ 1 ].x1 = X+1; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = X+1; segs[ 1 ].y2 = bot;
nSegs = 2;
}
fl_color( mCursor_color );
for ( int k = 0; k < nSegs; k++ ) {
fl_line( segs[ k ].x1, segs[ k ].y1, segs[ k ].x2, segs[ k ].y2 );
}
if (Fl::focus() == this) {
fl_set_spot(textfont(), textsize(), X, bot, text_area.w, text_area.h, window());
}
}
int Fl_Text_Display::position_style( int lineStartPos, int lineLen, int lineIndex) const
{
IS_UTF8_ALIGNED2(buffer(), lineStartPos)
Fl_Text_Buffer * buf = mBuffer;
Fl_Text_Buffer *styleBuf = mStyleBuffer;
int pos, style = 0;
if ( lineStartPos == -1 || buf == NULL )
return FILL_MASK;
pos = lineStartPos + min( lineIndex, lineLen );
if ( styleBuf && lineIndex==lineLen && lineLen>0) {
style = ( unsigned char ) styleBuf->byte_at( pos-1 );
if (style == mUnfinishedStyle && mUnfinishedHighlightCB) {
(mUnfinishedHighlightCB)( pos, mHighlightCBArg);
style = (unsigned char) styleBuf->byte_at( pos);
}
int si = (style & STYLE_LOOKUP_MASK) - 'A';
if (si < 0) si = 0;
else if (si >= mNStyles) si = mNStyles - 1;
const Style_Table_Entry *styleRec = mStyleTable + si;
if ((styleRec->attr&ATTR_BGCOLOR_EXT_)==0)
style = FILL_MASK;
} else if ( lineIndex >= lineLen ) {
style = FILL_MASK;
} else if ( styleBuf != NULL ) {
style = ( unsigned char ) styleBuf->byte_at( pos );
if (style == mUnfinishedStyle && mUnfinishedHighlightCB) {
(mUnfinishedHighlightCB)( pos, mHighlightCBArg);
style = (unsigned char) styleBuf->byte_at( pos);
}
}
if (buf->primary_selection()->includes(pos))
style |= PRIMARY_MASK;
if (buf->highlight_selection()->includes(pos))
style |= HIGHLIGHT_MASK;
if (buf->secondary_selection()->includes(pos))
style |= SECONDARY_MASK;
return style;
}
double Fl_Text_Display::string_width( const char *string, int length, int style ) const {
IS_UTF8_ALIGNED(string)
Fl_Font font;
Fl_Fontsize fsize;
if ( mNStyles && (style & STYLE_LOOKUP_MASK) ) {
int si = (style & STYLE_LOOKUP_MASK) - 'A';
if (si < 0) si = 0;
else if (si >= mNStyles) si = mNStyles - 1;
font = mStyleTable[si].font;
fsize = mStyleTable[si].size;
} else {
font = textfont();
fsize = textsize();
}
fl_font( font, fsize );
return fl_width( string, length );
}
int Fl_Text_Display::xy_to_position( int X, int Y, int posType ) const {
int lineStart, lineLen, fontHeight;
int visLineNum;
fontHeight = mMaxsize;
visLineNum = ( Y - text_area.y ) / fontHeight;
if ( visLineNum < 0 )
return mFirstChar;
if ( visLineNum >= mNVisibleLines )
visLineNum = mNVisibleLines - 1;
lineStart = mLineStarts[ visLineNum ];
if ( lineStart == -1 )
return mBuffer->length();
lineLen = vline_length( visLineNum );
int mode = (posType == CURSOR_POS) ? FIND_CURSOR_INDEX : FIND_INDEX; return handle_vline(mode,
lineStart, lineLen, 0, 0,
0, 0,
text_area.x, X);
}
void Fl_Text_Display::xy_to_rowcol( int X, int Y, int *row,
int *column, int posType ) const {
int fontHeight = mMaxsize;
int fontWidth = TMPFONTWIDTH;
*row = ( Y - text_area.y ) / fontHeight;
if ( *row < 0 ) *row = 0;
if ( *row >= mNVisibleLines ) *row = mNVisibleLines - 1;
*column = ( ( X - text_area.x ) + mHorizOffset +
( posType == CURSOR_POS ? fontWidth / 2 : 0 ) ) / fontWidth;
if ( *column < 0 ) * column = 0;
}
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;
int i, lastLineNum;
Fl_Text_Buffer *buf = mBuffer;
if ( lineDelta == 0 )
return;
lastLineNum = oldTopLineNum + nVisLines - 1;
if ( newTopLineNum < oldTopLineNum && newTopLineNum < -lineDelta ) {
mFirstChar = skip_lines( 0, newTopLineNum - 1, true );
} else if ( newTopLineNum < oldTopLineNum ) {
mFirstChar = rewind_lines( mFirstChar, -lineDelta );
} else if ( newTopLineNum < lastLineNum ) {
mFirstChar = lineStarts[ newTopLineNum - oldTopLineNum ];
} else if ( newTopLineNum - lastLineNum < mNBufferLines - newTopLineNum ) {
mFirstChar = skip_lines( lineStarts[ nVisLines - 1 ],
newTopLineNum - lastLineNum, true );
} else {
mFirstChar = rewind_lines( buf->length(), mNBufferLines - newTopLineNum + 1 );
}
if ( lineDelta < 0 && -lineDelta < nVisLines ) {
for ( i = nVisLines - 1; i >= -lineDelta; i-- )
lineStarts[ i ] = lineStarts[ i + lineDelta ];
calc_line_starts( 0, -lineDelta );
} else if ( lineDelta > 0 && lineDelta < nVisLines ) {
for ( i = 0; i < nVisLines - lineDelta; i++ )
lineStarts[ i ] = lineStarts[ i + lineDelta ];
calc_line_starts( nVisLines - lineDelta, nVisLines - 1 );
} else
calc_line_starts( 0, nVisLines );
calc_last_char();
mTopLineNum = newTopLineNum;
absolute_top_line_number(oldFirstChar);
}
void Fl_Text_Display::update_line_starts(int pos, int charsInserted,
int charsDeleted, int linesInserted,
int linesDeleted, int *scrolled ) {
IS_UTF8_ALIGNED2(buffer(), pos)
int *lineStarts = mLineStarts;
int i, lineOfPos, lineOfEnd, nVisLines = mNVisibleLines;
int charDelta = charsInserted - charsDeleted;
int lineDelta = linesInserted - linesDeleted;
if ( pos + charsDeleted < mFirstChar ) {
mTopLineNum += lineDelta;
for ( i = 0; i < nVisLines && lineStarts[i] != -1; i++ )
lineStarts[ i ] += charDelta;
mFirstChar += charDelta;
mLastChar += charDelta;
*scrolled = 0;
return;
}
if ( pos < mFirstChar ) {
if ( position_to_line( pos + charsDeleted, &lineOfEnd ) &&
++lineOfEnd < nVisLines && lineStarts[ lineOfEnd ] != -1 ) {
mTopLineNum = max( 1, mTopLineNum + lineDelta );
mFirstChar = rewind_lines(lineStarts[ lineOfEnd ] + charDelta, lineOfEnd );
} else {
if ( mTopLineNum > mNBufferLines + lineDelta ) {
mTopLineNum = 1;
mFirstChar = 0;
} else
mFirstChar = skip_lines( 0, mTopLineNum - 1, true );
}
calc_line_starts( 0, nVisLines - 1 );
calc_last_char();
*scrolled = 1;
return;
}
if ( pos <= mLastChar ) {
position_to_line( pos, &lineOfPos );
if ( lineDelta == 0 ) {
for ( i = lineOfPos + 1; i < nVisLines && lineStarts[ i ] != -1; i++ )
lineStarts[ i ] += charDelta;
} else if ( lineDelta > 0 ) {
for ( i = nVisLines - 1; i >= lineOfPos + lineDelta + 1; i-- )
lineStarts[ i ] = lineStarts[ i - lineDelta ] +
( lineStarts[ i - lineDelta ] == -1 ? 0 : charDelta );
} else {
for ( i = max( 0, lineOfPos + 1 ); i < nVisLines + lineDelta; i++ )
lineStarts[ i ] = lineStarts[ i - lineDelta ] +
( lineStarts[ i - lineDelta ] == -1 ? 0 : charDelta );
}
if ( linesInserted >= 0 )
calc_line_starts( lineOfPos + 1, lineOfPos + linesInserted );
if ( lineDelta < 0 )
calc_line_starts( nVisLines + lineDelta, nVisLines );
calc_last_char();
*scrolled = 0;
return;
}
if ( empty_vlines() ) {
position_to_line( pos, &lineOfPos );
calc_line_starts( lineOfPos, lineOfPos + linesInserted );
calc_last_char();
*scrolled = 0;
return;
}
*scrolled = 0;
}
void Fl_Text_Display::calc_line_starts( int startLine, int endLine ) {
int startPos, bufLen = mBuffer->length();
int line, lineEnd, nextLineStart, nVis = mNVisibleLines;
int *lineStarts = mLineStarts;
if ( endLine < 0 ) endLine = 0;
if ( endLine >= nVis ) endLine = nVis - 1;
if ( startLine < 0 ) startLine = 0;
if ( startLine >= nVis ) startLine = nVis - 1;
if ( startLine > endLine )
return;
if ( startLine == 0 ) {
lineStarts[ 0 ] = mFirstChar;
startLine = 1;
}
startPos = lineStarts[ startLine - 1 ];
if ( startPos == -1 ) {
for ( line = startLine; line <= endLine; line++ )
lineStarts[ line ] = -1;
return;
}
for ( line = startLine; line <= endLine; line++ ) {
find_line_end(startPos, true, &lineEnd, &nextLineStart);
startPos = nextLineStart;
if ( startPos >= bufLen ) {
if ( line == 0 || ( lineStarts[ line - 1 ] != bufLen &&
lineEnd != nextLineStart ) ) {
lineStarts[ line ] = bufLen;
line++;
}
break;
}
lineStarts[ line ] = startPos;
}
for ( ; line <= endLine; line++ )
lineStarts[ line ] = -1;
}
void Fl_Text_Display::calc_last_char() {
int i;
for (i = mNVisibleLines - 1; i >= 0 && mLineStarts[i] == -1; i--) ;
mLastChar = i < 0 ? 0 : line_end(mLineStarts[i], true);
}
void Fl_Text_Display::scroll(int topLineNum, int horizOffset) {
mTopLineNumHint = topLineNum;
mHorizOffsetHint = horizOffset;
display_needs_recalc(); }
int Fl_Text_Display::scroll_(int topLineNum, int horizOffset) {
if (topLineNum > mNBufferLines + 3 - mNVisibleLines)
topLineNum = mNBufferLines + 3 - mNVisibleLines;
if (topLineNum < 1) topLineNum = 1;
if (horizOffset > longest_vline() - text_area.w)
horizOffset = longest_vline() - text_area.w;
if (horizOffset < 0) horizOffset = 0;
if (mHorizOffset == horizOffset && mTopLineNum == topLineNum)
return 0;
offset_line_starts(topLineNum);
mHorizOffset = horizOffset;
damage(FL_DAMAGE_EXPOSE);
return 1;
}
void Fl_Text_Display::update_v_scrollbar() {
#ifdef DEBUG
printf("Fl_Text_Display::update_v_scrollbar():\n"
" mTopLineNum=%d, mNVisibleLines=%d, mNBufferLines=%d\n",
mTopLineNum, mNVisibleLines, mNBufferLines);
#endif
mVScrollBar->value(mTopLineNum, mNVisibleLines, 1, mNBufferLines+1+
((mContinuousWrap && mWrapMarginPix) ? 0 : 1));
mVScrollBar->linesize(3);
}
void Fl_Text_Display::update_h_scrollbar() {
int sliderMax = max(longest_vline(), text_area.w + mHorizOffset);
mHScrollBar->value( mHorizOffset, text_area.w, 0, sliderMax );
}
void Fl_Text_Display::v_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) {
if (b->value() == textD->mTopLineNum) return;
textD->scroll(b->value(), textD->mHorizOffset);
}
void Fl_Text_Display::h_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) {
if (b->value() == textD->mHorizOffset) return;
textD->scroll(textD->mTopLineNum, b->value());
}
void Fl_Text_Display::draw_line_numbers(bool ) {
int Y, line, visLine, lineStart;
char lineNumString[16];
int lineHeight = mMaxsize;
int isactive = active_r() ? 1 : 0;
if (mLineNumWidth <= 0 || !visible_r())
return;
int hscroll_h = mHScrollBar->visible() ? mHScrollBar->h() : 0;
int xoff = Fl::box_dx(box());
int yoff = text_area.y - y();
#ifndef LINENUM_LEFT_OF_VSCROLL
int vscroll_w = mVScrollBar->visible() ? mVScrollBar->w() : 0;
if (scrollbar_align() & FL_ALIGN_LEFT)
xoff += vscroll_w;
#endif
Fl_Color fgcolor = isactive ? linenumber_fgcolor() : fl_inactive(linenumber_fgcolor());
Fl_Color bgcolor = isactive ? linenumber_bgcolor() : fl_inactive(linenumber_bgcolor());
fl_push_clip(x() + xoff,
y() + Fl::box_dy(box()),
mLineNumWidth,
h() - Fl::box_dh(box()));
{
fl_color(bgcolor);
fl_rectf(x()+xoff, y(), mLineNumWidth, h());
fl_font(linenumber_font(), linenumber_size());
Y = y() + yoff;
line = get_absolute_top_line_number();
fl_color(fgcolor);
for (visLine=0; visLine < mNVisibleLines; visLine++) {
lineStart = mLineStarts[visLine];
if (lineStart != -1 && (lineStart==0 || buffer()->char_at(lineStart-1)=='\n')) {
snprintf(lineNumString, sizeof(lineNumString),
linenumber_format(), line);
int xx = x() + xoff + 3,
yy = Y,
ww = mLineNumWidth - (3*2),
hh = lineHeight;
fl_draw(lineNumString, xx, yy, ww, hh, linenumber_align(), 0, 0);
line++;
} else {
if (visLine == 0) line++;
}
Y += lineHeight;
}
}
fl_color(FL_BACKGROUND_COLOR);
if (scrollbar_align() & FL_ALIGN_TOP)
fl_rectf(x() + xoff, y() + Fl::box_dy(box()), mLineNumWidth, hscroll_h);
else
fl_rectf(x() + xoff, y() + h() - hscroll_h - Fl::box_dy(box()), mLineNumWidth, hscroll_h + Fl::box_dy(box()));
fl_pop_clip();
}
static int max( int i1, int i2 ) {
return i1 >= i2 ? i1 : i2;
}
static int min( int i1, int i2 ) {
return i1 <= i2 ? i1 : i2;
}
static int countlines( const char *string ) {
IS_UTF8_ALIGNED(string)
const char * c;
int lineCount = 0;
if (!string) return 0;
for ( c = string; *c != '\0'; c++ )
if ( *c == '\n' ) lineCount++;
return lineCount;
}
int Fl_Text_Display::measure_vline( int visLineNum ) const {
int lineLen = vline_length( visLineNum );
int lineStartPos = mLineStarts[ visLineNum ];
if (lineStartPos < 0 || lineLen == 0) return 0;
return handle_vline(GET_WIDTH, lineStartPos, lineLen, 0, 0, 0, 0, 0, 0);
}
int Fl_Text_Display::empty_vlines() const {
return (mNVisibleLines > 0) && (mLineStarts[ mNVisibleLines - 1 ] == -1);
}
int Fl_Text_Display::vline_length( int visLineNum ) const {
int nextLineStart, lineStartPos;
if (visLineNum < 0 || visLineNum >= mNVisibleLines)
return (0);
lineStartPos = mLineStarts[ visLineNum ];
if ( lineStartPos == -1 )
return 0;
if ( visLineNum + 1 >= mNVisibleLines )
return mLastChar - lineStartPos;
nextLineStart = mLineStarts[ visLineNum + 1 ];
if ( nextLineStart == -1 )
return mLastChar - lineStartPos;
int nextLineStartMinus1 = buffer()->prev_char(nextLineStart);
if (wrap_uses_character(nextLineStartMinus1))
return nextLineStartMinus1 - lineStartPos;
return nextLineStart - lineStartPos;
}
void Fl_Text_Display::find_wrap_range(const char *deletedText, int pos,
int nInserted, int nDeleted,
int *modRangeStart, int *modRangeEnd,
int *linesInserted, int *linesDeleted) {
IS_UTF8_ALIGNED(deletedText)
IS_UTF8_ALIGNED2(buffer(), pos)
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;
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);
}
IS_UTF8_ALIGNED2(buf, countFrom)
lineStart = countFrom;
*modRangeStart = countFrom;
for (;;) {
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->char_at(buf->prev_char(lineStart)) == '\n') {
countTo = lineStart;
*modRangeEnd = lineStart;
break;
}
if (mSuppressResync)
continue;
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, buf->prev_char(lineStarts[visLineNum+1]));
else
*modRangeStart = countFrom;
} else
*modRangeStart = min(*modRangeStart, buf->prev_char(lineStart));
}
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;
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);
wrapped_line_counter(deletedTextBuf, 0, length, INT_MAX, true, countFrom,
&retPos, &retLines, &retLineStart, &retLineEnd, false);
delete deletedTextBuf;
*linesDeleted = retLines;
mSuppressResync = 0;
}
void Fl_Text_Display::measure_deleted_lines(int pos, int nDeleted) {
IS_UTF8_ALIGNED2(buffer(), pos)
int retPos, retLines, retLineStart, retLineEnd;
Fl_Text_Buffer *buf = buffer();
int nVisLines = mNVisibleLines;
int *lineStarts = mLineStarts;
int countFrom, lineStart;
int nLines = 0, i;
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];
} else
countFrom = buf->line_start(pos);
} else
countFrom = buf->line_start(pos);
lineStart = countFrom;
for (;;) {
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->char_at(lineStart-1) == '\n') {
break;
}
}
mNLinesDeleted = nLines;
mSuppressResync = 1;
}
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) const {
IS_UTF8_ALIGNED2(buf, startPos)
IS_UTF8_ALIGNED2(buf, maxPos)
int lineStart, newLineStart = 0, b, p, colNum, wrapMarginPix;
int i, foundBreak;
double width;
int nLines = 0;
unsigned int c;
if (mWrapMarginPix != 0) {
wrapMarginPix = mWrapMarginPix;
} else {
wrapMarginPix = text_area.w;
}
if (startPosIsLineStart)
lineStart = startPos;
else
lineStart = line_start(startPos);
colNum = 0;
width = 0;
for (p=lineStart; p<buf->length(); p=buf->next_char(p)) {
c = buf->char_at(p);
if (c == '\n') {
if (p >= maxPos) {
*retPos = maxPos;
*retLines = nLines;
*retLineStart = lineStart;
*retLineEnd = maxPos;
return;
}
nLines++;
int p1 = buf->next_char(p);
if (nLines >= maxLines) {
*retPos = p1;
*retLines = nLines;
*retLineStart = p1;
*retLineEnd = p;
return;
}
lineStart = p1;
colNum = 0;
width = 0;
} else {
const char *s = buf->address(p);
colNum++;
width += measure_proportional_character(s, (int)width, p+styleBufOffset);
}
if (width > wrapMarginPix) {
foundBreak = false;
for (b=p; b>=lineStart; b=buf->prev_char(b)) {
c = buf->char_at(b);
if (c == '\t' || c == ' ') {
newLineStart = buf->next_char(b);
colNum = 0;
width = 0;
int iMax = buf->next_char(p);
for (i=buf->next_char(b); i<iMax; i = buf->next_char(i)) {
width += measure_proportional_character(buf->address(i), (int)width,
i+styleBufOffset);
colNum++;
}
foundBreak = true;
break;
}
}
if (b<lineStart) b = lineStart;
if (!foundBreak) {
newLineStart = max(p, buf->next_char(lineStart));
colNum++;
if (b >= buf->length()) { width = 0;
} else {
const char *s = buf->address(b);
width = measure_proportional_character(s, 0, 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 ? buf->next_char(b) : max(p, buf->next_char(lineStart));
*retLines = nLines;
*retLineStart = lineStart;
*retLineEnd = foundBreak ? b : p;
return;
}
lineStart = newLineStart;
}
}
*retPos = buf->length();
*retLines = nLines;
if (countLastLineMissingNewLine && colNum > 0)
*retLines = buf->next_char(*retLines);
*retLineStart = lineStart;
*retLineEnd = buf->length();
}
double Fl_Text_Display::measure_proportional_character(const char *s, int xPix, int pos) const {
IS_UTF8_ALIGNED(s)
if (*s=='\t') {
int tab = (int)col_to_x(mBuffer->tab_distance());
return (((xPix/tab)+1)*tab) - xPix;
}
int charLen = fl_utf8len1(*s), style = 0;
if (mStyleBuffer) {
style = mStyleBuffer->byte_at(pos);
}
return string_width(s, charLen, style);
}
void Fl_Text_Display::find_line_end(int startPos, bool startPosIsLineStart,
int *lineEnd, int *nextLineStart) const {
IS_UTF8_ALIGNED2(buffer(), startPos)
int retLines, retLineStart;
if (!mContinuousWrap) {
int le = buffer()->line_end(startPos);
int ls = buffer()->next_char(le);
*lineEnd = le;
*nextLineStart = min(buffer()->length(), ls);
return;
}
wrapped_line_counter(buffer(), startPos, buffer()->length(),
1, startPosIsLineStart, 0, nextLineStart, &retLines,
&retLineStart, lineEnd);
}
int Fl_Text_Display::wrap_uses_character(int lineEndPos) const {
IS_UTF8_ALIGNED2(buffer(), lineEndPos)
unsigned int c;
if (!mContinuousWrap || lineEndPos == buffer()->length())
return 1;
c = buffer()->char_at(lineEndPos);
return c == '\n' || ((c == '\t' || c == ' ') &&
lineEndPos + 1 < buffer()->length());
}
void Fl_Text_Display::extend_range_for_styles( int *startpos, int *endpos ) {
IS_UTF8_ALIGNED2(buffer(), (*startpos))
IS_UTF8_ALIGNED2(buffer(), (*endpos))
Fl_Text_Selection * sel = mStyleBuffer->primary_selection();
int extended = 0;
if ( sel->selected() ) {
if ( sel->start() < *startpos ) {
*startpos = sel->start();
*startpos = buffer()->utf8_align(*startpos);
IS_UTF8_ALIGNED2(buffer(), (*startpos))
extended = 1;
}
if ( sel->end() > *endpos ) {
*endpos = sel->end();
*endpos = buffer()->utf8_align(*endpos);
IS_UTF8_ALIGNED2(buffer(), (*endpos))
extended = 1;
}
}
if ( extended )
*endpos = mBuffer->line_end( *endpos ) + 1;
IS_UTF8_ALIGNED2(buffer(), (*endpos))
}
void Fl_Text_Display::draw(void) {
if (!buffer()) { draw_box(); return; }
if (display_needs_recalc_ || (damage() & FL_DAMAGE_ALL)) {
display_needs_recalc_ = false;
recalc_display();
}
fl_push_clip(x(),y(),w(),h());
Fl_Color bgcolor = active_r() ? color() : fl_inactive(color());
if (damage() & FL_DAMAGE_ALL) {
recalc_display();
if (Fl_Surface_Device::surface() != Fl_Display_Device::display_device()) {
fl_rectf(text_area.x, text_area.y, text_area.w, text_area.h, bgcolor);
}
draw_box(box(), x(), y(), w(), h(), bgcolor);
fl_rectf(text_area.x-LEFT_MARGIN, text_area.y-TOP_MARGIN,
LEFT_MARGIN, text_area.h+TOP_MARGIN+BOTTOM_MARGIN,
bgcolor);
fl_rectf(text_area.x+text_area.w, text_area.y-TOP_MARGIN,
RIGHT_MARGIN, text_area.h+TOP_MARGIN+BOTTOM_MARGIN,
bgcolor);
fl_rectf(text_area.x, text_area.y-TOP_MARGIN,
text_area.w, TOP_MARGIN, bgcolor);
fl_rectf(text_area.x, text_area.y+text_area.h,
text_area.w, BOTTOM_MARGIN, bgcolor);
if (mVScrollBar->visible() && mHScrollBar->visible())
fl_rectf(mVScrollBar->x(), mHScrollBar->y(),
mVScrollBar->w(), mHScrollBar->h(),
FL_BACKGROUND_COLOR);
}
else if (damage() & (FL_DAMAGE_SCROLL | FL_DAMAGE_EXPOSE)) {
fl_push_clip(text_area.x-LEFT_MARGIN,
text_area.y,
text_area.w+LEFT_MARGIN+RIGHT_MARGIN,
text_area.h);
fl_rectf(text_area.x-LEFT_MARGIN, mCursorOldY,
LEFT_MARGIN, mMaxsize, bgcolor);
fl_rectf(text_area.x+text_area.w, mCursorOldY,
RIGHT_MARGIN, mMaxsize, bgcolor);
fl_pop_clip();
}
if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_CHILD)) {
mVScrollBar->damage(FL_DAMAGE_ALL);
mHScrollBar->damage(FL_DAMAGE_ALL);
}
update_child(*mVScrollBar);
update_child(*mHScrollBar);
if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_EXPOSE)) {
int X = 0, Y = 0, W = 0, H = 0;
if (fl_clip_box(text_area.x, text_area.y, text_area.w, text_area.h,
X, Y, W, H)) {
draw_text(X, Y, W, H);
} else {
draw_text(text_area.x, text_area.y, text_area.w, text_area.h);
}
}
else if (damage() & FL_DAMAGE_SCROLL) {
fl_push_clip(text_area.x, text_area.y,
text_area.w, text_area.h);
draw_range(damage_range1_start, damage_range1_end);
if (damage_range2_end != -1) {
draw_range(damage_range2_start, damage_range2_end);
}
damage_range1_start = damage_range1_end = -1;
damage_range2_start = damage_range2_end = -1;
fl_pop_clip();
}
int start, end;
int has_selection = buffer()->selection_position(&start, &end);
if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_SCROLL | FL_DAMAGE_EXPOSE)
&& (
(Fl::screen_driver()->has_marked_text() && Fl::compose_state) ||
(!has_selection) || mCursorPos < start || mCursorPos > end) &&
mCursorOn && Fl::focus() == (Fl_Widget*)this ) {
fl_push_clip(text_area.x-LEFT_MARGIN,
text_area.y,
text_area.w+LEFT_MARGIN+RIGHT_MARGIN,
text_area.h);
int X = 0, Y = 0;
if (position_to_xy(mCursorPos, &X, &Y)) {
draw_cursor(X, Y);
mCursorOldY = Y;
}
fl_pop_clip();
}
draw_line_numbers(true);
fl_pop_clip();
}
int fl_text_drag_prepare(int pos, int key, Fl_Text_Display* d) {
if (d->buffer()->selected()) {
int start, end;
d->buffer()->selection_position(&start, &end);
if ( (d->dragPos!=start || d->mCursorPos!=end) && (d->dragPos!=end || d->mCursorPos!=start) ) {
if (pos!=-1) {
if (pos<start) {
d->mCursorPos = start;
d->dragPos = end;
} else {
d->mCursorPos = end;
d->dragPos = start;
}
} else if (key!=-1) {
switch (key) {
case FL_Home: case FL_Left: case FL_Up: case FL_Page_Up:
d->dragPos = end; d->mCursorPos = start; break;
default:
d->dragPos = start; d->mCursorPos = end; break;
}
} else {
d->dragPos = start;
d->mCursorPos = end;
}
return 1;
}
}
return 0;
}
void fl_text_drag_me(int pos, Fl_Text_Display* d) {
if (d->dragType == Fl_Text_Display::DRAG_CHAR) {
if (pos >= d->dragPos) {
d->buffer()->select(d->dragPos, pos);
} else {
d->buffer()->select(pos, d->dragPos);
}
d->insert_position(pos);
} else if (d->dragType == Fl_Text_Display::DRAG_WORD) {
if (pos >= d->dragPos) {
d->insert_position(d->word_end(pos));
d->buffer()->select(d->word_start(d->dragPos), d->word_end(pos));
} else {
d->insert_position(d->word_start(pos));
d->buffer()->select(d->word_start(pos), d->word_end(d->dragPos));
}
} else if (d->dragType == Fl_Text_Display::DRAG_LINE) {
if (pos >= d->dragPos) {
d->insert_position(d->buffer()->line_end(pos)+1);
d->buffer()->select(d->buffer()->line_start(d->dragPos),
d->buffer()->line_end(pos)+1);
} else {
d->insert_position(d->buffer()->line_start(pos));
d->buffer()->select(d->buffer()->line_start(pos),
d->buffer()->line_end(d->dragPos)+1);
}
}
}
void Fl_Text_Display::scroll_timer_cb(void *user_data) {
Fl_Text_Display *w = (Fl_Text_Display*)user_data;
int pos;
switch (scroll_direction) {
case 1: w->scroll(w->mTopLineNum, w->mHorizOffset + scroll_amount);
pos = w->xy_to_position(w->text_area.x + w->text_area.w, scroll_y, CURSOR_POS);
break;
case 2: w->scroll(w->mTopLineNum, w->mHorizOffset + scroll_amount);
pos = w->xy_to_position(w->text_area.x, scroll_y, CURSOR_POS);
break;
case 3: w->scroll(w->mTopLineNum + scroll_amount, w->mHorizOffset);
pos = w->xy_to_position(scroll_x, w->text_area.y, CURSOR_POS);
break;
case 4: w->scroll(w->mTopLineNum + scroll_amount, w->mHorizOffset);
pos = w->xy_to_position(scroll_x, w->text_area.y + w->text_area.h, CURSOR_POS);
break;
default:
return;
}
fl_text_drag_me(pos, w);
Fl::repeat_timeout(.1, scroll_timer_cb, user_data);
}
int Fl_Text_Display::handle_rmb(int readonly) {
Fl_Text_Buffer *txtbuf = buffer();
int newpos = xy_to_position(Fl::event_x(), Fl::event_y(), CURSOR_POS);
int oldpos = txtbuf->primary_selection()->start();
int oldmark = txtbuf->primary_selection()->end();
if ( ((oldpos < newpos) && (oldmark > newpos))
|| ((oldmark < newpos) && (oldpos > newpos))
|| (type() == FL_SECRET_INPUT)) {
} else {
if ((txtbuf->char_at(newpos) == 0) || (txtbuf->char_at(newpos) == '\n')) {
txtbuf->select(newpos, newpos);
} else {
txtbuf->select(txtbuf->word_start(newpos), txtbuf->word_end(newpos));
}
}
rmb_menu[0].label(Fl_Input::cut_menu_text);
rmb_menu[1].label(Fl_Input::copy_menu_text);
rmb_menu[2].label(Fl_Input::paste_menu_text);
if (readonly) {
rmb_menu[0].deactivate(); rmb_menu[2].deactivate(); } else {
rmb_menu[0].activate(); rmb_menu[2].activate(); }
fl_cursor(FL_CURSOR_DEFAULT);
const Fl_Menu_Item *mi = rmb_menu->popup(Fl::event_x(), Fl::event_y());
if (mi) return (int)mi->argument();
return 0;
}
int Fl_Text_Display::handle(int event) {
if (!buffer()) return 0;
if (!Fl::event_inside(text_area.x, text_area.y, text_area.w, text_area.h) &&
!dragging && event != FL_LEAVE && event != FL_ENTER &&
event != FL_MOVE && event != FL_FOCUS && event != FL_UNFOCUS &&
event != FL_KEYBOARD && event != FL_KEYUP && event != FL_MOUSEWHEEL) {
return Fl_Group::handle(event);
}
switch (event) {
case FL_ENTER:
case FL_MOVE:
if (active_r()) {
if (Fl::event_inside(text_area.x, text_area.y, text_area.w,
text_area.h)) window()->cursor(FL_CURSOR_INSERT);
else window()->cursor(FL_CURSOR_DEFAULT);
return 1;
} else {
return 0;
}
case FL_LEAVE:
case FL_HIDE:
if (active_r() && window()) {
window()->cursor(FL_CURSOR_DEFAULT);
return 1;
} else {
return 0;
}
case FL_PUSH: {
if (active_r() && window()) {
if (Fl::event_inside(text_area.x, text_area.y, text_area.w,
text_area.h)) window()->cursor(FL_CURSOR_INSERT);
else window()->cursor(FL_CURSOR_DEFAULT);
}
if (Fl::focus() != this) {
Fl::focus(this);
handle(FL_FOCUS);
}
if (Fl_Group::handle(event)) return 1;
if (Fl::event_button() == FL_RIGHT_MOUSE) {
switch (handle_rmb(1)) {
case 2: {
if (!buffer()->selected()) break;
const char *copy = buffer()->selection_text();
if (*copy) Fl::copy(copy, (int) strlen(copy), 1);
free((void*)copy);
show_insert_position();
break;
}
}
return 1;
}
if (Fl::event_state()&FL_SHIFT) {
if (buffer()->primary_selection()->selected()) {
int pos = xy_to_position(Fl::event_x(), Fl::event_y(), CURSOR_POS);
fl_text_drag_prepare(pos, -1, this);
} else {
dragPos = insert_position();
}
return handle(FL_DRAG);
}
dragging = 1;
int pos = xy_to_position(Fl::event_x(), Fl::event_y(), CURSOR_POS);
dragPos = pos;
if (buffer()->primary_selection()->includes(pos)) {
dragType = DRAG_START_DND;
return 1;
}
dragType = Fl::event_clicks();
if (dragType == DRAG_CHAR) {
buffer()->unselect();
}
else if (dragType == DRAG_WORD) {
buffer()->select(word_start(pos), word_end(pos));
dragPos = word_start(pos);
}
if (buffer()->primary_selection()->selected())
insert_position(buffer()->primary_selection()->end());
else
insert_position(pos);
show_insert_position();
return 1;
}
case FL_DRAG: {
if (dragType==DRAG_NONE)
return 1;
if (dragType==DRAG_START_DND) {
if (!Fl::event_is_click() && Fl::dnd_text_ops()) {
const char* copy = buffer()->selection_text();
Fl::copy(copy, (int)strlen(copy));
Fl::screen_driver()->dnd(1);
free((void*)copy);
}
return 1;
}
int X = Fl::event_x(), Y = Fl::event_y(), pos = insert_position();
if (Y < text_area.y) {
scroll_x = X;
scroll_amount = (Y - text_area.y) / 5 - 1;
if (!scroll_direction) {
Fl::add_timeout(.01, scroll_timer_cb, this);
}
scroll_direction = 3;
} else if (Y >= text_area.y+text_area.h) {
scroll_x = X;
scroll_amount = (Y - text_area.y - text_area.h) / 5 + 1;
if (!scroll_direction) {
Fl::add_timeout(.01, scroll_timer_cb, this);
}
scroll_direction = 4;
} else if (X < text_area.x) {
scroll_y = Y;
scroll_amount = (X - text_area.x) / 2 - 1;
if (!scroll_direction) {
Fl::add_timeout(.01, scroll_timer_cb, this);
}
scroll_direction = 2;
} else if (X >= text_area.x+text_area.w) {
scroll_y = Y;
scroll_amount = (X - text_area.x - text_area.w) / 2 + 1;
if (!scroll_direction) {
Fl::add_timeout(.01, scroll_timer_cb, this);
}
scroll_direction = 1;
} else {
if (scroll_direction) {
Fl::remove_timeout(scroll_timer_cb, this);
scroll_direction = 0;
}
pos = xy_to_position(X, Y, CURSOR_POS);
}
fl_text_drag_me(pos, this);
return 1;
}
case FL_RELEASE: {
if (Fl::event_is_click() && (! Fl::event_clicks()) &&
buffer()->primary_selection()->includes(dragPos) && !(Fl::event_state()&FL_SHIFT) ) {
buffer()->unselect(); insert_position(dragPos);
dragType = DRAG_CHAR;
return 1;
} else if (Fl::event_clicks() == DRAG_LINE && Fl::event_button() == FL_LEFT_MOUSE) {
buffer()->select(buffer()->line_start(dragPos), buffer()->next_char(buffer()->line_end(dragPos)));
dragPos = line_start(dragPos);
dragType = DRAG_CHAR;
} else {
dragging = 0;
if (scroll_direction) {
Fl::remove_timeout(scroll_timer_cb, this);
scroll_direction = 0;
}
dragType = DRAG_CHAR;
}
const char* copy = buffer()->selection_text();
if (*copy) Fl::copy(copy, (int) strlen(copy), 0);
free((void*)copy);
return 1;
}
case FL_MOUSEWHEEL:
if (Fl::e_dy && mVScrollBar->visible()) {
Fl_Scrollbar *vs = mVScrollBar;
if ((Fl::e_dy < 0) && (vs->value() == int(vs->minimum()))) return 0; if ((Fl::e_dy > 0) && (vs->value() == int(vs->maximum()))) return 0; return vs->handle(event);
} else if (Fl::e_dx && mHScrollBar->visible()) {
Fl_Scrollbar *hs = mHScrollBar;
if ((Fl::e_dx < 0) && (hs->value() == int(hs->minimum()))) return 0; if ((Fl::e_dx > 0) && (hs->value() == int(hs->maximum()))) return 0; return hs->handle(event);
}
return 0;
case FL_UNFOCUS:
if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT);
case FL_FOCUS:
if (buffer()->selected()) {
int start, end;
if (buffer()->selection_position(&start, &end))
redisplay_range(start, end);
}
if (buffer()->secondary_selected()) {
int start, end;
if (buffer()->secondary_selection_position(&start, &end))
redisplay_range(start, end);
}
if (buffer()->highlight()) {
int start, end;
if (buffer()->highlight_position(&start, &end))
redisplay_range(start, end);
}
return 1;
case FL_KEYBOARD:
if ((Fl::event_state()&(FL_CTRL|FL_COMMAND)) && Fl::event_key()=='c') {
if (!buffer()->selected()) return 1;
const char *copy = buffer()->selection_text();
if (*copy) Fl::copy(copy, (int) strlen(copy), 1);
free((void*)copy);
return 1;
}
if ((Fl::event_state()&(FL_CTRL|FL_COMMAND)) && Fl::event_key()=='a') {
buffer()->select(0,buffer()->length());
const char *copy = buffer()->selection_text();
if (*copy) Fl::copy(copy, (int) strlen(copy), 0);
free((void*)copy);
return 1;
}
if (mVScrollBar->handle(event)) return 1;
if (mHScrollBar->handle(event)) return 1;
break;
case FL_SHORTCUT:
if (!(shortcut() ? Fl::test_shortcut(shortcut()) : test_shortcut()))
return 0;
if (Fl::visible_focus() && handle(FL_FOCUS)) {
Fl::focus(this);
return 1;
}
break;
}
return 0;
}
double Fl_Text_Display::x_to_col(double x) const
{
if (!mColumnScale) {
mColumnScale = string_width("Mitg", 4, 'A') / 4.0;
}
return (x/mColumnScale)+0.5;
}
double Fl_Text_Display::col_to_x(double col) const
{
if (!mColumnScale) {
x_to_col(0);
}
return col*mColumnScale;
}