|
fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation) ![]() |
00001 // 00002 // "$Id: Fl_Text_Display.cxx 8078 2010-12-20 17:32:36Z AlbrechtS $" 00003 // 00004 // Copyright 2001-2010 by Bill Spitzak and others. 00005 // Original code Copyright Mark Edel. Permission to distribute under 00006 // the LGPL for the FLTK library granted by Mark Edel. 00007 // 00008 // This library is free software; you can redistribute it and/or 00009 // modify it under the terms of the GNU Library General Public 00010 // License as published by the Free Software Foundation; either 00011 // version 2 of the License, or (at your option) any later version. 00012 // 00013 // This library is distributed in the hope that it will be useful, 00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 // Library General Public License for more details. 00017 // 00018 // You should have received a copy of the GNU Library General Public 00019 // License along with this library; if not, write to the Free Software 00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 00021 // USA. 00022 // 00023 // Please report all bugs and problems on the following page: 00024 // 00025 // http://www.fltk.org/str.php 00026 // 00027 00028 // TODO: rendering of the "optional hyphen" 00029 // TODO: make line numbering work again 00030 00031 #include <stdio.h> 00032 #include <stdlib.h> 00033 #include <FL/fl_utf8.h> 00034 #include "flstring.h" 00035 #include <limits.h> 00036 #include <ctype.h> 00037 #include <FL/Fl.H> 00038 #include <FL/Fl_Text_Buffer.H> 00039 #include <FL/Fl_Text_Display.H> 00040 #include <FL/Fl_Window.H> 00041 00042 #undef min 00043 #undef max 00044 00045 // Text area margins. Left & right margins should be at least 3 so that 00046 // there is some room for the overhanging parts of the cursor! 00047 #define TOP_MARGIN 1 00048 #define BOTTOM_MARGIN 1 00049 #define LEFT_MARGIN 3 00050 #define RIGHT_MARGIN 3 00051 00052 #define NO_HINT -1 00053 00054 /* Masks for text drawing methods. These are or'd together to form an 00055 integer which describes what drawing calls to use to draw a string */ 00056 #define FILL_MASK 0x0100 00057 #define SECONDARY_MASK 0x0200 00058 #define PRIMARY_MASK 0x0400 00059 #define HIGHLIGHT_MASK 0x0800 00060 #define BG_ONLY_MASK 0x1000 00061 #define TEXT_ONLY_MASK 0x2000 00062 #define STYLE_LOOKUP_MASK 0xff 00063 00064 /* Maximum displayable line length (how many characters will fit across the 00065 widest window). This amount of memory is temporarily allocated from the 00066 stack in the draw_vline() method for drawing strings */ 00067 #define MAX_DISP_LINE_LEN 1000 00068 00069 static int max( int i1, int i2 ); 00070 static int min( int i1, int i2 ); 00071 static int countlines( const char *string ); 00072 00073 /* The variables below are used in a timer event to allow smooth 00074 scrolling of the text area when the pointer has left the area. */ 00075 static int scroll_direction = 0; 00076 static int scroll_amount = 0; 00077 static int scroll_y = 0; 00078 static int scroll_x = 0; 00079 00080 // CET - FIXME 00081 #define TMPFONTWIDTH 6 00082 00083 00084 00091 Fl_Text_Display::Fl_Text_Display(int X, int Y, int W, int H, const char* l) 00092 : Fl_Group(X, Y, W, H, l) { 00093 int i; 00094 00095 mMaxsize = 0; 00096 damage_range1_start = damage_range1_end = -1; 00097 damage_range2_start = damage_range2_end = -1; 00098 dragPos = dragging = 0; 00099 dragType = DRAG_CHAR; 00100 display_insert_position_hint = 0; 00101 shortcut_ = 0; 00102 00103 color(FL_BACKGROUND2_COLOR, FL_SELECTION_COLOR); 00104 box(FL_DOWN_FRAME); 00105 textsize(FL_NORMAL_SIZE); 00106 textcolor(FL_FOREGROUND_COLOR); 00107 textfont(FL_HELVETICA); 00108 set_flag(SHORTCUT_LABEL); 00109 00110 text_area.x = 0; 00111 text_area.y = 0; 00112 text_area.w = 0; 00113 text_area.h = 0; 00114 00115 mVScrollBar = new Fl_Scrollbar(0,0,1,1); 00116 mVScrollBar->callback((Fl_Callback*)v_scrollbar_cb, this); 00117 mHScrollBar = new Fl_Scrollbar(0,0,1,1); 00118 mHScrollBar->callback((Fl_Callback*)h_scrollbar_cb, this); 00119 mHScrollBar->type(FL_HORIZONTAL); 00120 00121 end(); 00122 00123 scrollbar_width(Fl::scrollbar_size()); 00124 scrollbar_align(FL_ALIGN_BOTTOM_RIGHT); 00125 00126 mCursorOn = 0; 00127 mCursorPos = 0; 00128 mCursorOldY = -100; 00129 mCursorToHint = NO_HINT; 00130 mCursorStyle = NORMAL_CURSOR; 00131 mCursorPreferredXPos = -1; 00132 mBuffer = 0; 00133 mFirstChar = 0; 00134 mLastChar = 0; 00135 mNBufferLines = 0; 00136 mTopLineNum = mTopLineNumHint = 1; 00137 mAbsTopLineNum = 1; 00138 mNeedAbsTopLineNum = 0; 00139 mHorizOffset = mHorizOffsetHint = 0; 00140 00141 mCursor_color = FL_FOREGROUND_COLOR; 00142 00143 mStyleBuffer = 0; 00144 mStyleTable = 0; 00145 mNStyles = 0; 00146 mNVisibleLines = 1; 00147 mLineStarts = new int[mNVisibleLines]; 00148 mLineStarts[0] = 0; 00149 for (i=1; i<mNVisibleLines; i++) 00150 mLineStarts[i] = -1; 00151 mSuppressResync = 0; 00152 mNLinesDeleted = 0; 00153 mModifyingTabDistance = 0; 00154 00155 mUnfinishedStyle = 0; 00156 mUnfinishedHighlightCB = 0; 00157 mHighlightCBArg = 0; 00158 00159 mLineNumLeft = mLineNumWidth = 0; 00160 mContinuousWrap = 0; 00161 mWrapMarginPix = 0; 00162 mSuppressResync = mNLinesDeleted = mModifyingTabDistance = 0; 00163 } 00164 00165 00166 00173 Fl_Text_Display::~Fl_Text_Display() { 00174 if (scroll_direction) { 00175 Fl::remove_timeout(scroll_timer_cb, this); 00176 scroll_direction = 0; 00177 } 00178 if (mBuffer) { 00179 mBuffer->remove_modify_callback(buffer_modified_cb, this); 00180 mBuffer->remove_predelete_callback(buffer_predelete_cb, this); 00181 } 00182 if (mLineStarts) delete[] mLineStarts; 00183 } 00184 00185 00186 00191 void Fl_Text_Display::buffer( Fl_Text_Buffer *buf ) { 00192 /* If the text display is already displaying a buffer, clear it off 00193 of the display and remove our callback from it */ 00194 if ( buf == mBuffer) return; 00195 if ( mBuffer != 0 ) { 00196 // we must provide a copy of the buffer that we are deleting! 00197 char *deletedText = mBuffer->text(); 00198 buffer_modified_cb( 0, 0, mBuffer->length(), 0, deletedText, this ); 00199 free(deletedText); 00200 mNBufferLines = 0; 00201 mBuffer->remove_modify_callback( buffer_modified_cb, this ); 00202 mBuffer->remove_predelete_callback( buffer_predelete_cb, this ); 00203 } 00204 00205 /* Add the buffer to the display, and attach a callback to the buffer for 00206 receiving modification information when the buffer contents change */ 00207 mBuffer = buf; 00208 if (mBuffer) { 00209 mBuffer->add_modify_callback( buffer_modified_cb, this ); 00210 mBuffer->add_predelete_callback( buffer_predelete_cb, this ); 00211 00212 /* Update the display */ 00213 buffer_modified_cb( 0, buf->length(), 0, 0, 0, this ); 00214 } 00215 00216 /* Resize the widget to update the screen... */ 00217 resize(x(), y(), w(), h()); 00218 } 00219 00220 00221 00249 void Fl_Text_Display::highlight_data(Fl_Text_Buffer *styleBuffer, 00250 const Style_Table_Entry *styleTable, 00251 int nStyles, char unfinishedStyle, 00252 Unfinished_Style_Cb unfinishedHighlightCB, 00253 void *cbArg ) { 00254 mStyleBuffer = styleBuffer; 00255 mStyleTable = styleTable; 00256 mNStyles = nStyles; 00257 mUnfinishedStyle = unfinishedStyle; 00258 mUnfinishedHighlightCB = unfinishedHighlightCB; 00259 mHighlightCBArg = cbArg; 00260 mColumnScale = 0; 00261 00262 mStyleBuffer->canUndo(0); 00263 damage(FL_DAMAGE_EXPOSE); 00264 } 00265 00266 00267 00272 int Fl_Text_Display::longest_vline() const { 00273 int longest = 0; 00274 for (int i = 0; i < mNVisibleLines; i++) 00275 longest = max(longest, measure_vline(i)); 00276 return longest; 00277 } 00278 00279 00280 00287 void Fl_Text_Display::resize(int X, int Y, int W, int H) { 00288 #ifdef DEBUG 00289 printf("Fl_Text_Display::resize(X=%d, Y=%d, W=%d, H=%d)\n", X, Y, W, H); 00290 #endif // DEBUG 00291 const int oldWidth = w(); 00292 #ifdef DEBUG 00293 printf(" oldWidth=%d, mContinuousWrap=%d, mWrapMargin=%d\n", oldWidth, mContinuousWrap, mWrapMargin); 00294 #endif // DEBUG 00295 Fl_Widget::resize(X,Y,W,H); 00296 if (!buffer()) return; 00297 X += Fl::box_dx(box()); 00298 Y += Fl::box_dy(box()); 00299 W -= Fl::box_dw(box()); 00300 H -= Fl::box_dh(box()); 00301 00302 text_area.x = X+LEFT_MARGIN; 00303 text_area.y = Y+TOP_MARGIN; 00304 text_area.w = W-LEFT_MARGIN-RIGHT_MARGIN; 00305 text_area.h = H-TOP_MARGIN-BOTTOM_MARGIN; 00306 int i; 00307 00308 /* Find the new maximum font height for this text display */ 00309 for (i = 0, mMaxsize = fl_height(textfont(), textsize()); i < mNStyles; i++) 00310 mMaxsize = max(mMaxsize, fl_height(mStyleTable[i].font, mStyleTable[i].size)); 00311 00312 // did we have scrollbars initially? 00313 unsigned int hscrollbarvisible = mHScrollBar->visible(); 00314 unsigned int vscrollbarvisible = mVScrollBar->visible(); 00315 00316 // try without scrollbars first 00317 mVScrollBar->clear_visible(); 00318 mHScrollBar->clear_visible(); 00319 00320 for (int again = 1; again;) { 00321 again = 0; 00322 /* In continuous wrap mode, a change in width affects the total number of 00323 lines in the buffer, and can leave the top line number incorrect, and 00324 the top character no longer pointing at a valid line start */ 00325 if (mContinuousWrap && !mWrapMarginPix && W!=oldWidth) { 00326 int oldFirstChar = mFirstChar; 00327 mNBufferLines = count_lines(0, buffer()->length(), true); 00328 mFirstChar = line_start(mFirstChar); 00329 mTopLineNum = count_lines(0, mFirstChar, true)+1; 00330 absolute_top_line_number(oldFirstChar); 00331 #ifdef DEBUG 00332 printf(" mNBufferLines=%d\n", mNBufferLines); 00333 #endif // DEBUG 00334 } 00335 00336 /* reallocate and update the line starts array, which may have changed 00337 size and / or contents. */ 00338 int nvlines = (text_area.h + mMaxsize - 1) / mMaxsize; 00339 if (nvlines < 1) nvlines = 1; 00340 if (mNVisibleLines != nvlines) { 00341 mNVisibleLines = nvlines; 00342 if (mLineStarts) delete[] mLineStarts; 00343 mLineStarts = new int [mNVisibleLines]; 00344 } 00345 00346 calc_line_starts(0, mNVisibleLines); 00347 calc_last_char(); 00348 00349 // figure the scrollbars 00350 if (scrollbar_width()) { 00351 /* Decide if the vertical scrollbar needs to be visible */ 00352 if (scrollbar_align() & (FL_ALIGN_LEFT|FL_ALIGN_RIGHT) && 00353 mNBufferLines >= mNVisibleLines - 1) 00354 { 00355 mVScrollBar->set_visible(); 00356 if (scrollbar_align() & FL_ALIGN_LEFT) { 00357 text_area.x = X+scrollbar_width()+LEFT_MARGIN; 00358 text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN; 00359 mVScrollBar->resize(X, text_area.y-TOP_MARGIN, scrollbar_width(), 00360 text_area.h+TOP_MARGIN+BOTTOM_MARGIN); 00361 } else { 00362 text_area.x = X+LEFT_MARGIN; 00363 text_area.w = W-scrollbar_width()-LEFT_MARGIN-RIGHT_MARGIN; 00364 mVScrollBar->resize(X+W-scrollbar_width(), text_area.y-TOP_MARGIN, 00365 scrollbar_width(), text_area.h+TOP_MARGIN+BOTTOM_MARGIN); 00366 } 00367 } 00368 00369 /* 00370 Decide if the horizontal scrollbar needs to be visible. If there 00371 is a vertical scrollbar, a horizontal is always created too. This 00372 is because the alternatives are unattractive: 00373 * Dynamically creating a horizontal scrollbar based on the currently 00374 visible lines is what the original nedit does, but it always wastes 00375 space for the scrollbar even when it's not used. Since the FLTK 00376 widget dynamically allocates the space for the scrollbar and 00377 rearranges the widget to make room for it, this would create a very 00378 visually displeasing "bounce" effect when the vertical scrollbar is 00379 dragged. Trust me, I tried it and it looks really bad. 00380 * The other alternative would be to keep track of what the longest 00381 line in the entire buffer is and base the scrollbar on that. I 00382 didn't do this because I didn't see any easy way to do that using 00383 the nedit code and this could involve a lengthy calculation for 00384 large buffers. If an efficient and non-costly way of doing this 00385 can be found, this might be a way to go. 00386 */ 00387 /* WAS: Suggestion: Try turning the horizontal scrollbar on when 00388 you first see a line that is too wide in the window, but then 00389 don't turn it off (ie mix both of your solutions). */ 00390 if (scrollbar_align() & (FL_ALIGN_TOP|FL_ALIGN_BOTTOM) && 00391 (mVScrollBar->visible() || longest_vline() > text_area.w)) 00392 { 00393 if (!mHScrollBar->visible()) { 00394 mHScrollBar->set_visible(); 00395 again = 1; // loop again to see if we now need vert. & recalc sizes 00396 } 00397 if (scrollbar_align() & FL_ALIGN_TOP) { 00398 text_area.y = Y + scrollbar_width()+TOP_MARGIN; 00399 text_area.h = H - scrollbar_width()-TOP_MARGIN-BOTTOM_MARGIN; 00400 mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y, 00401 text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width()); 00402 } else { 00403 text_area.y = Y+TOP_MARGIN; 00404 text_area.h = H - scrollbar_width()-TOP_MARGIN-BOTTOM_MARGIN; 00405 mHScrollBar->resize(text_area.x-LEFT_MARGIN, Y+H-scrollbar_width(), 00406 text_area.w+LEFT_MARGIN+RIGHT_MARGIN, scrollbar_width()); 00407 } 00408 } 00409 } 00410 } 00411 00412 // user request to change viewport 00413 if (mTopLineNumHint != mTopLineNum || mHorizOffsetHint != mHorizOffset) 00414 scroll_(mTopLineNumHint, mHorizOffsetHint); 00415 00416 // everything will fit in the viewport 00417 if (mNBufferLines < mNVisibleLines || mBuffer == NULL || mBuffer->length() == 0) { 00418 scroll_(1, mHorizOffset); 00419 /* if empty lines become visible, there may be an opportunity to 00420 display more text by scrolling down */ 00421 } else { 00422 while ( (mLineStarts[mNVisibleLines-2]==-1) 00423 && scroll_(mTopLineNum-1, mHorizOffset)) 00424 { } 00425 } 00426 00427 // user request to display insert position 00428 if (display_insert_position_hint) 00429 display_insert(); 00430 00431 // in case horizontal offset is now greater than longest line 00432 int maxhoffset = max(0, longest_vline()-text_area.w); 00433 if (mHorizOffset > maxhoffset) 00434 scroll_(mTopLineNumHint, maxhoffset); 00435 00436 mTopLineNumHint = mTopLineNum; 00437 mHorizOffsetHint = mHorizOffset; 00438 display_insert_position_hint = 0; 00439 00440 if (mContinuousWrap || 00441 hscrollbarvisible != mHScrollBar->visible() || 00442 vscrollbarvisible != mVScrollBar->visible()) 00443 redraw(); 00444 00445 update_v_scrollbar(); 00446 update_h_scrollbar(); 00447 } 00448 00449 00450 00456 void Fl_Text_Display::draw_text( int left, int top, int width, int height ) { 00457 int fontHeight, firstLine, lastLine, line; 00458 00459 /* find the line number range of the display */ 00460 fontHeight = mMaxsize ? mMaxsize : textsize_; 00461 firstLine = ( top - text_area.y - fontHeight + 1 ) / fontHeight; 00462 lastLine = ( top + height - text_area.y ) / fontHeight + 1; 00463 00464 fl_push_clip( left, top, width, height ); 00465 00466 /* draw the lines */ 00467 for ( line = firstLine; line <= lastLine; line++ ) 00468 draw_vline( line, left, left + width, 0, INT_MAX ); 00469 00470 /* draw the line numbers if exposed area includes them */ 00471 if (mLineNumWidth != 0 && left <= mLineNumLeft + mLineNumWidth) 00472 draw_line_numbers(false); 00473 00474 fl_pop_clip(); 00475 } 00476 00477 00478 00486 void Fl_Text_Display::redisplay_range(int startpos, int endpos) { 00487 IS_UTF8_ALIGNED2(buffer(), startpos) 00488 IS_UTF8_ALIGNED2(buffer(), endpos) 00489 00490 if (damage_range1_start == -1 && damage_range1_end == -1) { 00491 damage_range1_start = startpos; 00492 damage_range1_end = endpos; 00493 } else if ((startpos >= damage_range1_start && startpos <= damage_range1_end) || 00494 (endpos >= damage_range1_start && endpos <= damage_range1_end)) { 00495 damage_range1_start = min(damage_range1_start, startpos); 00496 damage_range1_end = max(damage_range1_end, endpos); 00497 } else if (damage_range2_start == -1 && damage_range2_end == -1) { 00498 damage_range2_start = startpos; 00499 damage_range2_end = endpos; 00500 } else { 00501 damage_range2_start = min(damage_range2_start, startpos); 00502 damage_range2_end = max(damage_range2_end, endpos); 00503 } 00504 damage(FL_DAMAGE_SCROLL); 00505 } 00506 00507 00508 00522 void Fl_Text_Display::draw_range(int startpos, int endpos) { 00523 startpos = buffer()->utf8_align(startpos); 00524 endpos = buffer()->utf8_align(endpos); 00525 00526 int i, startLine, lastLine, startIndex, endIndex; 00527 00528 /* If the range is outside of the displayed text, just return */ 00529 if ( endpos < mFirstChar || ( startpos > mLastChar && !empty_vlines() ) ) 00530 return; 00531 00532 /* Clean up the starting and ending values */ 00533 if ( startpos < 0 ) startpos = 0; 00534 if ( startpos > mBuffer->length() ) startpos = mBuffer->length(); 00535 if ( endpos < 0 ) endpos = 0; 00536 if ( endpos > mBuffer->length() ) endpos = mBuffer->length(); 00537 00538 /* Get the starting and ending lines */ 00539 if ( startpos < mFirstChar ) 00540 startpos = mFirstChar; 00541 if ( !position_to_line( startpos, &startLine ) ) 00542 startLine = mNVisibleLines - 1; 00543 if ( endpos >= mLastChar ) { 00544 lastLine = mNVisibleLines - 1; 00545 } else { 00546 if ( !position_to_line( endpos, &lastLine ) ) { 00547 /* shouldn't happen */ 00548 lastLine = mNVisibleLines - 1; 00549 } 00550 } 00551 00552 /* Get the starting and ending positions within the lines */ 00553 startIndex = mLineStarts[ startLine ] == -1 ? 0 : startpos - mLineStarts[ startLine ]; 00554 if ( endpos >= mLastChar ) 00555 endIndex = INT_MAX; 00556 else if ( mLineStarts[ lastLine ] == -1 ) 00557 endIndex = 0; 00558 else 00559 endIndex = endpos - mLineStarts[ lastLine ]; 00560 00561 /* If the starting and ending lines are the same, redisplay the single 00562 line between "start" and "end" */ 00563 if ( startLine == lastLine ) { 00564 draw_vline( startLine, 0, INT_MAX, startIndex, endIndex ); 00565 return; 00566 } 00567 00568 /* Redisplay the first line from "start" */ 00569 draw_vline( startLine, 0, INT_MAX, startIndex, INT_MAX ); 00570 00571 /* Redisplay the lines in between at their full width */ 00572 for ( i = startLine + 1; i < lastLine; i++ ) 00573 draw_vline( i, 0, INT_MAX, 0, INT_MAX ); 00574 00575 /* Redisplay the last line to "end" */ 00576 draw_vline( lastLine, 0, INT_MAX, 0, endIndex ); 00577 } 00578 00579 00580 00587 void Fl_Text_Display::insert_position( int newPos ) { 00588 IS_UTF8_ALIGNED2(buffer(), newPos) 00589 00590 /* make sure new position is ok, do nothing if it hasn't changed */ 00591 if ( newPos == mCursorPos ) return; 00592 if ( newPos < 0 ) newPos = 0; 00593 if ( newPos > mBuffer->length() ) newPos = mBuffer->length(); 00594 00595 /* cursor movement cancels vertical cursor motion column */ 00596 mCursorPreferredXPos = -1; 00597 00598 /* erase the cursor at its previous position */ 00599 redisplay_range(buffer()->prev_char_clipped(mCursorPos), buffer()->next_char(mCursorPos)); 00600 00601 mCursorPos = newPos; 00602 00603 /* draw cursor at its new position */ 00604 redisplay_range(buffer()->prev_char_clipped(mCursorPos), buffer()->next_char(mCursorPos)); 00605 } 00606 00607 00608 00614 void Fl_Text_Display::show_cursor(int b) { 00615 mCursorOn = b; 00616 redisplay_range(buffer()->prev_char_clipped(mCursorPos), buffer()->next_char(mCursorPos)); 00617 } 00618 00619 00620 00636 void Fl_Text_Display::cursor_style(int style) { 00637 mCursorStyle = style; 00638 if (mCursorOn) show_cursor(); 00639 } 00640 00641 00642 00663 void Fl_Text_Display::wrap_mode(int wrap, int wrapMargin) { 00664 switch (wrap) { 00665 case WRAP_NONE: 00666 mWrapMarginPix = 0; 00667 mContinuousWrap = 0; 00668 break; 00669 case WRAP_AT_COLUMN: 00670 default: 00671 mWrapMarginPix = int(col_to_x(wrapMargin)); 00672 mContinuousWrap = 1; 00673 break; 00674 case WRAP_AT_PIXEL: 00675 mWrapMarginPix = wrapMargin; 00676 mContinuousWrap = 1; 00677 break; 00678 case WRAP_AT_BOUNDS: 00679 mWrapMarginPix = 0; 00680 mContinuousWrap = 1; 00681 break; 00682 } 00683 00684 if (buffer()) { 00685 /* wrapping can change the total number of lines, re-count */ 00686 mNBufferLines = count_lines(0, buffer()->length(), true); 00687 00688 /* changing wrap margins or changing from wrapped mode to non-wrapped 00689 can leave the character at the top no longer at a line start, and/or 00690 change the line number */ 00691 mFirstChar = line_start(mFirstChar); 00692 mTopLineNum = count_lines(0, mFirstChar, true) + 1; 00693 00694 reset_absolute_top_line_number(); 00695 00696 /* update the line starts array */ 00697 calc_line_starts(0, mNVisibleLines); 00698 calc_last_char(); 00699 } else { 00700 // No buffer, so just clear the state info for later... 00701 mNBufferLines = 0; 00702 mFirstChar = 0; 00703 mTopLineNum = 1; 00704 mAbsTopLineNum = 0; 00705 } 00706 00707 resize(x(), y(), w(), h()); 00708 } 00709 00710 00711 00721 void Fl_Text_Display::insert(const char* text) { 00722 IS_UTF8_ALIGNED2(buffer(), mCursorPos) 00723 IS_UTF8_ALIGNED(text) 00724 00725 int pos = mCursorPos; 00726 00727 mCursorToHint = pos + strlen( text ); 00728 mBuffer->insert( pos, text ); 00729 mCursorToHint = NO_HINT; 00730 } 00731 00732 00733 00740 void Fl_Text_Display::overstrike(const char* text) { 00741 IS_UTF8_ALIGNED2(buffer(), mCursorPos) 00742 IS_UTF8_ALIGNED(text) 00743 00744 int startPos = mCursorPos; 00745 Fl_Text_Buffer *buf = mBuffer; 00746 int lineStart = buf->line_start( startPos ); 00747 int textLen = strlen( text ); 00748 int i, p, endPos, indent, startIndent, endIndent; 00749 const char *c; 00750 unsigned int ch; 00751 char *paddedText = NULL; 00752 00753 /* determine how many displayed character positions are covered */ 00754 startIndent = mBuffer->count_displayed_characters( lineStart, startPos ); 00755 indent = startIndent; 00756 for ( c = text; *c != '\0'; c += fl_utf8len1(*c) ) 00757 indent++; 00758 endIndent = indent; 00759 00760 /* find which characters to remove, and if necessary generate additional 00761 padding to make up for removed control characters at the end */ 00762 indent = startIndent; 00763 for ( p = startPos; ; p=buffer()->next_char(p) ) { 00764 if ( p == buf->length() ) 00765 break; 00766 ch = buf->char_at( p ); 00767 if ( ch == '\n' ) 00768 break; 00769 indent++; 00770 if ( indent == endIndent ) { 00771 p++; 00772 break; 00773 } else if ( indent > endIndent ) { 00774 if ( ch != '\t' ) { 00775 p++; 00776 paddedText = new char [ textLen + FL_TEXT_MAX_EXP_CHAR_LEN + 1 ]; 00777 strcpy( paddedText, text ); 00778 for ( i = 0; i < indent - endIndent; i++ ) 00779 paddedText[ textLen + i ] = ' '; 00780 paddedText[ textLen + i ] = '\0'; 00781 } 00782 break; 00783 } 00784 } 00785 endPos = p; 00786 00787 mCursorToHint = startPos + textLen; 00788 buf->replace( startPos, endPos, paddedText == NULL ? text : paddedText ); 00789 mCursorToHint = NO_HINT; 00790 if ( paddedText != NULL ) 00791 delete [] paddedText; 00792 } 00793 00794 00795 00809 int Fl_Text_Display::position_to_xy( int pos, int* X, int* Y ) const { 00810 IS_UTF8_ALIGNED2(buffer(), pos) 00811 00812 int lineStartPos, fontHeight, lineLen; 00813 int visLineNum; 00814 00815 /* If position is not displayed, return false */ 00816 if (pos < mFirstChar || (pos > mLastChar && !empty_vlines())) { 00817 return 0; 00818 } 00819 00820 /* Calculate Y coordinate */ 00821 if (!position_to_line(pos, &visLineNum)) { 00822 return 0; 00823 } 00824 if (visLineNum < 0 || visLineNum > mNBufferLines) { 00825 return 0; 00826 } 00827 00828 fontHeight = mMaxsize; 00829 *Y = text_area.y + visLineNum * fontHeight; 00830 00831 /* Get the text, length, and buffer position of the line. If the position 00832 is beyond the end of the buffer and should be at the first position on 00833 the first empty line, don't try to get or scan the text */ 00834 lineStartPos = mLineStarts[visLineNum]; 00835 if ( lineStartPos == -1 ) { 00836 *X = text_area.x - mHorizOffset; 00837 return 1; 00838 } 00839 lineLen = vline_length( visLineNum ); 00840 *X = text_area.x + handle_vline(GET_WIDTH, lineStartPos, pos-lineStartPos, 0, 0, 0, 0, 0, 0) - mHorizOffset; 00841 return 1; 00842 } 00843 00844 00845 00863 int Fl_Text_Display::position_to_linecol( int pos, int* lineNum, int* column ) const { 00864 IS_UTF8_ALIGNED2(buffer(), pos) 00865 00866 int retVal; 00867 00868 /* In continuous wrap mode, the absolute (non-wrapped) line count is 00869 maintained separately, as needed. Only return it if we're actually 00870 keeping track of it and pos is in the displayed text */ 00871 if (mContinuousWrap) { 00872 if (!maintaining_absolute_top_line_number() || pos < mFirstChar || pos > mLastChar) 00873 return 0; 00874 *lineNum = mAbsTopLineNum + buffer()->count_lines(mFirstChar, pos); 00875 *column = buffer()->count_displayed_characters(buffer()->line_start(pos), pos); 00876 return 1; 00877 } 00878 00879 retVal = position_to_line( pos, lineNum ); 00880 if ( retVal ) { 00881 *column = mBuffer->count_displayed_characters( mLineStarts[ *lineNum ], pos ); 00882 *lineNum += mTopLineNum; 00883 } 00884 return retVal; 00885 } 00886 00887 00888 00894 int Fl_Text_Display::in_selection( int X, int Y ) const { 00895 int pos = xy_to_position( X, Y, CHARACTER_POS ); 00896 IS_UTF8_ALIGNED2(buffer(), pos) 00897 Fl_Text_Buffer *buf = mBuffer; 00898 return buf->primary_selection()->includes(pos); 00899 } 00900 00901 00902 00923 int Fl_Text_Display::wrapped_column(int row, int column) const { 00924 int lineStart, dispLineStart; 00925 00926 if (!mContinuousWrap || row < 0 || row > mNVisibleLines) 00927 return column; 00928 dispLineStart = mLineStarts[row]; 00929 if (dispLineStart == -1) 00930 return column; 00931 lineStart = buffer()->line_start(dispLineStart); 00932 return column + buffer()->count_displayed_characters(lineStart, dispLineStart); 00933 } 00934 00935 00936 00951 int Fl_Text_Display::wrapped_row(int row) const { 00952 if (!mContinuousWrap || row < 0 || row > mNVisibleLines) 00953 return row; 00954 return buffer()->count_lines(mFirstChar, mLineStarts[row]); 00955 } 00956 00957 00958 00969 void Fl_Text_Display::display_insert() { 00970 int hOffset, topLine, X, Y; 00971 hOffset = mHorizOffset; 00972 topLine = mTopLineNum; 00973 00974 if (insert_position() < mFirstChar) { 00975 topLine -= count_lines(insert_position(), mFirstChar, false); 00976 } else if (mLineStarts[mNVisibleLines-2] != -1) { 00977 int lastChar = line_end(mLineStarts[mNVisibleLines-2],true); 00978 if (insert_position() >= lastChar) 00979 topLine += count_lines(lastChar - (wrap_uses_character(mLastChar) ? 0 : 1), 00980 insert_position(), false); 00981 } 00982 00983 /* Find the new setting for horizontal offset (this is a bit ungraceful). 00984 If the line is visible, just use PositionToXY to get the position 00985 to scroll to, otherwise, do the vertical scrolling first, then the 00986 horizontal */ 00987 if (!position_to_xy( mCursorPos, &X, &Y )) { 00988 scroll_(topLine, hOffset); 00989 if (!position_to_xy( mCursorPos, &X, &Y )) { 00990 #ifdef DEBUG 00991 printf ("*** display_insert/position_to_xy # GIVE UP !\n"); fflush(stdout); 00992 #endif // DEBUG 00993 return; /* Give up, it's not worth it (but why does it fail?) */ 00994 } 00995 } 00996 if (X > text_area.x + text_area.w) 00997 hOffset += X-(text_area.x + text_area.w); 00998 else if (X < text_area.x) 00999 hOffset += X-text_area.x; 01000 01001 /* Do the scroll */ 01002 if (topLine != mTopLineNum || hOffset != mHorizOffset) 01003 scroll_(topLine, hOffset); 01004 } 01005 01006 01012 void Fl_Text_Display::show_insert_position() { 01013 display_insert_position_hint = 1; 01014 resize(x(), y(), w(), h()); 01015 } 01016 01017 01018 /* 01019 Cursor movement functions 01020 */ 01021 01026 int Fl_Text_Display::move_right() { 01027 if ( mCursorPos >= mBuffer->length() ) 01028 return 0; 01029 int p = insert_position(); 01030 int q = buffer()->next_char(p); 01031 insert_position(q); 01032 return 1; 01033 } 01034 01035 01036 01041 int Fl_Text_Display::move_left() { 01042 if ( mCursorPos <= 0 ) 01043 return 0; 01044 int p = insert_position(); 01045 int q = buffer()->prev_char_clipped(p); 01046 insert_position(q); 01047 return 1; 01048 } 01049 01050 01051 01056 int Fl_Text_Display::move_up() { 01057 int lineStartPos, xPos, prevLineStartPos, newPos, visLineNum; 01058 01059 /* Find the position of the start of the line. Use the line starts array 01060 if possible */ 01061 if ( position_to_line( mCursorPos, &visLineNum ) ) 01062 lineStartPos = mLineStarts[ visLineNum ]; 01063 else { 01064 lineStartPos = line_start( mCursorPos ); 01065 visLineNum = -1; 01066 } 01067 if ( lineStartPos == 0 ) 01068 return 0; 01069 01070 /* Decide what column to move to, if there's a preferred column use that */ 01071 if (mCursorPreferredXPos >= 0) 01072 xPos = mCursorPreferredXPos; 01073 else 01074 xPos = handle_vline(GET_WIDTH, lineStartPos, mCursorPos-lineStartPos, 01075 0, 0, 0, 0, 0, INT_MAX); 01076 01077 /* count forward from the start of the previous line to reach the column */ 01078 if ( visLineNum != -1 && visLineNum != 0 ) 01079 prevLineStartPos = mLineStarts[ visLineNum - 1 ]; 01080 else 01081 prevLineStartPos = rewind_lines( lineStartPos, 1 ); 01082 01083 int lineEnd = line_end(prevLineStartPos, true); 01084 newPos = handle_vline(FIND_INDEX_FROM_ZERO, prevLineStartPos, lineEnd-prevLineStartPos, 01085 0, 0, 0, 0, 0, xPos); 01086 01087 /* move the cursor */ 01088 insert_position( newPos ); 01089 01090 /* if a preferred column wasn't aleady established, establish it */ 01091 mCursorPreferredXPos = xPos; 01092 return 1; 01093 } 01094 01095 01096 01101 int Fl_Text_Display::move_down() { 01102 int lineStartPos, xPos, newPos, visLineNum; 01103 01104 if ( mCursorPos == mBuffer->length() ) 01105 return 0; 01106 01107 if ( position_to_line( mCursorPos, &visLineNum ) ) 01108 lineStartPos = mLineStarts[ visLineNum ]; 01109 else { 01110 lineStartPos = line_start( mCursorPos ); 01111 visLineNum = -1; 01112 } 01113 if (mCursorPreferredXPos >= 0) { 01114 xPos = mCursorPreferredXPos; 01115 } else { 01116 xPos = handle_vline(GET_WIDTH, lineStartPos, mCursorPos-lineStartPos, 01117 0, 0, 0, 0, 0, INT_MAX); 01118 } 01119 01120 int nextLineStartPos = skip_lines( lineStartPos, 1, true ); 01121 int lineEnd = line_end(nextLineStartPos, true); 01122 newPos = handle_vline(FIND_INDEX_FROM_ZERO, nextLineStartPos, lineEnd-nextLineStartPos, 01123 0, 0, 0, 0, 0, xPos); 01124 01125 insert_position( newPos ); 01126 mCursorPreferredXPos = xPos; 01127 return 1; 01128 } 01129 01130 01131 01145 int Fl_Text_Display::count_lines(int startPos, int endPos, 01146 bool startPosIsLineStart) const { 01147 IS_UTF8_ALIGNED2(buffer(), startPos) 01148 IS_UTF8_ALIGNED2(buffer(), endPos) 01149 01150 int retLines, retPos, retLineStart, retLineEnd; 01151 01152 #ifdef DEBUG 01153 printf("Fl_Text_Display::count_lines(startPos=%d, endPos=%d, startPosIsLineStart=%d\n", 01154 startPos, endPos, startPosIsLineStart); 01155 #endif // DEBUG 01156 01157 /* If we're not wrapping use simple (and more efficient) BufCountLines */ 01158 if (!mContinuousWrap) 01159 return buffer()->count_lines(startPos, endPos); 01160 01161 wrapped_line_counter(buffer(), startPos, endPos, INT_MAX, 01162 startPosIsLineStart, 0, &retPos, &retLines, &retLineStart, 01163 &retLineEnd); 01164 01165 #ifdef DEBUG 01166 printf(" # after WLC: retPos=%d, retLines=%d, retLineStart=%d, retLineEnd=%d\n", 01167 retPos, retLines, retLineStart, retLineEnd); 01168 #endif // DEBUG 01169 01170 return retLines; 01171 } 01172 01173 01174 01188 int Fl_Text_Display::skip_lines(int startPos, int nLines, 01189 bool startPosIsLineStart) { 01190 IS_UTF8_ALIGNED2(buffer(), startPos) 01191 01192 int retLines, retPos, retLineStart, retLineEnd; 01193 01194 /* if we're not wrapping use more efficient BufCountForwardNLines */ 01195 if (!mContinuousWrap) 01196 return buffer()->skip_lines(startPos, nLines); 01197 01198 /* wrappedLineCounter can't handle the 0 lines case */ 01199 if (nLines == 0) 01200 return startPos; 01201 01202 /* use the common line counting routine to count forward */ 01203 wrapped_line_counter(buffer(), startPos, buffer()->length(), 01204 nLines, startPosIsLineStart, 0, 01205 &retPos, &retLines, &retLineStart, &retLineEnd); 01206 IS_UTF8_ALIGNED2(buffer(), retPos) 01207 return retPos; 01208 } 01209 01210 01211 01234 int Fl_Text_Display::line_end(int startPos, bool startPosIsLineStart) const { 01235 IS_UTF8_ALIGNED2(buffer(), startPos) 01236 01237 int retLines, retPos, retLineStart, retLineEnd; 01238 01239 /* If we're not wrapping use more efficient BufEndOfLine */ 01240 if (!mContinuousWrap) 01241 return buffer()->line_end(startPos); 01242 01243 if (startPos == buffer()->length()) 01244 return startPos; 01245 01246 wrapped_line_counter(buffer(), startPos, buffer()->length(), 1, 01247 startPosIsLineStart, 0, &retPos, &retLines, &retLineStart, 01248 &retLineEnd); 01249 01250 IS_UTF8_ALIGNED2(buffer(), retLineEnd) 01251 return retLineEnd; 01252 } 01253 01254 01255 01265 int Fl_Text_Display::line_start(int pos) const { 01266 IS_UTF8_ALIGNED2(buffer(), pos) 01267 01268 int retLines, retPos, retLineStart, retLineEnd; 01269 01270 /* If we're not wrapping, use the more efficient BufStartOfLine */ 01271 if (!mContinuousWrap) 01272 return buffer()->line_start(pos); 01273 01274 wrapped_line_counter(buffer(), buffer()->line_start(pos), pos, INT_MAX, true, 0, 01275 &retPos, &retLines, &retLineStart, &retLineEnd); 01276 01277 IS_UTF8_ALIGNED2(buffer(), retLineStart) 01278 return retLineStart; 01279 } 01280 01281 01282 01293 int Fl_Text_Display::rewind_lines(int startPos, int nLines) { 01294 IS_UTF8_ALIGNED2(buffer(), startPos) 01295 01296 Fl_Text_Buffer *buf = buffer(); 01297 int pos, lineStart, retLines, retPos, retLineStart, retLineEnd; 01298 01299 /* If we're not wrapping, use the more efficient BufCountBackwardNLines */ 01300 if (!mContinuousWrap) 01301 return buf->rewind_lines(startPos, nLines); 01302 01303 pos = startPos; 01304 for (;;) { 01305 lineStart = buf->line_start(pos); 01306 wrapped_line_counter(buf, lineStart, pos, INT_MAX, true, 0, 01307 &retPos, &retLines, &retLineStart, &retLineEnd, false); 01308 if (retLines > nLines) 01309 return skip_lines(lineStart, retLines-nLines, true); 01310 nLines -= retLines; 01311 pos = lineStart - 1; 01312 if (pos < 0) 01313 return 0; 01314 nLines -= 1; 01315 } 01316 } 01317 01318 01319 01320 static inline int fl_isseparator(unsigned int c) { 01321 // FIXME: this does not take UCS-4 encoding into account 01322 return c != '$' && c != '_' && (isspace(c) || ispunct(c)); 01323 } 01324 01325 01326 01330 void Fl_Text_Display::next_word() { 01331 int pos = insert_position(); 01332 01333 while (pos < buffer()->length() && !fl_isseparator(buffer()->char_at(pos))) { 01334 pos = buffer()->next_char(pos); 01335 } 01336 01337 while (pos < buffer()->length() && fl_isseparator(buffer()->char_at(pos))) { 01338 pos = buffer()->next_char(pos); 01339 } 01340 01341 insert_position( pos ); 01342 } 01343 01344 01345 01349 void Fl_Text_Display::previous_word() { 01350 int pos = insert_position(); 01351 if (pos==0) return; 01352 pos = buffer()->prev_char(pos); 01353 01354 while (pos && fl_isseparator(buffer()->char_at(pos))) { 01355 pos = buffer()->prev_char(pos); 01356 } 01357 01358 while (pos && !fl_isseparator(buffer()->char_at(pos))) { 01359 pos = buffer()->prev_char(pos); 01360 } 01361 01362 if (fl_isseparator(buffer()->char_at(pos))) { 01363 pos = buffer()->next_char(pos); 01364 } 01365 01366 insert_position( pos ); 01367 } 01368 01369 01370 01381 void Fl_Text_Display::buffer_predelete_cb(int pos, int nDeleted, void *cbArg) { 01382 Fl_Text_Display *textD = (Fl_Text_Display *)cbArg; 01383 if (textD->mContinuousWrap) { 01384 /* Note: we must perform this measurement, even if there is not a 01385 single character deleted; the number of "deleted" lines is the 01386 number of visual lines spanned by the real line in which the 01387 modification takes place. 01388 Also, a modification of the tab distance requires the same 01389 kind of calculations in advance, even if the font width is "fixed", 01390 because when the width of the tab characters changes, the layout 01391 of the text may be completely different. */ 01392 IS_UTF8_ALIGNED2(textD->buffer(), pos) 01393 textD->measure_deleted_lines(pos, nDeleted); 01394 } else { 01395 textD->mSuppressResync = 0; /* Probably not needed, but just in case */ 01396 } 01397 } 01398 01399 01400 01413 void Fl_Text_Display::buffer_modified_cb( int pos, int nInserted, int nDeleted, 01414 int nRestyled, const char *deletedText, void *cbArg ) { 01415 int linesInserted, linesDeleted, startDispPos, endDispPos; 01416 Fl_Text_Display *textD = ( Fl_Text_Display * ) cbArg; 01417 Fl_Text_Buffer *buf = textD->mBuffer; 01418 int oldFirstChar = textD->mFirstChar; 01419 int scrolled, origCursorPos = textD->mCursorPos; 01420 int wrapModStart = 0, wrapModEnd = 0; 01421 01422 IS_UTF8_ALIGNED2(buf, pos) 01423 IS_UTF8_ALIGNED2(buf, oldFirstChar) 01424 01425 /* buffer modification cancels vertical cursor motion column */ 01426 if ( nInserted != 0 || nDeleted != 0 ) 01427 textD->mCursorPreferredXPos = -1; 01428 01429 /* Count the number of lines inserted and deleted, and in the case 01430 of continuous wrap mode, how much has changed */ 01431 if (textD->mContinuousWrap) { 01432 textD->find_wrap_range(deletedText, pos, nInserted, nDeleted, 01433 &wrapModStart, &wrapModEnd, &linesInserted, &linesDeleted); 01434 } else { 01435 linesInserted = nInserted == 0 ? 0 : buf->count_lines( pos, pos + nInserted ); 01436 linesDeleted = nDeleted == 0 ? 0 : countlines( deletedText ); 01437 } 01438 01439 /* Update the line starts and mTopLineNum */ 01440 if ( nInserted != 0 || nDeleted != 0 ) { 01441 if (textD->mContinuousWrap) { 01442 textD->update_line_starts( wrapModStart, wrapModEnd-wrapModStart, 01443 nDeleted + pos-wrapModStart + (wrapModEnd-(pos+nInserted)), 01444 linesInserted, linesDeleted, &scrolled ); 01445 } else { 01446 textD->update_line_starts( pos, nInserted, nDeleted, linesInserted, 01447 linesDeleted, &scrolled ); 01448 } 01449 } else 01450 scrolled = 0; 01451 01452 /* If we're counting non-wrapped lines as well, maintain the absolute 01453 (non-wrapped) line number of the text displayed */ 01454 if (textD->maintaining_absolute_top_line_number() && 01455 (nInserted != 0 || nDeleted != 0)) { 01456 if (deletedText && (pos + nDeleted < oldFirstChar)) 01457 textD->mAbsTopLineNum += buf->count_lines(pos, pos + nInserted) - 01458 countlines(deletedText); 01459 else if (pos < oldFirstChar) 01460 textD->reset_absolute_top_line_number(); 01461 } 01462 01463 /* Update the line count for the whole buffer */ 01464 textD->mNBufferLines += linesInserted - linesDeleted; 01465 01466 /* Update the cursor position */ 01467 if ( textD->mCursorToHint != NO_HINT ) { 01468 textD->mCursorPos = textD->mCursorToHint; 01469 textD->mCursorToHint = NO_HINT; 01470 } else if ( textD->mCursorPos > pos ) { 01471 if ( textD->mCursorPos < pos + nDeleted ) 01472 textD->mCursorPos = pos; 01473 else 01474 textD->mCursorPos += nInserted - nDeleted; 01475 } 01476 01477 // refigure scrollbars & stuff 01478 textD->resize(textD->x(), textD->y(), textD->w(), textD->h()); 01479 01480 // don't need to do anything else if not visible? 01481 if (!textD->visible_r()) return; 01482 01483 /* If the changes caused scrolling, re-paint everything and we're done. */ 01484 if ( scrolled ) { 01485 textD->damage(FL_DAMAGE_EXPOSE); 01486 if ( textD->mStyleBuffer ) /* See comments in extendRangeForStyleMods */ 01487 textD->mStyleBuffer->primary_selection()->selected(0); 01488 return; 01489 } 01490 01491 /* If the changes didn't cause scrolling, decide the range of characters 01492 that need to be re-painted. Also if the cursor position moved, be 01493 sure that the redisplay range covers the old cursor position so the 01494 old cursor gets erased, and erase the bits of the cursor which extend 01495 beyond the left and right edges of the text. */ 01496 startDispPos = textD->mContinuousWrap ? wrapModStart : pos; 01497 IS_UTF8_ALIGNED2(buf, startDispPos) 01498 01499 if ( origCursorPos == startDispPos && textD->mCursorPos != startDispPos ) 01500 startDispPos = min( startDispPos, buf->prev_char_clipped(origCursorPos) ); 01501 IS_UTF8_ALIGNED2(buf, startDispPos) 01502 01503 if ( linesInserted == linesDeleted ) { 01504 if ( nInserted == 0 && nDeleted == 0 ) 01505 endDispPos = pos + nRestyled; 01506 else { 01507 if (textD->mContinuousWrap) 01508 endDispPos = wrapModEnd; 01509 else 01510 endDispPos = buf->next_char(buf->line_end( pos + nInserted )); 01511 01512 // CET - FIXME if ( origCursorPos >= startDispPos && 01513 // ( origCursorPos <= endDispPos || endDispPos == buf->length() ) ) 01514 } 01515 01516 if (linesInserted > 1) textD->draw_line_numbers(false); 01517 } else { 01518 endDispPos = buf->next_char(textD->mLastChar); 01519 // CET - FIXME if ( origCursorPos >= pos ) 01520 /* If more than one line is inserted/deleted, a line break may have 01521 been inserted or removed in between, and the line numbers may 01522 have changed. If only one line is altered, line numbers cannot 01523 be affected (the insertion or removal of a line break always 01524 results in at least two lines being redrawn). */ 01525 textD->draw_line_numbers(false); 01526 } 01527 IS_UTF8_ALIGNED2(buf, startDispPos) 01528 IS_UTF8_ALIGNED2(buf, endDispPos) 01529 01530 /* If there is a style buffer, check if the modification caused additional 01531 changes that need to be redisplayed. (Redisplaying separately would 01532 cause double-redraw on almost every modification involving styled 01533 text). Extend the redraw range to incorporate style changes */ 01534 if ( textD->mStyleBuffer ) 01535 textD->extend_range_for_styles( &startDispPos, &endDispPos ); 01536 IS_UTF8_ALIGNED2(buf, startDispPos) 01537 IS_UTF8_ALIGNED2(buf, endDispPos) 01538 01539 /* Redisplay computed range */ 01540 textD->redisplay_range( startDispPos, endDispPos ); 01541 } 01542 01543 01544 01557 void Fl_Text_Display::maintain_absolute_top_line_number(int state) { 01558 mNeedAbsTopLineNum = state; 01559 reset_absolute_top_line_number(); 01560 } 01561 01562 01563 01570 int Fl_Text_Display::get_absolute_top_line_number() const { 01571 if (!mContinuousWrap) 01572 return mTopLineNum; 01573 if (maintaining_absolute_top_line_number()) 01574 return mAbsTopLineNum; 01575 return 0; 01576 } 01577 01578 01579 01585 void Fl_Text_Display::absolute_top_line_number(int oldFirstChar) { 01586 if (maintaining_absolute_top_line_number()) { 01587 if (mFirstChar < oldFirstChar) 01588 mAbsTopLineNum -= buffer()->count_lines(mFirstChar, oldFirstChar); 01589 else 01590 mAbsTopLineNum += buffer()->count_lines(oldFirstChar, mFirstChar); 01591 } 01592 } 01593 01594 01595 01602 int Fl_Text_Display::maintaining_absolute_top_line_number() const { 01603 return mContinuousWrap && 01604 (mLineNumWidth != 0 || mNeedAbsTopLineNum); 01605 } 01606 01607 01608 01616 void Fl_Text_Display::reset_absolute_top_line_number() { 01617 mAbsTopLineNum = 1; 01618 absolute_top_line_number(0); 01619 } 01620 01621 01622 01634 int Fl_Text_Display::position_to_line( int pos, int *lineNum ) const { 01635 IS_UTF8_ALIGNED2(buffer(), pos) 01636 01637 int i; 01638 01639 *lineNum = 0; 01640 if ( pos < mFirstChar ) return 0; 01641 if ( pos > mLastChar ) { 01642 if ( empty_vlines() ) { 01643 if ( mLastChar < mBuffer->length() ) { 01644 if ( !position_to_line( mLastChar, lineNum ) ) { 01645 Fl::error("Fl_Text_Display::position_to_line(): Consistency check ptvl failed"); 01646 return 0; 01647 } 01648 return ++( *lineNum ) <= mNVisibleLines - 1; 01649 } else { 01650 position_to_line( buffer()->prev_char_clipped(mLastChar), lineNum ); 01651 return 1; 01652 } 01653 } 01654 return 0; 01655 } 01656 01657 for ( i = mNVisibleLines - 1; i >= 0; i-- ) { 01658 if ( mLineStarts[ i ] != -1 && pos >= mLineStarts[ i ] ) { 01659 *lineNum = i; 01660 return 1; 01661 } 01662 } 01663 return 0; /* probably never be reached */ 01664 } 01665 01666 01690 int Fl_Text_Display::handle_vline( 01691 int mode, 01692 int lineStartPos, int lineLen, int leftChar, int rightChar, 01693 int Y, int bottomClip, 01694 int leftClip, int rightClip) const 01695 { 01696 IS_UTF8_ALIGNED2(buffer(), lineStartPos) 01697 01698 // FIXME: we need to allow two modes for FIND_INDEX: one on the edge of the 01699 // FIXME: character for selection, and one on the character center for cursors. 01700 int i, X, startX, startIndex, style, charStyle; 01701 char *lineStr; 01702 01703 if ( lineStartPos == -1 ) { 01704 lineStr = NULL; 01705 } else { 01706 lineStr = mBuffer->text_range( lineStartPos, lineStartPos + lineLen ); 01707 } 01708 01709 if (mode==GET_WIDTH) { 01710 X = 0; 01711 } else if (mode==FIND_INDEX_FROM_ZERO) { 01712 X = 0; 01713 mode = FIND_INDEX; 01714 } else { 01715 X = text_area.x - mHorizOffset; 01716 } 01717 01718 startX = X; 01719 startIndex = 0; 01720 if (!lineStr) { 01721 // just clear the background 01722 if (mode==DRAW_LINE) { 01723 style = position_style(lineStartPos, lineLen, -1); 01724 draw_string( style|BG_ONLY_MASK, text_area.x, Y, text_area.x+text_area.w, lineStr, lineLen ); 01725 } 01726 if (mode==FIND_INDEX) { 01727 IS_UTF8_ALIGNED2(buffer(), lineStartPos) 01728 return lineStartPos; 01729 } 01730 return 0; 01731 } 01732 01733 char currChar = 0, prevChar = 0; 01734 // draw the line 01735 style = position_style(lineStartPos, lineLen, 0); 01736 for (i=0; i<lineLen; ) { 01737 currChar = lineStr[i]; // one byte is enough to handele tabs and other cases 01738 int len = fl_utf8len1(currChar); 01739 if (len<=0) len = 1; // OUCH! 01740 charStyle = position_style(lineStartPos, lineLen, i); 01741 if (charStyle!=style || currChar=='\t' || prevChar=='\t') { 01742 // draw a segment whenever the style changes or a Tab is found 01743 int w = 0; 01744 if (prevChar=='\t') { 01745 // draw a single Tab space 01746 int tab = (int)col_to_x(8); 01747 int xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x; 01748 w = (((xAbs/tab)+1)*tab) - xAbs; 01749 if (mode==DRAW_LINE) 01750 draw_string( style|BG_ONLY_MASK, startX, Y, startX+w, 0, 0 ); 01751 if (mode==FIND_INDEX && startX+w>rightClip) { 01752 // find x pos inside block 01753 free(lineStr); 01754 return lineStartPos + startIndex; 01755 } 01756 } else { 01757 // draw a text segment 01758 w = int( string_width( lineStr+startIndex, i-startIndex, style ) ); 01759 if (mode==DRAW_LINE) 01760 draw_string( style, startX, Y, startX+w, lineStr+startIndex, i-startIndex ); 01761 if (mode==FIND_INDEX && startX+w>rightClip) { 01762 // find x pos inside block 01763 int di = find_x(lineStr+startIndex, i-startIndex, style, rightClip-startX); 01764 free(lineStr); 01765 IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di)) 01766 return lineStartPos + startIndex + di; 01767 } 01768 } 01769 style = charStyle; 01770 startX += w; 01771 startIndex = i; 01772 } 01773 i += len; 01774 prevChar = currChar; 01775 } 01776 int w = 0; 01777 if (currChar=='\t') { 01778 // draw a single Tab space 01779 int tab = (int)col_to_x(8); 01780 int xAbs = (mode==GET_WIDTH) ? startX : startX+mHorizOffset-text_area.x; 01781 w = (((xAbs/tab)+1)*tab) - xAbs; 01782 if (mode==DRAW_LINE) 01783 draw_string( style|BG_ONLY_MASK, startX, Y, startX+w, 0, 0 ); 01784 if (mode==FIND_INDEX) { 01785 // find x pos inside block 01786 free(lineStr); 01787 return lineStartPos + startIndex + ( rightClip-startX>w ? 1 : 0 ); 01788 } 01789 } else { 01790 w = int( string_width( lineStr+startIndex, i-startIndex, style ) ); 01791 if (mode==DRAW_LINE) 01792 draw_string( style, startX, Y, startX+w, lineStr+startIndex, i-startIndex ); 01793 if (mode==FIND_INDEX) { 01794 // find x pos inside block 01795 int di = find_x(lineStr+startIndex, i-startIndex, style, rightClip-startX); 01796 free(lineStr); 01797 IS_UTF8_ALIGNED2(buffer(), (lineStartPos+startIndex+di)) 01798 return lineStartPos + startIndex + di; 01799 } 01800 } 01801 if (mode==GET_WIDTH) { 01802 free(lineStr); 01803 return startX+w; 01804 } 01805 01806 // clear the rest of the line 01807 startX += w; 01808 style = position_style(lineStartPos, lineLen, i); 01809 if (mode==DRAW_LINE) 01810 draw_string( style|BG_ONLY_MASK, startX, Y, text_area.x+text_area.w, lineStr, lineLen ); 01811 01812 free(lineStr); 01813 IS_UTF8_ALIGNED2(buffer(), (lineStartPos+lineLen)) 01814 return lineStartPos + lineLen; 01815 } 01816 01817 01826 int Fl_Text_Display::find_x(const char *s, int len, int style, int x) const { 01827 IS_UTF8_ALIGNED(s) 01828 01829 // TODO: use binary search which may be quicker. 01830 int i = 0; 01831 while (i<len) { 01832 int cl = fl_utf8len1(s[i]); 01833 int w = int( string_width(s, i+cl, style) ); 01834 if (w>x) 01835 return i; 01836 i += cl; 01837 } 01838 return len; 01839 } 01840 01841 01842 01856 void Fl_Text_Display::draw_vline(int visLineNum, int leftClip, int rightClip, 01857 int leftCharIndex, int rightCharIndex) { 01858 int Y, lineStartPos, lineLen, fontHeight; 01859 01860 // printf("draw_vline(visLineNum=%d, leftClip=%d, rightClip=%d, leftCharIndex=%d, rightCharIndex=%d)\n", 01861 // visLineNum, leftClip, rightClip, leftCharIndex, rightCharIndex); 01862 // printf("nNVisibleLines=%d\n", mNVisibleLines); 01863 01864 /* If line is not displayed, skip it */ 01865 if ( visLineNum < 0 || visLineNum >= mNVisibleLines ) 01866 return; 01867 01868 /* Calculate Y coordinate of the string to draw */ 01869 fontHeight = mMaxsize; 01870 Y = text_area.y + visLineNum * fontHeight; 01871 01872 /* Get the text, length, and buffer position of the line to display */ 01873 lineStartPos = mLineStarts[ visLineNum ]; 01874 if ( lineStartPos == -1 ) { 01875 lineLen = 0; 01876 } else { 01877 lineLen = vline_length( visLineNum ); 01878 } 01879 01880 /* Shrink the clipping range to the active display area */ 01881 leftClip = max( text_area.x, leftClip ); 01882 rightClip = min( rightClip, text_area.x + text_area.w ); 01883 01884 handle_vline(DRAW_LINE, 01885 lineStartPos, lineLen, leftCharIndex, rightCharIndex, 01886 Y, Y+fontHeight, leftClip, rightClip); 01887 return; 01888 } 01889 01890 01891 01908 void Fl_Text_Display::draw_string(int style, 01909 int X, int Y, int toX, 01910 const char *string, int nChars) const { 01911 IS_UTF8_ALIGNED(string) 01912 01913 const Style_Table_Entry * styleRec; 01914 01915 /* Draw blank area rather than text, if that was the request */ 01916 if ( style & FILL_MASK ) { 01917 if (style & TEXT_ONLY_MASK) return; 01918 clear_rect( style, X, Y, toX - X, mMaxsize ); 01919 return; 01920 } 01921 /* Set font, color, and gc depending on style. For normal text, GCs 01922 for normal drawing, or drawing within a Fl_Text_Selection or highlight are 01923 pre-allocated and pre-configured. For syntax highlighting, GCs are 01924 configured here, on the fly. */ 01925 01926 Fl_Font font = textfont(); 01927 int fsize = textsize(); 01928 Fl_Color foreground; 01929 Fl_Color background; 01930 01931 if ( style & STYLE_LOOKUP_MASK ) { 01932 int si = (style & STYLE_LOOKUP_MASK) - 'A'; 01933 if (si < 0) si = 0; 01934 else if (si >= mNStyles) si = mNStyles - 1; 01935 01936 styleRec = mStyleTable + si; 01937 font = styleRec->font; 01938 fsize = styleRec->size; 01939 01940 if (style & PRIMARY_MASK) { 01941 if (Fl::focus() == this) background = selection_color(); 01942 else background = fl_color_average(color(), selection_color(), 0.4f); 01943 } else if (style & HIGHLIGHT_MASK) { 01944 if (Fl::focus() == this) background = fl_color_average(color(), selection_color(), 0.5f); 01945 else background = fl_color_average(color(), selection_color(), 0.6f); 01946 } else background = color(); 01947 foreground = fl_contrast(styleRec->color, background); 01948 } else if (style & PRIMARY_MASK) { 01949 if (Fl::focus() == this) background = selection_color(); 01950 else background = fl_color_average(color(), selection_color(), 0.4f); 01951 foreground = fl_contrast(textcolor(), background); 01952 } else if (style & HIGHLIGHT_MASK) { 01953 if (Fl::focus() == this) background = fl_color_average(color(), selection_color(), 0.5f); 01954 else background = fl_color_average(color(), selection_color(), 0.6f); 01955 foreground = fl_contrast(textcolor(), background); 01956 } else { 01957 foreground = textcolor(); 01958 background = color(); 01959 } 01960 01961 if (!(style & TEXT_ONLY_MASK)) { 01962 fl_color( background ); 01963 fl_rectf( X, Y, toX - X, mMaxsize ); 01964 } 01965 if (!(style & BG_ONLY_MASK)) { 01966 fl_color( foreground ); 01967 fl_font( font, fsize ); 01968 #if !(defined(__APPLE__) || defined(WIN32)) && USE_XFT 01969 // makes sure antialiased ÄÖÜ do not leak on line above 01970 fl_push_clip(X, Y, toX - X, mMaxsize); 01971 #endif 01972 fl_draw( string, nChars, X, Y + mMaxsize - fl_descent()); 01973 #if !(defined(__APPLE__) || defined(WIN32)) && USE_XFT 01974 fl_pop_clip(); 01975 #endif 01976 } 01977 01978 // CET - FIXME 01979 /* If any space around the character remains unfilled (due to use of 01980 different sized fonts for highlighting), fill in above or below 01981 to erase previously drawn characters */ 01982 /* 01983 if (fs->ascent < mAscent) 01984 clear_rect( style, X, Y, toX - X, mAscent - fs->ascent); 01985 if (fs->descent < mDescent) 01986 clear_rect( style, X, Y + mAscent + fs->descent, toX - x, 01987 mDescent - fs->descent); 01988 */ 01989 /* Underline if style is secondary Fl_Text_Selection */ 01990 01991 /* 01992 if (style & SECONDARY_MASK) 01993 XDrawLine(XtDisplay(mW), XtWindow(mW), gc, x, 01994 y + mAscent, toX - 1, Y + fs->ascent); 01995 */ 01996 } 01997 01998 01999 02006 void Fl_Text_Display::clear_rect(int style, 02007 int X, int Y, 02008 int width, int height) const { 02009 /* A width of zero means "clear to end of window" to XClearArea */ 02010 if ( width == 0 ) 02011 return; 02012 02013 if (style & PRIMARY_MASK) { 02014 if (Fl::focus()==this) { 02015 fl_color(selection_color()); 02016 } else { 02017 fl_color(fl_color_average(color(), selection_color(), 0.4f)); 02018 } 02019 } else if (style & HIGHLIGHT_MASK) { 02020 if (Fl::focus()==this) { 02021 fl_color(fl_color_average(color(), selection_color(), 0.5f)); 02022 } else { 02023 fl_color(fl_color_average(color(), selection_color(), 0.6f)); 02024 } 02025 } else { 02026 fl_color( color() ); 02027 } 02028 fl_rectf( X, Y, width, height ); 02029 } 02030 02031 02032 02038 void Fl_Text_Display::draw_cursor( int X, int Y ) { 02039 02040 typedef struct { 02041 int x1, y1, x2, y2; 02042 } 02043 Segment; 02044 02045 Segment segs[ 5 ]; 02046 int left, right, cursorWidth, midY; 02047 // int fontWidth = mFontStruct->min_bounds.width, nSegs = 0; 02048 int fontWidth = TMPFONTWIDTH; // CET - FIXME 02049 int nSegs = 0; 02050 int fontHeight = mMaxsize; 02051 int bot = Y + fontHeight - 1; 02052 02053 if ( X < text_area.x - 1 || X > text_area.x + text_area.w ) 02054 return; 02055 02056 /* For cursors other than the block, make them around 2/3 of a character 02057 width, rounded to an even number of pixels so that X will draw an 02058 odd number centered on the stem at x. */ 02059 cursorWidth = 4; //(fontWidth/3) * 2; 02060 left = X - cursorWidth / 2; 02061 right = left + cursorWidth; 02062 02063 /* Create segments and draw cursor */ 02064 if ( mCursorStyle == CARET_CURSOR ) { 02065 midY = bot - fontHeight / 5; 02066 segs[ 0 ].x1 = left; segs[ 0 ].y1 = bot; segs[ 0 ].x2 = X; segs[ 0 ].y2 = midY; 02067 segs[ 1 ].x1 = X; segs[ 1 ].y1 = midY; segs[ 1 ].x2 = right; segs[ 1 ].y2 = bot; 02068 segs[ 2 ].x1 = left; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = midY - 1; 02069 segs[ 3 ].x1 = X; segs[ 3 ].y1 = midY - 1; segs[ 3 ].x2 = right; segs[ 3 ].y2 = bot; 02070 nSegs = 4; 02071 } else if ( mCursorStyle == NORMAL_CURSOR ) { 02072 segs[ 0 ].x1 = left; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = right; segs[ 0 ].y2 = Y; 02073 segs[ 1 ].x1 = X; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = X; segs[ 1 ].y2 = bot; 02074 segs[ 2 ].x1 = left; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = right; segs[ 2 ].y2 = bot; 02075 nSegs = 3; 02076 } else if ( mCursorStyle == HEAVY_CURSOR ) { 02077 segs[ 0 ].x1 = X - 1; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = X - 1; segs[ 0 ].y2 = bot; 02078 segs[ 1 ].x1 = X; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = X; segs[ 1 ].y2 = bot; 02079 segs[ 2 ].x1 = X + 1; segs[ 2 ].y1 = Y; segs[ 2 ].x2 = X + 1; segs[ 2 ].y2 = bot; 02080 segs[ 3 ].x1 = left; segs[ 3 ].y1 = Y; segs[ 3 ].x2 = right; segs[ 3 ].y2 = Y; 02081 segs[ 4 ].x1 = left; segs[ 4 ].y1 = bot; segs[ 4 ].x2 = right; segs[ 4 ].y2 = bot; 02082 nSegs = 5; 02083 } else if ( mCursorStyle == DIM_CURSOR ) { 02084 midY = Y + fontHeight / 2; 02085 segs[ 0 ].x1 = X; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = X; segs[ 0 ].y2 = Y; 02086 segs[ 1 ].x1 = X; segs[ 1 ].y1 = midY; segs[ 1 ].x2 = X; segs[ 1 ].y2 = midY; 02087 segs[ 2 ].x1 = X; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = bot; 02088 nSegs = 3; 02089 } else if ( mCursorStyle == BLOCK_CURSOR ) { 02090 right = X + fontWidth; 02091 segs[ 0 ].x1 = X; segs[ 0 ].y1 = Y; segs[ 0 ].x2 = right; segs[ 0 ].y2 = Y; 02092 segs[ 1 ].x1 = right; segs[ 1 ].y1 = Y; segs[ 1 ].x2 = right; segs[ 1 ].y2 = bot; 02093 segs[ 2 ].x1 = right; segs[ 2 ].y1 = bot; segs[ 2 ].x2 = X; segs[ 2 ].y2 = bot; 02094 segs[ 3 ].x1 = X; segs[ 3 ].y1 = bot; segs[ 3 ].x2 = X; segs[ 3 ].y2 = Y; 02095 nSegs = 4; 02096 } 02097 fl_color( mCursor_color ); 02098 02099 for ( int k = 0; k < nSegs; k++ ) { 02100 fl_line( segs[ k ].x1, segs[ k ].y1, segs[ k ].x2, segs[ k ].y2 ); 02101 } 02102 } 02103 02104 02105 02129 int Fl_Text_Display::position_style( int lineStartPos, int lineLen, int lineIndex) const 02130 { 02131 IS_UTF8_ALIGNED2(buffer(), lineStartPos) 02132 02133 Fl_Text_Buffer * buf = mBuffer; 02134 Fl_Text_Buffer *styleBuf = mStyleBuffer; 02135 int pos, style = 0; 02136 02137 if ( lineStartPos == -1 || buf == NULL ) 02138 return FILL_MASK; 02139 02140 pos = lineStartPos + min( lineIndex, lineLen ); 02141 02142 if ( lineIndex >= lineLen ) 02143 style = FILL_MASK; 02144 else if ( styleBuf != NULL ) { 02145 style = ( unsigned char ) styleBuf->byte_at( pos ); 02146 if (style == mUnfinishedStyle && mUnfinishedHighlightCB) { 02147 /* encountered "unfinished" style, trigger parsing */ 02148 (mUnfinishedHighlightCB)( pos, mHighlightCBArg); 02149 style = (unsigned char) styleBuf->byte_at( pos); 02150 } 02151 } 02152 if (buf->primary_selection()->includes(pos)) 02153 style |= PRIMARY_MASK; 02154 if (buf->highlight_selection()->includes(pos)) 02155 style |= HIGHLIGHT_MASK; 02156 if (buf->secondary_selection()->includes(pos)) 02157 style |= SECONDARY_MASK; 02158 return style; 02159 } 02160 02161 02170 double Fl_Text_Display::string_width( const char *string, int length, int style ) const { 02171 IS_UTF8_ALIGNED(string) 02172 02173 Fl_Font font; 02174 Fl_Fontsize fsize; 02175 02176 if ( mNStyles && (style & STYLE_LOOKUP_MASK) ) { 02177 int si = (style & STYLE_LOOKUP_MASK) - 'A'; 02178 if (si < 0) si = 0; 02179 else if (si >= mNStyles) si = mNStyles - 1; 02180 02181 font = mStyleTable[si].font; 02182 fsize = mStyleTable[si].size; 02183 } else { 02184 font = textfont(); 02185 fsize = textsize(); 02186 } 02187 fl_font( font, fsize ); 02188 return fl_width( string, length ); 02189 } 02190 02191 02192 02206 int Fl_Text_Display::xy_to_position( int X, int Y, int posType ) const { 02207 int lineStart, lineLen, fontHeight; 02208 int visLineNum; 02209 02210 /* Find the visible line number corresponding to the Y coordinate */ 02211 fontHeight = mMaxsize; 02212 visLineNum = ( Y - text_area.y ) / fontHeight; 02213 if ( visLineNum < 0 ) 02214 return mFirstChar; 02215 if ( visLineNum >= mNVisibleLines ) 02216 visLineNum = mNVisibleLines - 1; 02217 02218 /* Find the position at the start of the line */ 02219 lineStart = mLineStarts[ visLineNum ]; 02220 02221 /* If the line start was empty, return the last position in the buffer */ 02222 if ( lineStart == -1 ) 02223 return mBuffer->length(); 02224 02225 /* Get the line text and its length */ 02226 lineLen = vline_length( visLineNum ); 02227 02228 return handle_vline(FIND_INDEX, 02229 lineStart, lineLen, 0, 0, 02230 0, 0, 02231 text_area.x, X); 02232 } 02233 02234 02235 02250 void Fl_Text_Display::xy_to_rowcol( int X, int Y, int *row, 02251 int *column, int posType ) const { 02252 int fontHeight = mMaxsize; 02253 int fontWidth = TMPFONTWIDTH; //mFontStruct->max_bounds.width; 02254 02255 /* Find the visible line number corresponding to the Y coordinate */ 02256 *row = ( Y - text_area.y ) / fontHeight; 02257 if ( *row < 0 ) *row = 0; 02258 if ( *row >= mNVisibleLines ) *row = mNVisibleLines - 1; 02259 02260 *column = ( ( X - text_area.x ) + mHorizOffset + 02261 ( posType == CURSOR_POS ? fontWidth / 2 : 0 ) ) / fontWidth; 02262 if ( *column < 0 ) * column = 0; 02263 } 02264 02265 02266 02278 void Fl_Text_Display::offset_line_starts( int newTopLineNum ) { 02279 int oldTopLineNum = mTopLineNum; 02280 int oldFirstChar = mFirstChar; 02281 int lineDelta = newTopLineNum - oldTopLineNum; 02282 int nVisLines = mNVisibleLines; 02283 int *lineStarts = mLineStarts; 02284 int i, lastLineNum; 02285 Fl_Text_Buffer *buf = mBuffer; 02286 02287 /* If there was no offset, nothing needs to be changed */ 02288 if ( lineDelta == 0 ) 02289 return; 02290 02291 /* Find the new value for mFirstChar by counting lines from the nearest 02292 known line start (start or end of buffer, or the closest value in the 02293 lineStarts array) */ 02294 lastLineNum = oldTopLineNum + nVisLines - 1; 02295 if ( newTopLineNum < oldTopLineNum && newTopLineNum < -lineDelta ) { 02296 mFirstChar = skip_lines( 0, newTopLineNum - 1, true ); 02297 } else if ( newTopLineNum < oldTopLineNum ) { 02298 mFirstChar = rewind_lines( mFirstChar, -lineDelta ); 02299 } else if ( newTopLineNum < lastLineNum ) { 02300 mFirstChar = lineStarts[ newTopLineNum - oldTopLineNum ]; 02301 } else if ( newTopLineNum - lastLineNum < mNBufferLines - newTopLineNum ) { 02302 mFirstChar = skip_lines( lineStarts[ nVisLines - 1 ], 02303 newTopLineNum - lastLineNum, true ); 02304 } else { 02305 mFirstChar = rewind_lines( buf->length(), mNBufferLines - newTopLineNum + 1 ); 02306 } 02307 02308 /* Fill in the line starts array */ 02309 if ( lineDelta < 0 && -lineDelta < nVisLines ) { 02310 for ( i = nVisLines - 1; i >= -lineDelta; i-- ) 02311 lineStarts[ i ] = lineStarts[ i + lineDelta ]; 02312 calc_line_starts( 0, -lineDelta ); 02313 } else if ( lineDelta > 0 && lineDelta < nVisLines ) { 02314 for ( i = 0; i < nVisLines - lineDelta; i++ ) 02315 lineStarts[ i ] = lineStarts[ i + lineDelta ]; 02316 calc_line_starts( nVisLines - lineDelta, nVisLines - 1 ); 02317 } else 02318 calc_line_starts( 0, nVisLines ); 02319 02320 /* Set lastChar and mTopLineNum */ 02321 calc_last_char(); 02322 mTopLineNum = newTopLineNum; 02323 02324 /* If we're numbering lines or being asked to maintain an absolute line 02325 number, re-calculate the absolute line number */ 02326 absolute_top_line_number(oldFirstChar); 02327 } 02328 02329 02330 02346 void Fl_Text_Display::update_line_starts(int pos, int charsInserted, 02347 int charsDeleted, int linesInserted, 02348 int linesDeleted, int *scrolled ) { 02349 IS_UTF8_ALIGNED2(buffer(), pos) 02350 02351 int *lineStarts = mLineStarts; 02352 int i, lineOfPos, lineOfEnd, nVisLines = mNVisibleLines; 02353 int charDelta = charsInserted - charsDeleted; 02354 int lineDelta = linesInserted - linesDeleted; 02355 02356 /* If all of the changes were before the displayed text, the display 02357 doesn't change, just update the top line num and offset the line 02358 start entries and first and last characters */ 02359 if ( pos + charsDeleted < mFirstChar ) { 02360 mTopLineNum += lineDelta; 02361 for ( i = 0; i < nVisLines && lineStarts[i] != -1; i++ ) 02362 lineStarts[ i ] += charDelta; 02363 mFirstChar += charDelta; 02364 mLastChar += charDelta; 02365 *scrolled = 0; 02366 return; 02367 } 02368 02369 /* The change began before the beginning of the displayed text, but 02370 part or all of the displayed text was deleted */ 02371 if ( pos < mFirstChar ) { 02372 /* If some text remains in the window, anchor on that */ 02373 if ( position_to_line( pos + charsDeleted, &lineOfEnd ) && 02374 ++lineOfEnd < nVisLines && lineStarts[ lineOfEnd ] != -1 ) { 02375 mTopLineNum = max( 1, mTopLineNum + lineDelta ); 02376 mFirstChar = rewind_lines(lineStarts[ lineOfEnd ] + charDelta, lineOfEnd ); 02377 /* Otherwise anchor on original line number and recount everything */ 02378 } else { 02379 if ( mTopLineNum > mNBufferLines + lineDelta ) { 02380 mTopLineNum = 1; 02381 mFirstChar = 0; 02382 } else 02383 mFirstChar = skip_lines( 0, mTopLineNum - 1, true ); 02384 } 02385 calc_line_starts( 0, nVisLines - 1 ); 02386 /* calculate lastChar by finding the end of the last displayed line */ 02387 calc_last_char(); 02388 *scrolled = 1; 02389 return; 02390 } 02391 02392 /* If the change was in the middle of the displayed text (it usually is), 02393 salvage as much of the line starts array as possible by moving and 02394 offsetting the entries after the changed area, and re-counting the 02395 added lines or the lines beyond the salvaged part of the line starts 02396 array */ 02397 if ( pos <= mLastChar ) { 02398 /* find line on which the change began */ 02399 position_to_line( pos, &lineOfPos ); 02400 /* salvage line starts after the changed area */ 02401 if ( lineDelta == 0 ) { 02402 for ( i = lineOfPos + 1; i < nVisLines && lineStarts[ i ] != -1; i++ ) 02403 lineStarts[ i ] += charDelta; 02404 } else if ( lineDelta > 0 ) { 02405 for ( i = nVisLines - 1; i >= lineOfPos + lineDelta + 1; i-- ) 02406 lineStarts[ i ] = lineStarts[ i - lineDelta ] + 02407 ( lineStarts[ i - lineDelta ] == -1 ? 0 : charDelta ); 02408 } else /* (lineDelta < 0) */ { 02409 for ( i = max( 0, lineOfPos + 1 ); i < nVisLines + lineDelta; i++ ) 02410 lineStarts[ i ] = lineStarts[ i - lineDelta ] + 02411 ( lineStarts[ i - lineDelta ] == -1 ? 0 : charDelta ); 02412 } 02413 /* fill in the missing line starts */ 02414 if ( linesInserted >= 0 ) 02415 calc_line_starts( lineOfPos + 1, lineOfPos + linesInserted ); 02416 if ( lineDelta < 0 ) 02417 calc_line_starts( nVisLines + lineDelta, nVisLines ); 02418 /* calculate lastChar by finding the end of the last displayed line */ 02419 calc_last_char(); 02420 *scrolled = 0; 02421 return; 02422 } 02423 02424 /* Change was past the end of the displayed text, but displayable by virtue 02425 of being an insert at the end of the buffer into visible blank lines */ 02426 if ( empty_vlines() ) { 02427 position_to_line( pos, &lineOfPos ); 02428 calc_line_starts( lineOfPos, lineOfPos + linesInserted ); 02429 calc_last_char(); 02430 *scrolled = 0; 02431 return; 02432 } 02433 02434 /* Change was beyond the end of the buffer and not visible, do nothing */ 02435 *scrolled = 0; 02436 } 02437 02438 02439 02452 void Fl_Text_Display::calc_line_starts( int startLine, int endLine ) { 02453 int startPos, bufLen = mBuffer->length(); 02454 int line, lineEnd, nextLineStart, nVis = mNVisibleLines; 02455 int *lineStarts = mLineStarts; 02456 02457 /* Clean up (possibly) messy input parameters */ 02458 if ( endLine < 0 ) endLine = 0; 02459 if ( endLine >= nVis ) endLine = nVis - 1; 02460 if ( startLine < 0 ) startLine = 0; 02461 if ( startLine >= nVis ) startLine = nVis - 1; 02462 if ( startLine > endLine ) 02463 return; 02464 02465 /* Find the last known good line number -> position mapping */ 02466 if ( startLine == 0 ) { 02467 lineStarts[ 0 ] = mFirstChar; 02468 startLine = 1; 02469 } 02470 startPos = lineStarts[ startLine - 1 ]; 02471 02472 /* If the starting position is already past the end of the text, 02473 fill in -1's (means no text on line) and return */ 02474 if ( startPos == -1 ) { 02475 for ( line = startLine; line <= endLine; line++ ) 02476 lineStarts[ line ] = -1; 02477 return; 02478 } 02479 02480 /* Loop searching for ends of lines and storing the positions of the 02481 start of the next line in lineStarts */ 02482 for ( line = startLine; line <= endLine; line++ ) { 02483 find_line_end(startPos, true, &lineEnd, &nextLineStart); 02484 startPos = nextLineStart; 02485 if ( startPos >= bufLen ) { 02486 /* If the buffer ends with a newline or line break, put 02487 buf->length() in the next line start position (instead of 02488 a -1 which is the normal marker for an empty line) to 02489 indicate that the cursor may safely be displayed there */ 02490 if ( line == 0 || ( lineStarts[ line - 1 ] != bufLen && 02491 lineEnd != nextLineStart ) ) { 02492 lineStarts[ line ] = bufLen; 02493 line++; 02494 } 02495 break; 02496 } 02497 lineStarts[ line ] = startPos; 02498 } 02499 02500 /* Set any entries beyond the end of the text to -1 */ 02501 for ( ; line <= endLine; line++ ) 02502 lineStarts[ line ] = -1; 02503 } 02504 02505 02506 02513 void Fl_Text_Display::calc_last_char() { 02514 int i; 02515 for (i = mNVisibleLines - 1; i >= 0 && mLineStarts[i] == -1; i--) ; 02516 mLastChar = i < 0 ? 0 : line_end(mLineStarts[i], true); 02517 } 02518 02519 02520 02527 void Fl_Text_Display::scroll(int topLineNum, int horizOffset) { 02528 mTopLineNumHint = topLineNum; 02529 mHorizOffsetHint = horizOffset; 02530 resize(x(), y(), w(), h()); 02531 } 02532 02533 02534 02541 int Fl_Text_Display::scroll_(int topLineNum, int horizOffset) { 02542 /* Limit the requested scroll position to allowable values */ 02543 if (topLineNum > mNBufferLines + 3 - mNVisibleLines) 02544 topLineNum = mNBufferLines + 3 - mNVisibleLines; 02545 if (topLineNum < 1) topLineNum = 1; 02546 02547 if (horizOffset > longest_vline() - text_area.w) 02548 horizOffset = longest_vline() - text_area.w; 02549 if (horizOffset < 0) horizOffset = 0; 02550 02551 /* Do nothing if scroll position hasn't actually changed or there's no 02552 window to draw in yet */ 02553 if (mHorizOffset == horizOffset && mTopLineNum == topLineNum) 02554 return 0; 02555 02556 /* If the vertical scroll position has changed, update the line 02557 starts array and related counters in the text display */ 02558 offset_line_starts(topLineNum); 02559 02560 /* Just setting mHorizOffset is enough information for redisplay */ 02561 mHorizOffset = horizOffset; 02562 02563 // redraw all text 02564 damage(FL_DAMAGE_EXPOSE); 02565 return 1; 02566 } 02567 02568 02569 02576 void Fl_Text_Display::update_v_scrollbar() { 02577 /* The vertical scrollbar value and slider size directly represent the top 02578 line number, and the number of visible lines respectively. The scroll 02579 bar maximum value is chosen to generally represent the size of the whole 02580 buffer, with minor adjustments to keep the scrollbar widget happy */ 02581 #ifdef DEBUG 02582 printf("Fl_Text_Display::update_v_scrollbar():\n" 02583 " mTopLineNum=%d, mNVisibleLines=%d, mNBufferLines=%d\n", 02584 mTopLineNum, mNVisibleLines, mNBufferLines); 02585 #endif // DEBUG 02586 02587 mVScrollBar->value(mTopLineNum, mNVisibleLines, 1, mNBufferLines+2); 02588 mVScrollBar->linesize(3); 02589 } 02590 02591 02598 void Fl_Text_Display::update_h_scrollbar() { 02599 int sliderMax = max(longest_vline(), text_area.w + mHorizOffset); 02600 mHScrollBar->value( mHorizOffset, text_area.w, 0, sliderMax ); 02601 } 02602 02603 02604 02608 void Fl_Text_Display::v_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) { 02609 if (b->value() == textD->mTopLineNum) return; 02610 textD->scroll(b->value(), textD->mHorizOffset); 02611 } 02612 02613 02614 02618 void Fl_Text_Display::h_scrollbar_cb(Fl_Scrollbar* b, Fl_Text_Display* textD) { 02619 if (b->value() == textD->mHorizOffset) return; 02620 textD->scroll(textD->mTopLineNum, b->value()); 02621 } 02622 02623 02624 02634 void Fl_Text_Display::draw_line_numbers(bool /*clearAll*/) { 02635 #if 0 02636 int y, line, visLine, nCols, lineStart; 02637 char lineNumString[12]; 02638 int lineHeight = mMaxsize ? mMaxsize : textsize_; 02639 int charWidth = TMPFONTWIDTH; //mFontStruct->max_bounds.width; 02640 02641 /* Don't draw if mLineNumWidth == 0 (line numbers are hidden), or widget is 02642 not yet realized */ 02643 if (mLineNumWidth == 0 || visible_r()) 02644 return; 02645 02646 /* GC is allocated on demand, since not everyone will use line numbering */ 02647 if (textD->lineNumGC == NULL) { 02648 XGCValues values; 02649 values.foreground = textD->lineNumFGPixel; 02650 values.background = textD->bgPixel; 02651 values.font = textD->fontStruct->fid; 02652 textD->lineNumGC = XtGetGC(textD->w, 02653 GCFont| GCForeground | GCBackground, &values); 02654 } 02655 02656 /* Erase the previous contents of the line number area, if requested */ 02657 if (clearAll) 02658 XClearArea(XtDisplay(textD->w), XtWindow(textD->w), textD->lineNumLeft, 02659 textD->top, textD->lineNumWidth, textD->height, False); 02660 02661 /* Draw the line numbers, aligned to the text */ 02662 nCols = min(11, textD->lineNumWidth / charWidth); 02663 y = textD->top; 02664 line = getAbsTopLineNum(textD); 02665 for (visLine=0; visLine < textD->nVisibleLines; visLine++) { 02666 lineStart = textD->lineStarts[visLine]; 02667 if (lineStart != -1 && (lineStart==0 || 02668 BufGetCharacter(textD->buffer, lineStart-1)=='\n')) { 02669 sprintf(lineNumString, "%*d", nCols, line); 02670 XDrawImageString(XtDisplay(textD->w), XtWindow(textD->w), 02671 textD->lineNumGC, textD->lineNumLeft, y + textD->ascent, 02672 lineNumString, strlen(lineNumString)); 02673 line++; 02674 } else { 02675 XClearArea(XtDisplay(textD->w), XtWindow(textD->w), 02676 textD->lineNumLeft, y, textD->lineNumWidth, 02677 textD->ascent + textD->descent, False); 02678 if (visLine == 0) 02679 line++; 02680 } 02681 y += lineHeight; 02682 } 02683 #endif 02684 } 02685 02686 static int max( int i1, int i2 ) { 02687 return i1 >= i2 ? i1 : i2; 02688 } 02689 02690 static int min( int i1, int i2 ) { 02691 return i1 <= i2 ? i1 : i2; 02692 } 02693 02694 02695 02699 static int countlines( const char *string ) { 02700 IS_UTF8_ALIGNED(string) 02701 02702 const char * c; 02703 int lineCount = 0; 02704 02705 if (!string) return 0; 02706 02707 for ( c = string; *c != '\0'; c++ ) 02708 if ( *c == '\n' ) lineCount++; 02709 return lineCount; 02710 } 02711 02712 02713 02714 02720 int Fl_Text_Display::measure_vline( int visLineNum ) const { 02721 int lineLen = vline_length( visLineNum ); 02722 int lineStartPos = mLineStarts[ visLineNum ]; 02723 if (lineStartPos < 0 || lineLen == 0) return 0; 02724 return handle_vline(GET_WIDTH, lineStartPos, lineLen, 0, 0, 0, 0, 0, 0); 02725 } 02726 02727 02728 02733 int Fl_Text_Display::empty_vlines() const { 02734 return (mNVisibleLines > 0) && (mLineStarts[ mNVisibleLines - 1 ] == -1); 02735 } 02736 02737 02738 02748 int Fl_Text_Display::vline_length( int visLineNum ) const { 02749 int nextLineStart, lineStartPos; 02750 02751 if (visLineNum < 0 || visLineNum >= mNVisibleLines) 02752 return (0); 02753 02754 lineStartPos = mLineStarts[ visLineNum ]; 02755 02756 if ( lineStartPos == -1 ) 02757 return 0; 02758 02759 if ( visLineNum + 1 >= mNVisibleLines ) 02760 return mLastChar - lineStartPos; 02761 02762 nextLineStart = mLineStarts[ visLineNum + 1 ]; 02763 if ( nextLineStart == -1 ) 02764 return mLastChar - lineStartPos; 02765 02766 int nextLineStartMinus1 = buffer()->prev_char(nextLineStart); 02767 if (wrap_uses_character(nextLineStartMinus1)) 02768 return nextLineStartMinus1 - lineStartPos; 02769 02770 return nextLineStart - lineStartPos; 02771 } 02772 02773 02774 02795 void Fl_Text_Display::find_wrap_range(const char *deletedText, int pos, 02796 int nInserted, int nDeleted, 02797 int *modRangeStart, int *modRangeEnd, 02798 int *linesInserted, int *linesDeleted) { 02799 IS_UTF8_ALIGNED(deletedText) 02800 IS_UTF8_ALIGNED2(buffer(), pos) 02801 02802 int length, retPos, retLines, retLineStart, retLineEnd; 02803 Fl_Text_Buffer *deletedTextBuf, *buf = buffer(); 02804 int nVisLines = mNVisibleLines; 02805 int *lineStarts = mLineStarts; 02806 int countFrom, countTo, lineStart, adjLineStart, i; 02807 int visLineNum = 0, nLines = 0; 02808 02809 /* 02810 ** Determine where to begin searching: either the previous newline, or 02811 ** if possible, limit to the start of the (original) previous displayed 02812 ** line, using information from the existing line starts array 02813 */ 02814 if (pos >= mFirstChar && pos <= mLastChar) { 02815 for (i=nVisLines-1; i>0; i--) { 02816 if (lineStarts[i] != -1 && pos >= lineStarts[i]) { 02817 break; 02818 } 02819 } 02820 if (i > 0) { 02821 countFrom = lineStarts[i-1]; 02822 visLineNum = i-1; 02823 } else { 02824 countFrom = buf->line_start(pos); 02825 } 02826 } else { 02827 countFrom = buf->line_start(pos); 02828 } 02829 02830 IS_UTF8_ALIGNED2(buf, countFrom) 02831 02832 /* 02833 ** Move forward through the (new) text one line at a time, counting 02834 ** displayed lines, and looking for either a real newline, or for the 02835 ** line starts to re-sync with the original line starts array 02836 */ 02837 lineStart = countFrom; 02838 *modRangeStart = countFrom; 02839 for (;;) { 02840 02841 /* advance to the next line. If the line ended in a real newline 02842 or the end of the buffer, that's far enough */ 02843 wrapped_line_counter(buf, lineStart, buf->length(), 1, true, 0, 02844 &retPos, &retLines, &retLineStart, &retLineEnd); 02845 if (retPos >= buf->length()) { 02846 countTo = buf->length(); 02847 *modRangeEnd = countTo; 02848 if (retPos != retLineEnd) 02849 nLines++; 02850 break; 02851 } else { 02852 lineStart = retPos; 02853 } 02854 nLines++; 02855 if (lineStart > pos + nInserted && buf->char_at(buf->prev_char(lineStart)) == '\n') { 02856 countTo = lineStart; 02857 *modRangeEnd = lineStart; 02858 break; 02859 } 02860 02861 /* Don't try to resync in continuous wrap mode with non-fixed font 02862 sizes; it would result in a chicken-and-egg dependency between 02863 the calculations for the inserted and the deleted lines. 02864 If we're in that mode, the number of deleted lines is calculated in 02865 advance, without resynchronization, so we shouldn't resynchronize 02866 for the inserted lines either. */ 02867 if (mSuppressResync) 02868 continue; 02869 02870 /* check for synchronization with the original line starts array 02871 before pos, if so, the modified range can begin later */ 02872 if (lineStart <= pos) { 02873 while (visLineNum<nVisLines && lineStarts[visLineNum] < lineStart) 02874 visLineNum++; 02875 if (visLineNum < nVisLines && lineStarts[visLineNum] == lineStart) { 02876 countFrom = lineStart; 02877 nLines = 0; 02878 if (visLineNum+1 < nVisLines && lineStarts[visLineNum+1] != -1) 02879 *modRangeStart = min(pos, buf->prev_char(lineStarts[visLineNum+1])); 02880 else 02881 *modRangeStart = countFrom; 02882 } else 02883 *modRangeStart = min(*modRangeStart, buf->prev_char(lineStart)); 02884 } 02885 02886 /* check for synchronization with the original line starts array 02887 after pos, if so, the modified range can end early */ 02888 else if (lineStart > pos + nInserted) { 02889 adjLineStart = lineStart - nInserted + nDeleted; 02890 while (visLineNum<nVisLines && lineStarts[visLineNum]<adjLineStart) 02891 visLineNum++; 02892 if (visLineNum < nVisLines && lineStarts[visLineNum] != -1 && 02893 lineStarts[visLineNum] == adjLineStart) { 02894 countTo = line_end(lineStart, true); 02895 *modRangeEnd = lineStart; 02896 break; 02897 } 02898 } 02899 } 02900 *linesInserted = nLines; 02901 02902 02903 /* Count deleted lines between countFrom and countTo as the text existed 02904 before the modification (that is, as if the text between pos and 02905 pos+nInserted were replaced by "deletedText"). This extra context is 02906 necessary because wrapping can occur outside of the modified region 02907 as a result of adding or deleting text in the region. This is done by 02908 creating a textBuffer containing the deleted text and the necessary 02909 additional context, and calling the wrappedLineCounter on it. 02910 02911 NOTE: This must not be done in continuous wrap mode when the font 02912 width is not fixed. In that case, the calculation would try 02913 to access style information that is no longer available (deleted 02914 text), or out of date (updated highlighting), possibly leading 02915 to completely wrong calculations and/or even crashes eventually. 02916 (This is not theoretical; it really happened.) 02917 02918 In that case, the calculation of the number of deleted lines 02919 has happened before the buffer was modified (only in that case, 02920 because resynchronization of the line starts is impossible 02921 in that case, which makes the whole calculation less efficient). 02922 */ 02923 if (mSuppressResync) { 02924 *linesDeleted = mNLinesDeleted; 02925 mSuppressResync = 0; 02926 return; 02927 } 02928 02929 length = (pos-countFrom) + nDeleted +(countTo-(pos+nInserted)); 02930 deletedTextBuf = new Fl_Text_Buffer(length); 02931 deletedTextBuf->copy(buffer(), countFrom, pos, 0); 02932 if (nDeleted != 0) 02933 deletedTextBuf->insert(pos-countFrom, deletedText); 02934 deletedTextBuf->copy(buffer(), pos+nInserted, countTo, pos-countFrom+nDeleted); 02935 /* Note that we need to take into account an offset for the style buffer: 02936 the deletedTextBuf can be out of sync with the style buffer. */ 02937 wrapped_line_counter(deletedTextBuf, 0, length, INT_MAX, true, countFrom, 02938 &retPos, &retLines, &retLineStart, &retLineEnd, false); 02939 delete deletedTextBuf; 02940 *linesDeleted = retLines; 02941 mSuppressResync = 0; 02942 } 02943 02944 02945 02963 void Fl_Text_Display::measure_deleted_lines(int pos, int nDeleted) { 02964 IS_UTF8_ALIGNED2(buffer(), pos) 02965 02966 int retPos, retLines, retLineStart, retLineEnd; 02967 Fl_Text_Buffer *buf = buffer(); 02968 int nVisLines = mNVisibleLines; 02969 int *lineStarts = mLineStarts; 02970 int countFrom, lineStart; 02971 int visLineNum = 0, nLines = 0, i; 02972 /* 02973 ** Determine where to begin searching: either the previous newline, or 02974 ** if possible, limit to the start of the (original) previous displayed 02975 ** line, using information from the existing line starts array 02976 */ 02977 if (pos >= mFirstChar && pos <= mLastChar) { 02978 for (i=nVisLines-1; i>0; i--) 02979 if (lineStarts[i] != -1 && pos >= lineStarts[i]) 02980 break; 02981 if (i > 0) { 02982 countFrom = lineStarts[i-1]; 02983 visLineNum = i-1; 02984 } else 02985 countFrom = buf->line_start(pos); 02986 } else 02987 countFrom = buf->line_start(pos); 02988 02989 /* 02990 ** Move forward through the (new) text one line at a time, counting 02991 ** displayed lines, and looking for either a real newline, or for the 02992 ** line starts to re-sync with the original line starts array 02993 */ 02994 lineStart = countFrom; 02995 for (;;) { 02996 /* advance to the next line. If the line ended in a real newline 02997 or the end of the buffer, that's far enough */ 02998 wrapped_line_counter(buf, lineStart, buf->length(), 1, true, 0, 02999 &retPos, &retLines, &retLineStart, &retLineEnd); 03000 if (retPos >= buf->length()) { 03001 if (retPos != retLineEnd) 03002 nLines++; 03003 break; 03004 } else 03005 lineStart = retPos; 03006 nLines++; 03007 if (lineStart > pos + nDeleted && buf->char_at(lineStart-1) == '\n') { 03008 break; 03009 } 03010 03011 /* Unlike in the findWrapRange() function above, we don't try to 03012 resync with the line starts, because we don't know the length 03013 of the inserted text yet, nor the updated style information. 03014 03015 Because of that, we also shouldn't resync with the line starts 03016 after the modification either, because we must perform the 03017 calculations for the deleted and inserted lines in the same way. 03018 03019 This can result in some unnecessary recalculation and redrawing 03020 overhead, and therefore we should only use this two-phase mode 03021 of calculation when it's really needed (continuous wrap + variable 03022 font width). */ 03023 } 03024 mNLinesDeleted = nLines; 03025 mSuppressResync = 1; 03026 } 03027 03028 03029 03055 void Fl_Text_Display::wrapped_line_counter(Fl_Text_Buffer *buf, int startPos, 03056 int maxPos, int maxLines, bool startPosIsLineStart, int styleBufOffset, 03057 int *retPos, int *retLines, int *retLineStart, int *retLineEnd, 03058 bool countLastLineMissingNewLine) const { 03059 IS_UTF8_ALIGNED2(buf, startPos) 03060 IS_UTF8_ALIGNED2(buf, maxPos) 03061 03062 int lineStart, newLineStart = 0, b, p, colNum, wrapMarginPix; 03063 int i, foundBreak; 03064 double width; 03065 int nLines = 0; 03066 unsigned int c; 03067 03068 /* Set the wrap margin to the wrap column or the view width */ 03069 if (mWrapMarginPix != 0) { 03070 wrapMarginPix = mWrapMarginPix; 03071 } else { 03072 wrapMarginPix = text_area.w; 03073 } 03074 03075 /* Find the start of the line if the start pos is not marked as a 03076 line start. */ 03077 if (startPosIsLineStart) 03078 lineStart = startPos; 03079 else 03080 lineStart = line_start(startPos); 03081 03082 /* 03083 ** Loop until position exceeds maxPos or line count exceeds maxLines. 03084 ** (actually, continues beyond maxPos to end of line containing maxPos, 03085 ** in case later characters cause a word wrap back before maxPos) 03086 */ 03087 colNum = 0; 03088 width = 0; 03089 for (p=lineStart; p<buf->length(); p=buf->next_char(p)) { 03090 c = buf->char_at(p); // UCS-4 03091 03092 /* If the character was a newline, count the line and start over, 03093 otherwise, add it to the width and column counts */ 03094 if (c == '\n') { 03095 if (p >= maxPos) { 03096 *retPos = maxPos; 03097 *retLines = nLines; 03098 *retLineStart = lineStart; 03099 *retLineEnd = maxPos; 03100 return; 03101 } 03102 nLines++; 03103 int p1 = buf->next_char(p); 03104 if (nLines >= maxLines) { 03105 *retPos = p1; 03106 *retLines = nLines; 03107 *retLineStart = p1; 03108 *retLineEnd = p; 03109 return; 03110 } 03111 lineStart = p1; 03112 colNum = 0; 03113 width = 0; 03114 } else { 03115 const char *s = buf->address(p); 03116 colNum++; 03117 // FIXME: it is not a good idea to simply add character widths because on 03118 // some platforms, the width is a floating point value and depends on the 03119 // previous character as well. 03120 width += measure_proportional_character(s, (int)width, p+styleBufOffset); 03121 } 03122 03123 /* If character exceeded wrap margin, find the break point and wrap there */ 03124 if (width > wrapMarginPix) { 03125 foundBreak = false; 03126 for (b=p; b>=lineStart; b=buf->prev_char(b)) { 03127 c = buf->char_at(b); 03128 if (c == '\t' || c == ' ') { 03129 newLineStart = buf->next_char(b); 03130 colNum = 0; 03131 width = 0; 03132 int iMax = buf->next_char(p); 03133 for (i=buf->next_char(b); i<iMax; i = buf->next_char(i)) { 03134 width += measure_proportional_character(buf->address(i), (int)width, 03135 i+styleBufOffset); 03136 colNum++; 03137 } 03138 foundBreak = true; 03139 break; 03140 } 03141 } 03142 if (!foundBreak) { /* no whitespace, just break at margin */ 03143 newLineStart = max(p, buf->next_char(lineStart)); 03144 const char *s = buf->address(b); 03145 colNum++; 03146 width = measure_proportional_character(s, 0, p+styleBufOffset); 03147 } 03148 if (p >= maxPos) { 03149 *retPos = maxPos; 03150 *retLines = maxPos < newLineStart ? nLines : nLines + 1; 03151 *retLineStart = maxPos < newLineStart ? lineStart : newLineStart; 03152 *retLineEnd = maxPos; 03153 return; 03154 } 03155 nLines++; 03156 if (nLines >= maxLines) { 03157 *retPos = foundBreak ? buf->next_char(b) : max(p, buf->next_char(lineStart)); 03158 *retLines = nLines; 03159 *retLineStart = lineStart; 03160 *retLineEnd = foundBreak ? b : p; 03161 return; 03162 } 03163 lineStart = newLineStart; 03164 } 03165 } 03166 03167 /* reached end of buffer before reaching pos or line target */ 03168 *retPos = buf->length(); 03169 *retLines = nLines; 03170 if (countLastLineMissingNewLine && colNum > 0) 03171 *retLines = buf->next_char(*retLines); 03172 *retLineStart = lineStart; 03173 *retLineEnd = buf->length(); 03174 } 03175 03176 03177 03199 double Fl_Text_Display::measure_proportional_character(const char *s, int xPix, int pos) const { 03200 IS_UTF8_ALIGNED(s) 03201 03202 if (*s=='\t') { 03203 int tab = (int)col_to_x(8); 03204 return (((xPix/tab)+1)*tab) - xPix; 03205 } 03206 03207 int charLen = fl_utf8len1(*s), style = 0; 03208 if (mStyleBuffer) { 03209 style = mStyleBuffer->byte_at(pos); 03210 } 03211 return string_width(s, charLen, style); 03212 } 03213 03214 03215 03233 void Fl_Text_Display::find_line_end(int startPos, bool startPosIsLineStart, 03234 int *lineEnd, int *nextLineStart) const { 03235 IS_UTF8_ALIGNED2(buffer(), startPos) 03236 03237 int retLines, retLineStart; 03238 03239 /* if we're not wrapping use more efficient BufEndOfLine */ 03240 if (!mContinuousWrap) { 03241 int le = buffer()->line_end(startPos); 03242 int ls = buffer()->next_char(le); 03243 *lineEnd = le; 03244 *nextLineStart = min(buffer()->length(), ls); 03245 return; 03246 } 03247 03248 /* use the wrapped line counter routine to count forward one line */ 03249 wrapped_line_counter(buffer(), startPos, buffer()->length(), 03250 1, startPosIsLineStart, 0, nextLineStart, &retLines, 03251 &retLineStart, lineEnd); 03252 } 03253 03254 03255 03277 int Fl_Text_Display::wrap_uses_character(int lineEndPos) const { 03278 IS_UTF8_ALIGNED2(buffer(), lineEndPos) 03279 03280 unsigned int c; 03281 03282 if (!mContinuousWrap || lineEndPos == buffer()->length()) 03283 return 1; 03284 03285 c = buffer()->char_at(lineEndPos); 03286 return c == '\n' || ((c == '\t' || c == ' ') && 03287 lineEndPos + 1 < buffer()->length()); 03288 } 03289 03290 03291 03304 void Fl_Text_Display::extend_range_for_styles( int *startpos, int *endpos ) { 03305 IS_UTF8_ALIGNED2(buffer(), (*startpos)) 03306 IS_UTF8_ALIGNED2(buffer(), (*endpos)) 03307 03308 Fl_Text_Selection * sel = mStyleBuffer->primary_selection(); 03309 int extended = 0; 03310 03311 /* The peculiar protocol used here is that modifications to the style 03312 buffer are marked by selecting them with the buffer's primary Fl_Text_Selection. 03313 The style buffer is usually modified in response to a modify callback on 03314 the text buffer BEFORE Fl_Text_Display.c's modify callback, so that it can keep 03315 the style buffer in step with the text buffer. The style-update 03316 callback can't just call for a redraw, because Fl_Text_Display hasn't processed 03317 the original text changes yet. Anyhow, to minimize redrawing and to 03318 avoid the complexity of scheduling redraws later, this simple protocol 03319 tells the text display's buffer modify callback to extend its redraw 03320 range to show the text color/and font changes as well. */ 03321 if ( sel->selected() ) { 03322 if ( sel->start() < *startpos ) { 03323 *startpos = sel->start(); 03324 // somewhere while deleting, alignment is lost. We do this just to be sure. 03325 *startpos = buffer()->utf8_align(*startpos); 03326 IS_UTF8_ALIGNED2(buffer(), (*startpos)) 03327 extended = 1; 03328 } 03329 if ( sel->end() > *endpos ) { 03330 *endpos = sel->end(); 03331 *endpos = buffer()->utf8_align(*endpos); 03332 IS_UTF8_ALIGNED2(buffer(), (*endpos)) 03333 extended = 1; 03334 } 03335 } 03336 03337 /* If the Fl_Text_Selection was extended due to a style change, and some of the 03338 fonts don't match in spacing, extend redraw area to end of line to 03339 redraw characters exposed by possible font size changes */ 03340 if ( extended ) 03341 *endpos = mBuffer->line_end( *endpos ) + 1; 03342 03343 IS_UTF8_ALIGNED2(buffer(), (*endpos)) 03344 } 03345 03346 03347 03353 void Fl_Text_Display::draw(void) { 03354 // don't even try if there is no associated text buffer! 03355 if (!buffer()) { draw_box(); return; } 03356 03357 fl_push_clip(x(),y(),w(),h()); // prevent drawing outside widget area 03358 03359 // draw the non-text, non-scrollbar areas. 03360 if (damage() & FL_DAMAGE_ALL) { 03361 // printf("drawing all (box = %d)\n", box()); 03362 // draw the box() 03363 int W = w(), H = h(); 03364 draw_box(box(), x(), y(), W, H, color()); 03365 03366 if (mHScrollBar->visible()) 03367 W -= scrollbar_width(); 03368 if (mVScrollBar->visible()) 03369 H -= scrollbar_width(); 03370 03371 // left margin 03372 fl_rectf(text_area.x-LEFT_MARGIN, text_area.y-TOP_MARGIN, 03373 LEFT_MARGIN, text_area.h+TOP_MARGIN+BOTTOM_MARGIN, 03374 color()); 03375 03376 // right margin 03377 fl_rectf(text_area.x+text_area.w, text_area.y-TOP_MARGIN, 03378 RIGHT_MARGIN, text_area.h+TOP_MARGIN+BOTTOM_MARGIN, 03379 color()); 03380 03381 // top margin 03382 fl_rectf(text_area.x, text_area.y-TOP_MARGIN, 03383 text_area.w, TOP_MARGIN, color()); 03384 03385 // bottom margin 03386 fl_rectf(text_area.x, text_area.y+text_area.h, 03387 text_area.w, BOTTOM_MARGIN, color()); 03388 03389 // draw that little box in the corner of the scrollbars 03390 if (mVScrollBar->visible() && mHScrollBar->visible()) 03391 fl_rectf(mVScrollBar->x(), mHScrollBar->y(), 03392 mVScrollBar->w(), mHScrollBar->h(), 03393 FL_GRAY); 03394 03395 // blank the previous cursor protrusions 03396 } 03397 else if (damage() & (FL_DAMAGE_SCROLL | FL_DAMAGE_EXPOSE)) { 03398 // printf("blanking previous cursor extrusions at Y: %d\n", mCursorOldY); 03399 // CET - FIXME - save old cursor position instead and just draw side needed? 03400 fl_push_clip(text_area.x-LEFT_MARGIN, 03401 text_area.y, 03402 text_area.w+LEFT_MARGIN+RIGHT_MARGIN, 03403 text_area.h); 03404 fl_rectf(text_area.x-LEFT_MARGIN, mCursorOldY, 03405 LEFT_MARGIN, mMaxsize, color()); 03406 fl_rectf(text_area.x+text_area.w, mCursorOldY, 03407 RIGHT_MARGIN, mMaxsize, color()); 03408 fl_pop_clip(); 03409 } 03410 03411 // draw the scrollbars 03412 if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_CHILD)) { 03413 mVScrollBar->damage(FL_DAMAGE_ALL); 03414 mHScrollBar->damage(FL_DAMAGE_ALL); 03415 } 03416 update_child(*mVScrollBar); 03417 update_child(*mHScrollBar); 03418 03419 // draw all of the text 03420 if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_EXPOSE)) { 03421 //printf("drawing all text\n"); 03422 int X, Y, W, H; 03423 if (fl_clip_box(text_area.x, text_area.y, text_area.w, text_area.h, 03424 X, Y, W, H)) { 03425 // Draw text using the intersected clipping box... 03426 // (this sets the clipping internally) 03427 draw_text(X, Y, W, H); 03428 } else { 03429 // Draw the whole area... 03430 draw_text(text_area.x, text_area.y, text_area.w, text_area.h); 03431 } 03432 } 03433 else if (damage() & FL_DAMAGE_SCROLL) { 03434 // draw some lines of text 03435 fl_push_clip(text_area.x, text_area.y, 03436 text_area.w, text_area.h); 03437 //printf("drawing text from %d to %d\n", damage_range1_start, damage_range1_end); 03438 draw_range(damage_range1_start, damage_range1_end); 03439 if (damage_range2_end != -1) { 03440 //printf("drawing text from %d to %d\n", damage_range2_start, damage_range2_end); 03441 draw_range(damage_range2_start, damage_range2_end); 03442 } 03443 damage_range1_start = damage_range1_end = -1; 03444 damage_range2_start = damage_range2_end = -1; 03445 fl_pop_clip(); 03446 } 03447 03448 // draw the text cursor 03449 if (damage() & (FL_DAMAGE_ALL | FL_DAMAGE_SCROLL | FL_DAMAGE_EXPOSE) 03450 && !buffer()->primary_selection()->selected() && 03451 mCursorOn && Fl::focus() == this ) { 03452 fl_push_clip(text_area.x-LEFT_MARGIN, 03453 text_area.y, 03454 text_area.w+LEFT_MARGIN+RIGHT_MARGIN, 03455 text_area.h); 03456 03457 int X, Y; 03458 if (position_to_xy(mCursorPos, &X, &Y)) draw_cursor(X, Y); 03459 // else puts("position_to_xy() failed - unable to draw cursor!"); 03460 //printf("drew cursor at pos: %d (%d,%d)\n", mCursorPos, X, Y); 03461 mCursorOldY = Y; 03462 fl_pop_clip(); 03463 } 03464 fl_pop_clip(); 03465 } 03466 03467 03468 03469 // this processes drag events due to mouse for Fl_Text_Display and 03470 // also drags due to cursor movement with shift held down for 03471 // Fl_Text_Editor 03472 void fl_text_drag_me(int pos, Fl_Text_Display* d) { 03473 if (d->dragType == Fl_Text_Display::DRAG_CHAR) { 03474 if (pos >= d->dragPos) { 03475 d->buffer()->select(d->dragPos, pos); 03476 } else { 03477 d->buffer()->select(pos, d->dragPos); 03478 } 03479 d->insert_position(pos); 03480 } else if (d->dragType == Fl_Text_Display::DRAG_WORD) { 03481 if (pos >= d->dragPos) { 03482 d->insert_position(d->word_end(pos)); 03483 d->buffer()->select(d->word_start(d->dragPos), d->word_end(pos)); 03484 } else { 03485 d->insert_position(d->word_start(pos)); 03486 d->buffer()->select(d->word_start(pos), d->word_end(d->dragPos)); 03487 } 03488 } else if (d->dragType == Fl_Text_Display::DRAG_LINE) { 03489 if (pos >= d->dragPos) { 03490 d->insert_position(d->buffer()->line_end(pos)+1); 03491 d->buffer()->select(d->buffer()->line_start(d->dragPos), 03492 d->buffer()->line_end(pos)+1); 03493 } else { 03494 d->insert_position(d->buffer()->line_start(pos)); 03495 d->buffer()->select(d->buffer()->line_start(pos), 03496 d->buffer()->line_end(d->dragPos)+1); 03497 } 03498 } 03499 } 03500 03501 03502 03510 void Fl_Text_Display::scroll_timer_cb(void *user_data) { 03511 Fl_Text_Display *w = (Fl_Text_Display*)user_data; 03512 int pos; 03513 switch (scroll_direction) { 03514 case 1: // mouse is to the right, scroll left 03515 w->scroll(w->mTopLineNum, w->mHorizOffset + scroll_amount); 03516 pos = w->xy_to_position(w->text_area.x + w->text_area.w, scroll_y, CURSOR_POS); 03517 break; 03518 case 2: // mouse is to the left, scroll right 03519 w->scroll(w->mTopLineNum, w->mHorizOffset + scroll_amount); 03520 pos = w->xy_to_position(w->text_area.x, scroll_y, CURSOR_POS); 03521 break; 03522 case 3: // mouse is above, scroll down 03523 w->scroll(w->mTopLineNum + scroll_amount, w->mHorizOffset); 03524 pos = w->xy_to_position(scroll_x, w->text_area.y, CURSOR_POS); 03525 break; 03526 case 4: // mouse is below, scroll up 03527 w->scroll(w->mTopLineNum + scroll_amount, w->mHorizOffset); 03528 pos = w->xy_to_position(scroll_x, w->text_area.y + w->text_area.h, CURSOR_POS); 03529 break; 03530 default: 03531 return; 03532 } 03533 fl_text_drag_me(pos, w); 03534 Fl::repeat_timeout(.1, scroll_timer_cb, user_data); 03535 } 03536 03537 03538 03542 int Fl_Text_Display::handle(int event) { 03543 if (!buffer()) return 0; 03544 // This isn't very elegant! 03545 if (!Fl::event_inside(text_area.x, text_area.y, text_area.w, text_area.h) && 03546 !dragging && event != FL_LEAVE && event != FL_ENTER && 03547 event != FL_MOVE && event != FL_FOCUS && event != FL_UNFOCUS && 03548 event != FL_KEYBOARD && event != FL_KEYUP) { 03549 return Fl_Group::handle(event); 03550 } 03551 03552 switch (event) { 03553 case FL_ENTER: 03554 case FL_MOVE: 03555 if (active_r()) { 03556 if (Fl::event_inside(text_area.x, text_area.y, text_area.w, 03557 text_area.h)) window()->cursor(FL_CURSOR_INSERT); 03558 else window()->cursor(FL_CURSOR_DEFAULT); 03559 return 1; 03560 } else { 03561 return 0; 03562 } 03563 03564 case FL_LEAVE: 03565 case FL_HIDE: 03566 if (active_r() && window()) { 03567 window()->cursor(FL_CURSOR_DEFAULT); 03568 03569 return 1; 03570 } else { 03571 return 0; 03572 } 03573 03574 case FL_PUSH: { 03575 if (active_r() && window()) { 03576 if (Fl::event_inside(text_area.x, text_area.y, text_area.w, 03577 text_area.h)) window()->cursor(FL_CURSOR_INSERT); 03578 else window()->cursor(FL_CURSOR_DEFAULT); 03579 } 03580 03581 if (Fl::focus() != this) { 03582 Fl::focus(this); 03583 handle(FL_FOCUS); 03584 } 03585 if (Fl_Group::handle(event)) return 1; 03586 if (Fl::event_state()&FL_SHIFT) return handle(FL_DRAG); 03587 dragging = 1; 03588 int pos = xy_to_position(Fl::event_x(), Fl::event_y(), CURSOR_POS); 03589 dragPos = pos; 03590 if (buffer()->primary_selection()->includes(pos)) { 03591 dragType = DRAG_START_DND; 03592 return 1; 03593 } 03594 dragType = Fl::event_clicks(); 03595 if (dragType == DRAG_CHAR) { 03596 buffer()->unselect(); 03597 Fl::copy("", 0, 0); 03598 } 03599 else if (dragType == DRAG_WORD) { 03600 buffer()->select(word_start(pos), word_end(pos)); 03601 dragPos = word_start(pos); 03602 } 03603 03604 if (buffer()->primary_selection()->selected()) 03605 insert_position(buffer()->primary_selection()->end()); 03606 else 03607 insert_position(pos); 03608 show_insert_position(); 03609 return 1; 03610 } 03611 03612 case FL_DRAG: { 03613 if (dragType==DRAG_NONE) 03614 return 1; 03615 if (dragType==DRAG_START_DND) { 03616 if (!Fl::event_is_click() && Fl::dnd_text_ops()) { 03617 const char* copy = buffer()->selection_text(); 03618 Fl::dnd(); 03619 free((void*)copy); 03620 } 03621 return 1; 03622 } 03623 int X = Fl::event_x(), Y = Fl::event_y(), pos = insert_position(); 03624 // if we leave the text_area, we start a timer event 03625 // that will take care of scrolling and selecting 03626 if (Y < text_area.y) { 03627 scroll_x = X; 03628 scroll_amount = (Y - text_area.y) / 5 - 1; 03629 if (!scroll_direction) { 03630 Fl::add_timeout(.01, scroll_timer_cb, this); 03631 } 03632 scroll_direction = 3; 03633 } else if (Y >= text_area.y+text_area.h) { 03634 scroll_x = X; 03635 scroll_amount = (Y - text_area.y - text_area.h) / 5 + 1; 03636 if (!scroll_direction) { 03637 Fl::add_timeout(.01, scroll_timer_cb, this); 03638 } 03639 scroll_direction = 4; 03640 } else if (X < text_area.x) { 03641 scroll_y = Y; 03642 scroll_amount = (X - text_area.x) / 2 - 1; 03643 if (!scroll_direction) { 03644 Fl::add_timeout(.01, scroll_timer_cb, this); 03645 } 03646 scroll_direction = 2; 03647 } else if (X >= text_area.x+text_area.w) { 03648 scroll_y = Y; 03649 scroll_amount = (X - text_area.x - text_area.w) / 2 + 1; 03650 if (!scroll_direction) { 03651 Fl::add_timeout(.01, scroll_timer_cb, this); 03652 } 03653 scroll_direction = 1; 03654 } else { 03655 if (scroll_direction) { 03656 Fl::remove_timeout(scroll_timer_cb, this); 03657 scroll_direction = 0; 03658 } 03659 pos = xy_to_position(X, Y, CURSOR_POS); 03660 pos = buffer()->next_char(pos); 03661 } 03662 fl_text_drag_me(pos, this); 03663 return 1; 03664 } 03665 03666 case FL_RELEASE: { 03667 if (Fl::event_is_click() && (! Fl::event_clicks()) && 03668 buffer()->primary_selection()->includes(dragPos) && !(Fl::event_state()&FL_SHIFT) ) { 03669 buffer()->unselect(); // clicking in the selection: unselect and move cursor 03670 insert_position(dragPos); 03671 return 1; 03672 } else if (Fl::event_clicks() == DRAG_LINE && Fl::event_button() == FL_LEFT_MOUSE) { 03673 buffer()->select(buffer()->line_start(dragPos), buffer()->next_char(buffer()->line_end(dragPos))); 03674 dragPos = line_start(dragPos); 03675 dragType = DRAG_CHAR; 03676 } else { 03677 dragging = 0; 03678 if (scroll_direction) { 03679 Fl::remove_timeout(scroll_timer_cb, this); 03680 scroll_direction = 0; 03681 } 03682 03683 // convert from WORD or LINE selection to CHAR 03684 /*if (insert_position() >= dragPos) 03685 dragPos = buffer()->primary_selection()->start(); 03686 else 03687 dragPos = buffer()->primary_selection()->end();*/ 03688 dragType = DRAG_CHAR; 03689 } 03690 03691 const char* copy = buffer()->selection_text(); 03692 if (*copy) Fl::copy(copy, strlen(copy), 0); 03693 free((void*)copy); 03694 return 1; 03695 } 03696 03697 case FL_MOUSEWHEEL: 03698 if (Fl::event_dy()) return mVScrollBar->handle(event); 03699 else return mHScrollBar->handle(event); 03700 03701 case FL_UNFOCUS: 03702 if (active_r() && window()) window()->cursor(FL_CURSOR_DEFAULT); 03703 case FL_FOCUS: 03704 if (buffer()->selected()) { 03705 int start, end; 03706 if (buffer()->selection_position(&start, &end)) 03707 redisplay_range(start, end); 03708 } 03709 if (buffer()->secondary_selected()) { 03710 int start, end; 03711 if (buffer()->secondary_selection_position(&start, &end)) 03712 redisplay_range(start, end); 03713 } 03714 if (buffer()->highlight()) { 03715 int start, end; 03716 if (buffer()->highlight_position(&start, &end)) 03717 redisplay_range(start, end); 03718 } 03719 return 1; 03720 03721 case FL_KEYBOARD: 03722 // Copy? 03723 if ((Fl::event_state()&(FL_CTRL|FL_COMMAND)) && Fl::event_key()=='c') { 03724 if (!buffer()->selected()) return 1; 03725 const char *copy = buffer()->selection_text(); 03726 if (*copy) Fl::copy(copy, strlen(copy), 1); 03727 free((void*)copy); 03728 return 1; 03729 } 03730 03731 // Select all ? 03732 if ((Fl::event_state()&(FL_CTRL|FL_COMMAND)) && Fl::event_key()=='a') { 03733 buffer()->select(0,buffer()->length()); 03734 const char *copy = buffer()->selection_text(); 03735 if (*copy) Fl::copy(copy, strlen(copy), 0); 03736 free((void*)copy); 03737 return 1; 03738 } 03739 03740 if (mVScrollBar->handle(event)) return 1; 03741 if (mHScrollBar->handle(event)) return 1; 03742 03743 break; 03744 03745 case FL_SHORTCUT: 03746 if (!(shortcut() ? Fl::test_shortcut(shortcut()) : test_shortcut())) 03747 return 0; 03748 if (Fl::visible_focus() && handle(FL_FOCUS)) { 03749 Fl::focus(this); 03750 return 1; 03751 } 03752 break; 03753 03754 } 03755 03756 return 0; 03757 } 03758 03759 03760 /* 03761 Convert an x pixel position into a column number. 03762 */ 03763 double Fl_Text_Display::x_to_col(double y) const 03764 { 03765 if (!mColumnScale) { 03766 mColumnScale = string_width("Mitg", 4, 'A') / 4.0; 03767 } 03768 return (y/mColumnScale)+0.5; 03769 } 03770 03771 03775 double Fl_Text_Display::col_to_x(double col) const 03776 { 03777 if (!mColumnScale) { 03778 // recalculate column scale value 03779 x_to_col(0); 03780 } 03781 return col*mColumnScale; 03782 } 03783 03784 03785 03786 03787 // 03788 // End of "$Id: Fl_Text_Display.cxx 8078 2010-12-20 17:32:36Z AlbrechtS $". 03789 //