📄 fl_text_display.cxx
字号:
text_area.x = X+LEFT_MARGIN;
text_area.y = Y+BOTTOM_MARGIN;
text_area.w = W-LEFT_MARGIN-RIGHT_MARGIN;
text_area.h = H-TOP_MARGIN-BOTTOM_MARGIN;
int i;
/* Find the new maximum font height for this text display */
for (i = 0, mMaxsize = fl_height(textfont(), textsize()); i < mNStyles; i++)
mMaxsize = max(mMaxsize, fl_height(mStyleTable[i].font, mStyleTable[i].size));
// did we have scrollbars initially?
int hscrollbarvisible = mHScrollBar->visible();
int vscrollbarvisible = mVScrollBar->visible();
// try without scrollbars first
mVScrollBar->clear_visible();
mHScrollBar->clear_visible();
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;
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();
}
// figure the scrollbars
if (scrollbar_width()) {
/* Decide if the vertical scroll bar needs to be visible */
if (scrollbar_align() & (FL_ALIGN_LEFT|FL_ALIGN_RIGHT) &&
mNBufferLines >= mNVisibleLines - 1)
{
mVScrollBar->set_visible();
if (scrollbar_align() & FL_ALIGN_LEFT) {
text_area.x = X+scrollbar_width()+LEFT_MARGIN;
text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN;
mVScrollBar->resize(X, text_area.y-TOP_MARGIN, scrollbar_width(),
text_area.h+TOP_MARGIN+BOTTOM_MARGIN);
} else {
text_area.x = X+LEFT_MARGIN;
text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN;
mVScrollBar->resize(X+W-scrollbar_width(), text_area.y-TOP_MARGIN,
scrollbar_width(), text_area.h+TOP_MARGIN+BOTTOM_MARGIN);
}
}
/*
Decide if the horizontal scroll bar needs to be visible. If there
is a vertical scrollbar, a horizontal is always created too. This
is because the alternatives are unatractive:
* Dynamically creating a horizontal scrollbar based on the currently
visible lines is what the original nedit does, but it always wastes
space for the scrollbar even when it's not used. Since the FLTK
widget dynamically allocates the space for the scrollbar and
rearranges the widget to make room for it, this would create a very
visually displeasing "bounce" effect when the vertical scrollbar is
dragged. Trust me, I tried it and it looks really bad.
* The other alternative would be to keep track of what the longest
line in the entire buffer is and base the scrollbar on that. I
didn't do this because I didn't see any easy way to do that using
the nedit code and this could involve a lengthy calculation for
large buffers. If an efficient and non-costly way of doing this
can be found, this might be a way to go.
*/
/* WAS: Suggestion: Try turning the horizontal scrollbar on when
you first see a line that is too wide in the window, but then
don't turn it off (ie mix both of your solutions). */
if (scrollbar_align() & (FL_ALIGN_TOP|FL_ALIGN_BOTTOM) &&
(mVScrollBar->visible() || longest_vline() > text_area.w))
{
if (!mHScrollBar->visible()) {
mHScrollBar->set_visible();
again = 1; // loop again to see if we now need vert. & recalc sizes
}
if (scrollbar_align() & FL_ALIGN_TOP) {
text_area.y = Y + scrollbar_width()+TOP_MARGIN;
text_area.h = H - scrollbar_width()-TOP_MARGIN-BOTTOM_MARGIN;
mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y,
text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width());
} else {
text_area.y = Y+TOP_MARGIN;
text_area.h = H - scrollbar_width()-TOP_MARGIN-BOTTOM_MARGIN;
mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y+H-scrollbar_width(),
text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width());
}
}
}
}
// user request to change viewport
if (mTopLineNumHint != mTopLineNum || mHorizOffsetHint != mHorizOffset)
scroll_(mTopLineNumHint, mHorizOffsetHint);
// everything will fit in the viewport
if (mNBufferLines < mNVisibleLines || mBuffer == NULL || mBuffer->length() == 0)
scroll_(1, mHorizOffset);
/* if empty lines become visible, there may be an opportunity to
display more text by scrolling down */
else while (mLineStarts[mNVisibleLines-2] == -1)
scroll_(mTopLineNum-1, mHorizOffset);
// user request to display insert position
if (display_insert_position_hint)
display_insert();
// in case horizontal offset is now greater than longest line
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();
}
/*
** Refresh a rectangle of the text display. left and top are in coordinates of
** the text drawing window
*/
void Fl_Text_Display::draw_text( int left, int top, int width, int height ) {
int fontHeight, firstLine, lastLine, line;
/* find the line number range of the display */
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 );
/* draw the lines */
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();
}
void Fl_Text_Display::redisplay_range(int startpos, int 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);
}
/*
** Refresh all of the text between buffer positions "start" and "end"
** not including the character at the position "end".
** If end points beyond the end of the buffer, refresh the whole display
** after pos, including blank lines which are not technically part of
** any range of characters.
*/
void Fl_Text_Display::draw_range(int startpos, int endpos) {
int i, startLine, lastLine, startIndex, endIndex;
/* If the range is outside of the displayed text, just return */
if ( endpos < mFirstChar || ( startpos > mLastChar &&
!empty_vlines() ) ) return;
/* Clean up the starting and ending values */
if ( startpos < 0 ) startpos = 0;
if ( startpos > mBuffer->length() ) startpos = mBuffer->length();
if ( endpos < 0 ) endpos = 0;
if ( endpos > mBuffer->length() ) endpos = mBuffer->length();
/* Get the starting and ending lines */
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 ) ) {
/* shouldn't happen */
lastLine = mNVisibleLines - 1;
}
}
/* Get the starting and ending positions within the lines */
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 the starting and ending lines are the same, redisplay the single
line between "start" and "end" */
if ( startLine == lastLine ) {
draw_vline( startLine, 0, INT_MAX, startIndex, endIndex );
return;
}
/* Redisplay the first line from "start" */
draw_vline( startLine, 0, INT_MAX, startIndex, INT_MAX );
/* Redisplay the lines in between at their full width */
for ( i = startLine + 1; i < lastLine; i++ )
draw_vline( i, 0, INT_MAX, 0, INT_MAX );
/* Redisplay the last line to "end" */
draw_vline( lastLine, 0, INT_MAX, 0, endIndex );
}
/*
** Set the position of the text insertion cursor for text display
*/
void Fl_Text_Display::insert_position( int newPos ) {
/* make sure new position is ok, do nothing if it hasn't changed */
if ( newPos == mCursorPos )
return;
if ( newPos < 0 ) newPos = 0;
if ( newPos > mBuffer->length() ) newPos = mBuffer->length();
/* cursor movement cancels vertical cursor motion column */
mCursorPreferredCol = -1;
/* erase the cursor at it's previous position */
redisplay_range(mCursorPos - 1, mCursorPos + 1);
mCursorPos = newPos;
/* draw cursor at its new position */
redisplay_range(mCursorPos - 1, mCursorPos + 1);
}
void Fl_Text_Display::show_cursor(int b) {
mCursorOn = b;
redisplay_range(mCursorPos - 1, mCursorPos + 1);
}
void Fl_Text_Display::cursor_style(int style) {
mCursorStyle = 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
** then moving the insert position after the newly inserted text, except
** that it's optimized to do less redrawing.
*/
void Fl_Text_Display::insert(const char* text) {
int pos = mCursorPos;
mCursorToHint = pos + strlen( text );
mBuffer->insert( pos, text );
mCursorToHint = NO_HINT;
}
/*
** Insert "text" (which must not contain newlines), overstriking the current
** cursor location.
*/
void Fl_Text_Display::overstrike(const char* text) {
int startPos = mCursorPos;
Fl_Text_Buffer *buf = mBuffer;
int lineStart = buf->line_start( startPos );
int textLen = strlen( text );
int i, p, endPos, indent, startIndent, endIndent;
const char *c;
char ch, *paddedText = NULL;
/* determine how many displayed character positions are covered */
startIndent = mBuffer->count_displayed_characters( lineStart, startPos );
indent = startIndent;
for ( c = text; *c != '\0'; c++ )
indent += Fl_Text_Buffer::character_width( *c, indent, buf->tab_distance(), buf->null_substitution_character() );
endIndent = indent;
/* find which characters to remove, and if necessary generate additional
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -