|
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_Buffer.cxx 8040 2010-12-15 17:38:39Z manolo $" 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 #include <stdio.h> 00029 #include <stdlib.h> 00030 #include <FL/fl_utf8.h> 00031 #include "flstring.h" 00032 #include <ctype.h> 00033 #include <FL/Fl.H> 00034 #include <FL/Fl_Text_Buffer.H> 00035 #include <FL/fl_ask.H> 00036 00037 00038 /* 00039 This file is based on a port of NEdit to FLTK many years ago. NEdit at that 00040 point was already stretched beyond the task it was designed for which explains 00041 why the source code is sometimes pretty convoluted. It still is a very useful 00042 widget for FLTK, and we are thankful that the nedit team allowed us to 00043 integrate their code. 00044 00045 With the introduction of Unicode and UTF-8, Fl_Text_... has to go into a whole 00046 new generation of code. Originally designed for monospaced fonts only, many 00047 features make less sense in the multibyte and multiwidth world of UTF-8. 00048 00049 Columns are a good example. There is simply no such thing. The new Fl_Text_... 00050 widget converts columns to pixels by multiplying them with the average 00051 character width for a given font. 00052 00053 Rectangular selections were rarely used (if at all) and make little sense when 00054 using variable width fonts. They have been removed. 00055 00056 Using multiple spaces to emulate tab stops has been replaced by pixel counting 00057 routines. They are slower, but give the expected result for proportional fonts. 00058 00059 And constantly recalculating character widths is just much too expensive. Lines 00060 of text are now subdivided into blocks of text which are measured at once 00061 instead of individual characters. 00062 */ 00063 00064 00065 #ifndef min 00066 00067 static int max(int i1, int i2) 00068 { 00069 return i1 >= i2 ? i1 : i2; 00070 } 00071 00072 static int min(int i1, int i2) 00073 { 00074 return i1 <= i2 ? i1 : i2; 00075 } 00076 00077 #endif 00078 00079 00080 static char *undobuffer; 00081 static int undobufferlength; 00082 static Fl_Text_Buffer *undowidget; 00083 static int undoat; // points after insertion 00084 static int undocut; // number of characters deleted there 00085 static int undoinsert; // number of characters inserted 00086 static int undoyankcut; // length of valid contents of buffer, even if undocut=0 00087 00088 /* 00089 Resize the undo buffer to match at least the requested size. 00090 */ 00091 static void undobuffersize(int n) 00092 { 00093 if (n > undobufferlength) { 00094 if (undobuffer) { 00095 do { 00096 undobufferlength *= 2; 00097 } while (undobufferlength < n); 00098 undobuffer = (char *) realloc(undobuffer, undobufferlength); 00099 } else { 00100 undobufferlength = n + 9; 00101 undobuffer = (char *) malloc(undobufferlength); 00102 } 00103 } 00104 } 00105 00106 static void def_transcoding_warning_action(Fl_Text_Buffer *text) 00107 { 00108 fl_alert("%s", text->file_encoding_warning_message); 00109 } 00110 00111 /* 00112 Initialize all variables. 00113 */ 00114 Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize) 00115 { 00116 mLength = 0; 00117 mPreferredGapSize = preferredGapSize; 00118 mBuf = (char *) malloc(requestedSize + mPreferredGapSize); 00119 mGapStart = 0; 00120 mGapEnd = mPreferredGapSize; 00121 mTabDist = 8; 00122 mPrimary.mSelected = 0; 00123 mPrimary.mStart = mPrimary.mEnd = 0; 00124 mSecondary.mSelected = 0; 00125 mSecondary.mStart = mSecondary.mEnd = 0; 00126 mHighlight.mSelected = 0; 00127 mHighlight.mStart = mHighlight.mEnd = 0; 00128 mModifyProcs = NULL; 00129 mCbArgs = NULL; 00130 mNModifyProcs = 0; 00131 mNPredeleteProcs = 0; 00132 mPredeleteProcs = NULL; 00133 mPredeleteCbArgs = NULL; 00134 mCursorPosHint = 0; 00135 mCanUndo = 1; 00136 input_file_was_transcoded = 0; 00137 transcoding_warning_action = def_transcoding_warning_action; 00138 } 00139 00140 00141 /* 00142 Free all resources. 00143 */ 00144 Fl_Text_Buffer::~Fl_Text_Buffer() 00145 { 00146 free(mBuf); 00147 if (mNModifyProcs != 0) { 00148 delete[]mModifyProcs; 00149 delete[]mCbArgs; 00150 } 00151 if (mNPredeleteProcs != 0) { 00152 delete[]mPredeleteProcs; 00153 delete[]mPredeleteCbArgs; 00154 } 00155 } 00156 00157 00158 /* 00159 This function copies verbose whatever is in front and after the gap into a 00160 single buffer. 00161 */ 00162 char *Fl_Text_Buffer::text() const { 00163 char *t = (char *) malloc(mLength + 1); 00164 memcpy(t, mBuf, mGapStart); 00165 memcpy(t+mGapStart, mBuf+mGapEnd, mLength - mGapStart); 00166 t[mLength] = '\0'; 00167 return t; 00168 } 00169 00170 00171 /* 00172 Set the text buffer to a new string. 00173 */ 00174 void Fl_Text_Buffer::text(const char *t) 00175 { 00176 IS_UTF8_ALIGNED(t) 00177 00178 call_predelete_callbacks(0, length()); 00179 00180 /* Save information for redisplay, and get rid of the old buffer */ 00181 const char *deletedText = text(); 00182 int deletedLength = mLength; 00183 free((void *) mBuf); 00184 00185 /* Start a new buffer with a gap of mPreferredGapSize at the end */ 00186 int insertedLength = strlen(t); 00187 mBuf = (char *) malloc(insertedLength + mPreferredGapSize); 00188 mLength = insertedLength; 00189 mGapStart = insertedLength; 00190 mGapEnd = mGapStart + mPreferredGapSize; 00191 memcpy(mBuf, t, insertedLength); 00192 00193 /* Zero all of the existing selections */ 00194 update_selections(0, deletedLength, 0); 00195 00196 /* Call the saved display routine(s) to update the screen */ 00197 call_modify_callbacks(0, deletedLength, insertedLength, 0, deletedText); 00198 free((void *) deletedText); 00199 } 00200 00201 00202 /* 00203 Creates a range of text to a new buffer and copies verbose from around the gap. 00204 */ 00205 char *Fl_Text_Buffer::text_range(int start, int end) const { 00206 IS_UTF8_ALIGNED2(this, (start)) 00207 IS_UTF8_ALIGNED2(this, (end)) 00208 00209 char *s = NULL; 00210 00211 /* Make sure start and end are ok, and allocate memory for returned string. 00212 If start is bad, return "", if end is bad, adjust it. */ 00213 if (start < 0 || start > mLength) 00214 { 00215 s = (char *) malloc(1); 00216 s[0] = '\0'; 00217 return s; 00218 } 00219 if (end < start) { 00220 int temp = start; 00221 start = end; 00222 end = temp; 00223 } 00224 if (end > mLength) 00225 end = mLength; 00226 int copiedLength = end - start; 00227 s = (char *) malloc(copiedLength + 1); 00228 00229 /* Copy the text from the buffer to the returned string */ 00230 if (end <= mGapStart) { 00231 memcpy(s, mBuf + start, copiedLength); 00232 } else if (start >= mGapStart) { 00233 memcpy(s, mBuf + start + (mGapEnd - mGapStart), copiedLength); 00234 } else { 00235 int part1Length = mGapStart - start; 00236 memcpy(s, mBuf + start, part1Length); 00237 memcpy(s + part1Length, mBuf + mGapEnd, copiedLength - part1Length); 00238 } 00239 s[copiedLength] = '\0'; 00240 return s; 00241 } 00242 00243 /* 00244 Return a UCS-4 character at the given index. 00245 Pos must be at a character boundary. 00246 */ 00247 unsigned int Fl_Text_Buffer::char_at(int pos) const { 00248 if (pos < 0 || pos >= mLength) 00249 return '\0'; 00250 00251 IS_UTF8_ALIGNED2(this, (pos)) 00252 00253 const char *src = address(pos); 00254 return fl_utf8decode(src, 0, 0); 00255 } 00256 00257 00258 /* 00259 Return the raw byte at the given index. 00260 This function ignores all unicode encoding. 00261 */ 00262 char Fl_Text_Buffer::byte_at(int pos) const { 00263 if (pos < 0 || pos >= mLength) 00264 return '\0'; 00265 const char *src = address(pos); 00266 return *src; 00267 } 00268 00269 00270 /* 00271 Insert some text at the given index. 00272 Pos must be at a character boundary. 00273 */ 00274 void Fl_Text_Buffer::insert(int pos, const char *text) 00275 { 00276 IS_UTF8_ALIGNED2(this, (pos)) 00277 IS_UTF8_ALIGNED(text) 00278 00279 /* check if there is actually any text */ 00280 if (!text || !*text) 00281 return; 00282 00283 /* if pos is not contiguous to existing text, make it */ 00284 if (pos > mLength) 00285 pos = mLength; 00286 if (pos < 0) 00287 pos = 0; 00288 00289 /* Even if nothing is deleted, we must call these callbacks */ 00290 call_predelete_callbacks(pos, 0); 00291 00292 /* insert and redisplay */ 00293 int nInserted = insert_(pos, text); 00294 mCursorPosHint = pos + nInserted; 00295 IS_UTF8_ALIGNED2(this, (mCursorPosHint)) 00296 call_modify_callbacks(pos, 0, nInserted, 0, NULL); 00297 } 00298 00299 00300 /* 00301 Replace a range of text with new text. 00302 Start and end must be at a character boundary. 00303 */ 00304 void Fl_Text_Buffer::replace(int start, int end, const char *text) 00305 { 00306 // Range check... 00307 if (!text) 00308 return; 00309 if (start < 0) 00310 start = 0; 00311 if (end > mLength) 00312 end = mLength; 00313 00314 IS_UTF8_ALIGNED2(this, (start)) 00315 IS_UTF8_ALIGNED2(this, (end)) 00316 IS_UTF8_ALIGNED(text) 00317 00318 call_predelete_callbacks(start, end - start); 00319 const char *deletedText = text_range(start, end); 00320 remove_(start, end); 00321 int nInserted = insert_(start, text); 00322 mCursorPosHint = start + nInserted; 00323 call_modify_callbacks(start, end - start, nInserted, 0, deletedText); 00324 free((void *) deletedText); 00325 } 00326 00327 00328 /* 00329 Remove a range of text. 00330 Start and End must be at a character boundary. 00331 */ 00332 void Fl_Text_Buffer::remove(int start, int end) 00333 { 00334 /* Make sure the arguments make sense */ 00335 if (start > end) { 00336 int temp = start; 00337 start = end; 00338 end = temp; 00339 } 00340 if (start > mLength) 00341 start = mLength; 00342 if (start < 0) 00343 start = 0; 00344 if (end > mLength) 00345 end = mLength; 00346 if (end < 0) 00347 end = 0; 00348 00349 IS_UTF8_ALIGNED2(this, (start)) 00350 IS_UTF8_ALIGNED2(this, (end)) 00351 00352 if (start == end) 00353 return; 00354 00355 call_predelete_callbacks(start, end - start); 00356 /* Remove and redisplay */ 00357 const char *deletedText = text_range(start, end); 00358 remove_(start, end); 00359 mCursorPosHint = start; 00360 call_modify_callbacks(start, end - start, 0, 0, deletedText); 00361 free((void *) deletedText); 00362 } 00363 00364 00365 /* 00366 Copy a range of text from another text buffer. 00367 fromStart, fromEnd, and toPos must be at a character boundary. 00368 */ 00369 void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart, 00370 int fromEnd, int toPos) 00371 { 00372 IS_UTF8_ALIGNED2(fromBuf, fromStart) 00373 IS_UTF8_ALIGNED2(fromBuf, fromEnd) 00374 IS_UTF8_ALIGNED2(this, (toPos)) 00375 00376 int copiedLength = fromEnd - fromStart; 00377 00378 /* Prepare the buffer to receive the new text. If the new text fits in 00379 the current buffer, just move the gap (if necessary) to where 00380 the text should be inserted. If the new text is too large, reallocate 00381 the buffer with a gap large enough to accomodate the new text and a 00382 gap of mPreferredGapSize */ 00383 if (copiedLength > mGapEnd - mGapStart) 00384 reallocate_with_gap(toPos, copiedLength + mPreferredGapSize); 00385 else if (toPos != mGapStart) 00386 move_gap(toPos); 00387 00388 /* Insert the new text (toPos now corresponds to the start of the gap) */ 00389 if (fromEnd <= fromBuf->mGapStart) { 00390 memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], copiedLength); 00391 } else if (fromStart >= fromBuf->mGapStart) { 00392 memcpy(&mBuf[toPos], 00393 &fromBuf->mBuf[fromStart + (fromBuf->mGapEnd - fromBuf->mGapStart)], 00394 copiedLength); 00395 } else { 00396 int part1Length = fromBuf->mGapStart - fromStart; 00397 memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], part1Length); 00398 memcpy(&mBuf[toPos + part1Length], 00399 &fromBuf->mBuf[fromBuf->mGapEnd], copiedLength - part1Length); 00400 } 00401 mGapStart += copiedLength; 00402 mLength += copiedLength; 00403 update_selections(toPos, 0, copiedLength); 00404 } 00405 00406 00407 /* 00408 Take the previous changes and undo them. Return the previous 00409 cursor position in cursorPos. Returns 1 if the undo was applied. 00410 CursorPos will be at a character boundary. 00411 */ 00412 int Fl_Text_Buffer::undo(int *cursorPos) 00413 { 00414 if (undowidget != this || (!undocut && !undoinsert && !mCanUndo)) 00415 return 0; 00416 00417 int ilen = undocut; 00418 int xlen = undoinsert; 00419 int b = undoat - xlen; 00420 00421 if (xlen && undoyankcut && !ilen) { 00422 ilen = undoyankcut; 00423 } 00424 00425 if (xlen && ilen) { 00426 undobuffersize(ilen + 1); 00427 undobuffer[ilen] = 0; 00428 char *tmp = strdup(undobuffer); 00429 replace(b, undoat, tmp); 00430 if (cursorPos) 00431 *cursorPos = mCursorPosHint; 00432 free(tmp); 00433 } else if (xlen) { 00434 remove(b, undoat); 00435 if (cursorPos) 00436 *cursorPos = mCursorPosHint; 00437 } else if (ilen) { 00438 undobuffersize(ilen + 1); 00439 undobuffer[ilen] = 0; 00440 insert(undoat, undobuffer); 00441 if (cursorPos) 00442 *cursorPos = mCursorPosHint; 00443 undoyankcut = 0; 00444 } 00445 00446 return 1; 00447 } 00448 00449 00450 /* 00451 Set a flag if undo function will work. 00452 */ 00453 void Fl_Text_Buffer::canUndo(char flag) 00454 { 00455 mCanUndo = flag; 00456 // disabling undo also clears the last undo operation! 00457 if (!mCanUndo && undowidget==this) 00458 undowidget = 0; 00459 } 00460 00461 00462 /* 00463 Change the tab width. This will cause a couple of callbacks and a complete 00464 redisplay. 00465 Matt: I am not entirely sure why we need to trigger callbacks because 00466 tabs are only a graphical hint, not changing any text at all, but I leave 00467 this in here for back compatibility. 00468 */ 00469 void Fl_Text_Buffer::tab_distance(int tabDist) 00470 { 00471 /* First call the pre-delete callbacks with the previous tab setting 00472 still active. */ 00473 call_predelete_callbacks(0, mLength); 00474 00475 /* Change the tab setting */ 00476 mTabDist = tabDist; 00477 00478 /* Force any display routines to redisplay everything (unfortunately, 00479 this means copying the whole buffer contents to provide "deletedText" */ 00480 const char *deletedText = text(); 00481 call_modify_callbacks(0, mLength, mLength, 0, deletedText); 00482 free((void *) deletedText); 00483 } 00484 00485 00486 /* 00487 Select a range of text. 00488 Start and End must be at a character boundary. 00489 */ 00490 void Fl_Text_Buffer::select(int start, int end) 00491 { 00492 IS_UTF8_ALIGNED2(this, (start)) 00493 IS_UTF8_ALIGNED2(this, (end)) 00494 00495 Fl_Text_Selection oldSelection = mPrimary; 00496 00497 mPrimary.set(start, end); 00498 redisplay_selection(&oldSelection, &mPrimary); 00499 } 00500 00501 00502 /* 00503 Clear the primary selection. 00504 */ 00505 void Fl_Text_Buffer::unselect() 00506 { 00507 Fl_Text_Selection oldSelection = mPrimary; 00508 00509 mPrimary.mSelected = 0; 00510 redisplay_selection(&oldSelection, &mPrimary); 00511 } 00512 00513 00514 /* 00515 Return the primary selection range. 00516 */ 00517 int Fl_Text_Buffer::selection_position(int *start, int *end) 00518 { 00519 return mPrimary.position(start, end); 00520 } 00521 00522 00523 /* 00524 Return a copy of the selected text. 00525 */ 00526 char *Fl_Text_Buffer::selection_text() 00527 { 00528 return selection_text_(&mPrimary); 00529 } 00530 00531 00532 /* 00533 Remove the selected text. 00534 */ 00535 void Fl_Text_Buffer::remove_selection() 00536 { 00537 remove_selection_(&mPrimary); 00538 } 00539 00540 00541 /* 00542 Replace the selected text. 00543 */ 00544 void Fl_Text_Buffer::replace_selection(const char *text) 00545 { 00546 replace_selection_(&mPrimary, text); 00547 } 00548 00549 00550 /* 00551 Select text. 00552 Start and End must be at a character boundary. 00553 */ 00554 void Fl_Text_Buffer::secondary_select(int start, int end) 00555 { 00556 Fl_Text_Selection oldSelection = mSecondary; 00557 00558 mSecondary.set(start, end); 00559 redisplay_selection(&oldSelection, &mSecondary); 00560 } 00561 00562 00563 /* 00564 Deselect text. 00565 */ 00566 void Fl_Text_Buffer::secondary_unselect() 00567 { 00568 Fl_Text_Selection oldSelection = mSecondary; 00569 00570 mSecondary.mSelected = 0; 00571 redisplay_selection(&oldSelection, &mSecondary); 00572 } 00573 00574 00575 /* 00576 Return the selected range. 00577 */ 00578 int Fl_Text_Buffer::secondary_selection_position(int *start, int *end) 00579 { 00580 return mSecondary.position(start, end); 00581 } 00582 00583 00584 /* 00585 Return a copy of the text in this selection. 00586 */ 00587 char *Fl_Text_Buffer::secondary_selection_text() 00588 { 00589 return selection_text_(&mSecondary); 00590 } 00591 00592 00593 /* 00594 Remove the selected text. 00595 */ 00596 void Fl_Text_Buffer::remove_secondary_selection() 00597 { 00598 remove_selection_(&mSecondary); 00599 } 00600 00601 00602 /* 00603 Replace selected text. 00604 */ 00605 void Fl_Text_Buffer::replace_secondary_selection(const char *text) 00606 { 00607 replace_selection_(&mSecondary, text); 00608 } 00609 00610 00611 /* 00612 Highlight a range of text. 00613 Start and End must be at a character boundary. 00614 */ 00615 void Fl_Text_Buffer::highlight(int start, int end) 00616 { 00617 Fl_Text_Selection oldSelection = mHighlight; 00618 00619 mHighlight.set(start, end); 00620 redisplay_selection(&oldSelection, &mHighlight); 00621 } 00622 00623 00624 /* 00625 Remove text highlighting. 00626 */ 00627 void Fl_Text_Buffer::unhighlight() 00628 { 00629 Fl_Text_Selection oldSelection = mHighlight; 00630 00631 mHighlight.mSelected = 0; 00632 redisplay_selection(&oldSelection, &mHighlight); 00633 } 00634 00635 00636 /* 00637 Return position of highlight. 00638 */ 00639 int Fl_Text_Buffer::highlight_position(int *start, int *end) 00640 { 00641 return mHighlight.position(start, end); 00642 } 00643 00644 00645 /* 00646 Return a copy of highlighted text. 00647 */ 00648 char *Fl_Text_Buffer::highlight_text() 00649 { 00650 return selection_text_(&mHighlight); 00651 } 00652 00653 00654 /* 00655 Add a callback that is called whenever text is modified. 00656 */ 00657 void Fl_Text_Buffer::add_modify_callback(Fl_Text_Modify_Cb bufModifiedCB, 00658 void *cbArg) 00659 { 00660 Fl_Text_Modify_Cb *newModifyProcs = 00661 new Fl_Text_Modify_Cb[mNModifyProcs + 1]; 00662 void **newCBArgs = new void *[mNModifyProcs + 1]; 00663 for (int i = 0; i < mNModifyProcs; i++) { 00664 newModifyProcs[i + 1] = mModifyProcs[i]; 00665 newCBArgs[i + 1] = mCbArgs[i]; 00666 } 00667 if (mNModifyProcs != 0) { 00668 delete[]mModifyProcs; 00669 delete[]mCbArgs; 00670 } 00671 newModifyProcs[0] = bufModifiedCB; 00672 newCBArgs[0] = cbArg; 00673 mNModifyProcs++; 00674 mModifyProcs = newModifyProcs; 00675 mCbArgs = newCBArgs; 00676 } 00677 00678 00679 /* 00680 Remove a callback. 00681 */ 00682 void Fl_Text_Buffer::remove_modify_callback(Fl_Text_Modify_Cb bufModifiedCB, 00683 void *cbArg) 00684 { 00685 int i, toRemove = -1; 00686 00687 /* find the matching callback to remove */ 00688 for (i = 0; i < mNModifyProcs; i++) { 00689 if (mModifyProcs[i] == bufModifiedCB && mCbArgs[i] == cbArg) { 00690 toRemove = i; 00691 break; 00692 } 00693 } 00694 if (toRemove == -1) { 00695 Fl::error 00696 ("Fl_Text_Buffer::remove_modify_callback(): Can't find modify CB to remove"); 00697 return; 00698 } 00699 00700 /* Allocate new lists for remaining callback procs and args (if 00701 any are left) */ 00702 mNModifyProcs--; 00703 if (mNModifyProcs == 0) { 00704 mNModifyProcs = 0; 00705 delete[]mModifyProcs; 00706 mModifyProcs = NULL; 00707 delete[]mCbArgs; 00708 mCbArgs = NULL; 00709 return; 00710 } 00711 Fl_Text_Modify_Cb *newModifyProcs = new Fl_Text_Modify_Cb[mNModifyProcs]; 00712 void **newCBArgs = new void *[mNModifyProcs]; 00713 00714 /* copy out the remaining members and free the old lists */ 00715 for (i = 0; i < toRemove; i++) { 00716 newModifyProcs[i] = mModifyProcs[i]; 00717 newCBArgs[i] = mCbArgs[i]; 00718 } 00719 for (; i < mNModifyProcs; i++) { 00720 newModifyProcs[i] = mModifyProcs[i + 1]; 00721 newCBArgs[i] = mCbArgs[i + 1]; 00722 } 00723 delete[]mModifyProcs; 00724 delete[]mCbArgs; 00725 mModifyProcs = newModifyProcs; 00726 mCbArgs = newCBArgs; 00727 } 00728 00729 00730 /* 00731 Add a callback that is called before deleting text. 00732 */ 00733 void Fl_Text_Buffer::add_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, 00734 void *cbArg) 00735 { 00736 Fl_Text_Predelete_Cb *newPreDeleteProcs = 00737 new Fl_Text_Predelete_Cb[mNPredeleteProcs + 1]; 00738 void **newCBArgs = new void *[mNPredeleteProcs + 1]; 00739 for (int i = 0; i < mNPredeleteProcs; i++) { 00740 newPreDeleteProcs[i + 1] = mPredeleteProcs[i]; 00741 newCBArgs[i + 1] = mPredeleteCbArgs[i]; 00742 } 00743 if (!mNPredeleteProcs != 0) { 00744 delete[]mPredeleteProcs; 00745 delete[]mPredeleteCbArgs; 00746 } 00747 newPreDeleteProcs[0] = bufPreDeleteCB; 00748 newCBArgs[0] = cbArg; 00749 mNPredeleteProcs++; 00750 mPredeleteProcs = newPreDeleteProcs; 00751 mPredeleteCbArgs = newCBArgs; 00752 } 00753 00754 00755 /* 00756 Remove a callback. 00757 */ 00758 void Fl_Text_Buffer::remove_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg) 00759 { 00760 int i, toRemove = -1; 00761 /* find the matching callback to remove */ 00762 for (i = 0; i < mNPredeleteProcs; i++) { 00763 if (mPredeleteProcs[i] == bufPreDeleteCB && 00764 mPredeleteCbArgs[i] == cbArg) { 00765 toRemove = i; 00766 break; 00767 } 00768 } 00769 if (toRemove == -1) { 00770 Fl::error 00771 ("Fl_Text_Buffer::remove_predelete_callback(): Can't find pre-delete CB to remove"); 00772 return; 00773 } 00774 00775 /* Allocate new lists for remaining callback procs and args (if 00776 any are left) */ 00777 mNPredeleteProcs--; 00778 if (mNPredeleteProcs == 0) { 00779 mNPredeleteProcs = 0; 00780 delete[]mPredeleteProcs; 00781 mPredeleteProcs = NULL; 00782 delete[]mPredeleteCbArgs; 00783 mPredeleteCbArgs = NULL; 00784 return; 00785 } 00786 Fl_Text_Predelete_Cb *newPreDeleteProcs = 00787 new Fl_Text_Predelete_Cb[mNPredeleteProcs]; 00788 void **newCBArgs = new void *[mNPredeleteProcs]; 00789 00790 /* copy out the remaining members and free the old lists */ 00791 for (i = 0; i < toRemove; i++) { 00792 newPreDeleteProcs[i] = mPredeleteProcs[i]; 00793 newCBArgs[i] = mPredeleteCbArgs[i]; 00794 } 00795 for (; i < mNPredeleteProcs; i++) { 00796 newPreDeleteProcs[i] = mPredeleteProcs[i + 1]; 00797 newCBArgs[i] = mPredeleteCbArgs[i + 1]; 00798 } 00799 delete[]mPredeleteProcs; 00800 delete[]mPredeleteCbArgs; 00801 mPredeleteProcs = newPreDeleteProcs; 00802 mPredeleteCbArgs = newCBArgs; 00803 } 00804 00805 00806 /* 00807 Return a copy of the line that contains a given index. 00808 Pos must be at a character boundary. 00809 */ 00810 char *Fl_Text_Buffer::line_text(int pos) const { 00811 return text_range(line_start(pos), line_end(pos)); 00812 } 00813 00814 00815 /* 00816 Find the beginning of the line. 00817 */ 00818 int Fl_Text_Buffer::line_start(int pos) const 00819 { 00820 if (!findchar_backward(pos, '\n', &pos)) 00821 return 0; 00822 return pos + 1; 00823 } 00824 00825 00826 /* 00827 Find the end of the line. 00828 */ 00829 int Fl_Text_Buffer::line_end(int pos) const { 00830 if (!findchar_forward(pos, '\n', &pos)) 00831 pos = mLength; 00832 return pos; 00833 } 00834 00835 00836 /* 00837 Find the beginning of a word. 00838 NOT UNICODE SAFE. 00839 */ 00840 int Fl_Text_Buffer::word_start(int pos) const { 00841 // FIXME: character is ucs-4 00842 while (pos>0 && (isalnum(char_at(pos)) || char_at(pos) == '_')) { 00843 pos = prev_char(pos); 00844 } 00845 // FIXME: character is ucs-4 00846 if (!(isalnum(char_at(pos)) || char_at(pos) == '_')) 00847 pos = next_char(pos); 00848 return pos; 00849 } 00850 00851 00852 /* 00853 Find the end of a word. 00854 NOT UNICODE SAFE. 00855 */ 00856 int Fl_Text_Buffer::word_end(int pos) const { 00857 // FIXME: character is ucs-4 00858 while (pos < length() && (isalnum(char_at(pos)) || char_at(pos) == '_')) 00859 { 00860 pos = next_char(pos); 00861 } 00862 return pos; 00863 } 00864 00865 00866 /* 00867 Count the number of characters between two positions. 00868 */ 00869 int Fl_Text_Buffer::count_displayed_characters(int lineStartPos, 00870 int targetPos) const 00871 { 00872 IS_UTF8_ALIGNED2(this, (lineStartPos)) 00873 IS_UTF8_ALIGNED2(this, (targetPos)) 00874 00875 int charCount = 0; 00876 00877 int pos = lineStartPos; 00878 while (pos < targetPos) { 00879 pos = next_char(pos); 00880 charCount++; 00881 } 00882 return charCount; 00883 } 00884 00885 00886 /* 00887 Skip ahead a number of characters from a given index. 00888 This function breaks early if it encounters a newline character. 00889 */ 00890 int Fl_Text_Buffer::skip_displayed_characters(int lineStartPos, int nChars) 00891 { 00892 IS_UTF8_ALIGNED2(this, (lineStartPos)) 00893 00894 int pos = lineStartPos; 00895 00896 for (int charCount = 0; charCount < nChars && pos < mLength; charCount++) { 00897 unsigned int c = char_at(pos); 00898 if (c == '\n') 00899 return pos; 00900 pos = next_char(pos); 00901 } 00902 return pos; 00903 } 00904 00905 00906 /* 00907 Count the number of newline characters between start and end. 00908 startPos and endPos must be at a character boundary. 00909 This function is optimized for speed by not using UTF-8 calls. 00910 */ 00911 int Fl_Text_Buffer::count_lines(int startPos, int endPos) const { 00912 IS_UTF8_ALIGNED2(this, (startPos)) 00913 IS_UTF8_ALIGNED2(this, (endPos)) 00914 00915 int gapLen = mGapEnd - mGapStart; 00916 int lineCount = 0; 00917 00918 int pos = startPos; 00919 while (pos < mGapStart) 00920 { 00921 if (pos == endPos) 00922 return lineCount; 00923 if (mBuf[pos++] == '\n') 00924 lineCount++; 00925 } 00926 while (pos < mLength) { 00927 if (pos == endPos) 00928 return lineCount; 00929 if (mBuf[pos++ + gapLen] == '\n') 00930 lineCount++; 00931 } 00932 return lineCount; 00933 } 00934 00935 00936 /* 00937 Skip to the first character, n lines ahead. 00938 StartPos must be at a character boundary. 00939 This function is optimized for speed by not using UTF-8 calls. 00940 */ 00941 int Fl_Text_Buffer::skip_lines(int startPos, int nLines) 00942 { 00943 IS_UTF8_ALIGNED2(this, (startPos)) 00944 00945 if (nLines == 0) 00946 return startPos; 00947 00948 int gapLen = mGapEnd - mGapStart; 00949 int pos = startPos; 00950 int lineCount = 0; 00951 while (pos < mGapStart) { 00952 if (mBuf[pos++] == '\n') { 00953 lineCount++; 00954 if (lineCount == nLines) { 00955 IS_UTF8_ALIGNED2(this, (pos)) 00956 return pos; 00957 } 00958 } 00959 } 00960 while (pos < mLength) { 00961 if (mBuf[pos++ + gapLen] == '\n') { 00962 lineCount++; 00963 if (lineCount >= nLines) { 00964 IS_UTF8_ALIGNED2(this, (pos)) 00965 return pos; 00966 } 00967 } 00968 } 00969 IS_UTF8_ALIGNED2(this, (pos)) 00970 return pos; 00971 } 00972 00973 00974 /* 00975 Skip to the first character, n lines back. 00976 StartPos must be at a character boundary. 00977 This function is optimized for speed by not using UTF-8 calls. 00978 */ 00979 int Fl_Text_Buffer::rewind_lines(int startPos, int nLines) 00980 { 00981 IS_UTF8_ALIGNED2(this, (startPos)) 00982 00983 int pos = startPos - 1; 00984 if (pos <= 0) 00985 return 0; 00986 00987 int gapLen = mGapEnd - mGapStart; 00988 int lineCount = -1; 00989 while (pos >= mGapStart) { 00990 if (mBuf[pos + gapLen] == '\n') { 00991 if (++lineCount >= nLines) { 00992 IS_UTF8_ALIGNED2(this, (pos+1)) 00993 return pos + 1; 00994 } 00995 } 00996 pos--; 00997 } 00998 while (pos >= 0) { 00999 if (mBuf[pos] == '\n') { 01000 if (++lineCount >= nLines) { 01001 IS_UTF8_ALIGNED2(this, (pos+1)) 01002 return pos + 1; 01003 } 01004 } 01005 pos--; 01006 } 01007 return 0; 01008 } 01009 01010 01011 /* 01012 Find a matching string in the buffer. 01013 */ 01014 int Fl_Text_Buffer::search_forward(int startPos, const char *searchString, 01015 int *foundPos, int matchCase) const 01016 { 01017 IS_UTF8_ALIGNED2(this, (startPos)) 01018 IS_UTF8_ALIGNED(searchString) 01019 01020 if (!searchString) 01021 return 0; 01022 int bp; 01023 const char *sp; 01024 if (matchCase) { 01025 while (startPos < length()) { 01026 bp = startPos; 01027 sp = searchString; 01028 for (;;) { 01029 char c = *sp; 01030 // we reached the end of the "needle", so we found the string! 01031 if (!c) { 01032 *foundPos = startPos; 01033 return 1; 01034 } 01035 int l = fl_utf8len1(c); 01036 if (memcmp(sp, address(bp), l)) 01037 break; 01038 sp += l; bp += l; 01039 } 01040 startPos = next_char(startPos); 01041 } 01042 } else { 01043 while (startPos < length()) { 01044 bp = startPos; 01045 sp = searchString; 01046 for (;;) { 01047 // we reached the end of the "needle", so we found the string! 01048 if (!*sp) { 01049 *foundPos = startPos; 01050 return 1; 01051 } 01052 int l; 01053 unsigned int b = char_at(bp); 01054 unsigned int s = fl_utf8decode(sp, 0, &l); 01055 if (fl_tolower(b)!=fl_tolower(s)) 01056 break; 01057 sp += l; 01058 bp = next_char(bp); 01059 } 01060 startPos = next_char(startPos); 01061 } 01062 } 01063 return 0; 01064 } 01065 01066 int Fl_Text_Buffer::search_backward(int startPos, const char *searchString, 01067 int *foundPos, int matchCase) const 01068 { 01069 IS_UTF8_ALIGNED2(this, (startPos)) 01070 IS_UTF8_ALIGNED(searchString) 01071 01072 if (!searchString) 01073 return 0; 01074 int bp; 01075 const char *sp; 01076 if (matchCase) { 01077 while (startPos >= 0) { 01078 bp = startPos; 01079 sp = searchString; 01080 for (;;) { 01081 char c = *sp; 01082 // we reached the end of the "needle", so we found the string! 01083 if (!c) { 01084 *foundPos = startPos; 01085 return 1; 01086 } 01087 int l = fl_utf8len1(c); 01088 if (memcmp(sp, address(bp), l)) 01089 break; 01090 sp += l; bp += l; 01091 } 01092 startPos = prev_char(startPos); 01093 } 01094 } else { 01095 while (startPos >= 0) { 01096 bp = startPos; 01097 sp = searchString; 01098 for (;;) { 01099 // we reached the end of the "needle", so we found the string! 01100 if (!*sp) { 01101 *foundPos = startPos; 01102 return 1; 01103 } 01104 int l; 01105 unsigned int b = char_at(bp); 01106 unsigned int s = fl_utf8decode(sp, 0, &l); 01107 if (fl_tolower(b)!=fl_tolower(s)) 01108 break; 01109 sp += l; 01110 bp = next_char(bp); 01111 } 01112 startPos = prev_char(startPos); 01113 } 01114 } 01115 return 0; 01116 } 01117 01118 01119 01120 /* 01121 Insert a string into the buffer. 01122 Pos must be at a character boundary. Text must be a correct UTF-8 string. 01123 */ 01124 int Fl_Text_Buffer::insert_(int pos, const char *text) 01125 { 01126 if (!text || !*text) 01127 return 0; 01128 01129 int insertedLength = strlen(text); 01130 01131 /* Prepare the buffer to receive the new text. If the new text fits in 01132 the current buffer, just move the gap (if necessary) to where 01133 the text should be inserted. If the new text is too large, reallocate 01134 the buffer with a gap large enough to accomodate the new text and a 01135 gap of mPreferredGapSize */ 01136 if (insertedLength > mGapEnd - mGapStart) 01137 reallocate_with_gap(pos, insertedLength + mPreferredGapSize); 01138 else if (pos != mGapStart) 01139 move_gap(pos); 01140 01141 /* Insert the new text (pos now corresponds to the start of the gap) */ 01142 memcpy(&mBuf[pos], text, insertedLength); 01143 mGapStart += insertedLength; 01144 mLength += insertedLength; 01145 update_selections(pos, 0, insertedLength); 01146 01147 if (mCanUndo) { 01148 if (undowidget == this && undoat == pos && undoinsert) { 01149 undoinsert += insertedLength; 01150 } else { 01151 undoinsert = insertedLength; 01152 undoyankcut = (undoat == pos) ? undocut : 0; 01153 } 01154 undoat = pos + insertedLength; 01155 undocut = 0; 01156 undowidget = this; 01157 } 01158 01159 return insertedLength; 01160 } 01161 01162 01163 /* 01164 Remove a string from the buffer. 01165 Unicode safe. Start and end must be at a character boundary. 01166 */ 01167 void Fl_Text_Buffer::remove_(int start, int end) 01168 { 01169 /* if the gap is not contiguous to the area to remove, move it there */ 01170 01171 if (mCanUndo) { 01172 if (undowidget == this && undoat == end && undocut) { 01173 undobuffersize(undocut + end - start + 1); 01174 memmove(undobuffer + end - start, undobuffer, undocut); 01175 undocut += end - start; 01176 } else { 01177 undocut = end - start; 01178 undobuffersize(undocut); 01179 } 01180 undoat = start; 01181 undoinsert = 0; 01182 undoyankcut = 0; 01183 undowidget = this; 01184 } 01185 01186 if (start > mGapStart) { 01187 if (mCanUndo) 01188 memcpy(undobuffer, mBuf + (mGapEnd - mGapStart) + start, 01189 end - start); 01190 move_gap(start); 01191 } else if (end < mGapStart) { 01192 if (mCanUndo) 01193 memcpy(undobuffer, mBuf + start, end - start); 01194 move_gap(end); 01195 } else { 01196 int prelen = mGapStart - start; 01197 if (mCanUndo) { 01198 memcpy(undobuffer, mBuf + start, prelen); 01199 memcpy(undobuffer + prelen, mBuf + mGapEnd, end - start - prelen); 01200 } 01201 } 01202 01203 /* expand the gap to encompass the deleted characters */ 01204 mGapEnd += end - mGapStart; 01205 mGapStart -= mGapStart - start; 01206 01207 /* update the length */ 01208 mLength -= end - start; 01209 01210 /* fix up any selections which might be affected by the change */ 01211 update_selections(start, end - start, 0); 01212 } 01213 01214 01215 /* 01216 simple setter. 01217 Unicode safe. Start and end must be at a character boundary. 01218 */ 01219 void Fl_Text_Selection::set(int startpos, int endpos) 01220 { 01221 mSelected = startpos != endpos; 01222 mStart = min(startpos, endpos); 01223 mEnd = max(startpos, endpos); 01224 } 01225 01226 01227 /* 01228 simple getter. 01229 Unicode safe. Start and end will be at a character boundary. 01230 */ 01231 int Fl_Text_Selection::position(int *startpos, int *endpos) const { 01232 if (!mSelected) 01233 return 0; 01234 *startpos = mStart; 01235 *endpos = mEnd; 01236 01237 return 1; 01238 } 01239 01240 01241 /* 01242 Return if a position is inside the selected area. 01243 Unicode safe. Pos must be at a character boundary. 01244 */ 01245 int Fl_Text_Selection::includes(int pos) const { 01246 return (selected() && pos >= start() && pos < end() ); 01247 } 01248 01249 01250 /* 01251 Return a duplicate of the selected text, or an empty string. 01252 Unicode safe. 01253 */ 01254 char *Fl_Text_Buffer::selection_text_(Fl_Text_Selection * sel) const { 01255 int start, end; 01256 01257 /* If there's no selection, return an allocated empty string */ 01258 if (!sel->position(&start, &end)) 01259 { 01260 char *s = (char *) malloc(1); 01261 *s = '\0'; 01262 return s; 01263 } 01264 01265 /* Return the selected range */ 01266 return text_range(start, end); 01267 } 01268 01269 01270 /* 01271 Remove the selected text. 01272 Unicode safe. 01273 */ 01274 void Fl_Text_Buffer::remove_selection_(Fl_Text_Selection * sel) 01275 { 01276 int start, end; 01277 01278 if (!sel->position(&start, &end)) 01279 return; 01280 remove(start, end); 01281 //undoyankcut = undocut; 01282 } 01283 01284 01285 /* 01286 Replace selection with text. 01287 Unicode safe. 01288 */ 01289 void Fl_Text_Buffer::replace_selection_(Fl_Text_Selection * sel, 01290 const char *text) 01291 { 01292 Fl_Text_Selection oldSelection = *sel; 01293 01294 /* If there's no selection, return */ 01295 int start, end; 01296 if (!sel->position(&start, &end)) 01297 return; 01298 01299 /* Do the appropriate type of replace */ 01300 replace(start, end, text); 01301 01302 /* Unselect (happens automatically in BufReplace, but BufReplaceRect 01303 can't detect when the contents of a selection goes away) */ 01304 sel->mSelected = 0; 01305 redisplay_selection(&oldSelection, sel); 01306 } 01307 01308 01309 /* 01310 Call all callbacks. 01311 Unicode safe. 01312 */ 01313 void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted, 01314 int nInserted, int nRestyled, 01315 const char *deletedText) const { 01316 IS_UTF8_ALIGNED2(this, pos) 01317 for (int i = 0; i < mNModifyProcs; i++) 01318 (*mModifyProcs[i]) (pos, nInserted, nDeleted, nRestyled, 01319 deletedText, mCbArgs[i]); 01320 } 01321 01322 01323 /* 01324 Call all callbacks. 01325 Unicode safe. 01326 */ 01327 void Fl_Text_Buffer::call_predelete_callbacks(int pos, int nDeleted) const { 01328 for (int i = 0; i < mNPredeleteProcs; i++) 01329 (*mPredeleteProcs[i]) (pos, nDeleted, mPredeleteCbArgs[i]); 01330 } 01331 01332 01333 /* 01334 Redisplay a new selected area. 01335 Unicode safe. 01336 */ 01337 void Fl_Text_Buffer::redisplay_selection(Fl_Text_Selection * 01338 oldSelection, 01339 Fl_Text_Selection * 01340 newSelection) const 01341 { 01342 int oldStart, oldEnd, newStart, newEnd, ch1Start, ch1End, ch2Start, 01343 ch2End; 01344 01345 /* If either selection is rectangular, add an additional character to 01346 the end of the selection to request the redraw routines to wipe out 01347 the parts of the selection beyond the end of the line */ 01348 oldStart = oldSelection->mStart; 01349 newStart = newSelection->mStart; 01350 oldEnd = oldSelection->mEnd; 01351 newEnd = newSelection->mEnd; 01352 01353 /* If the old or new selection is unselected, just redisplay the 01354 single area that is (was) selected and return */ 01355 if (!oldSelection->mSelected && !newSelection->mSelected) 01356 return; 01357 if (!oldSelection->mSelected) 01358 { 01359 call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL); 01360 return; 01361 } 01362 if (!newSelection->mSelected) { 01363 call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL); 01364 return; 01365 } 01366 01367 /* If the selections are non-contiguous, do two separate updates 01368 and return */ 01369 if (oldEnd < newStart || newEnd < oldStart) { 01370 call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL); 01371 call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL); 01372 return; 01373 } 01374 01375 /* Otherwise, separate into 3 separate regions: ch1, and ch2 (the two 01376 changed areas), and the unchanged area of their intersection, 01377 and update only the changed area(s) */ 01378 ch1Start = min(oldStart, newStart); 01379 ch2End = max(oldEnd, newEnd); 01380 ch1End = max(oldStart, newStart); 01381 ch2Start = min(oldEnd, newEnd); 01382 if (ch1Start != ch1End) 01383 call_modify_callbacks(ch1Start, 0, 0, ch1End - ch1Start, NULL); 01384 if (ch2Start != ch2End) 01385 call_modify_callbacks(ch2Start, 0, 0, ch2End - ch2Start, NULL); 01386 } 01387 01388 01389 /* 01390 Move the gap around without changing buffer content. 01391 Unicode safe. Pos must be at a character boundary. 01392 */ 01393 void Fl_Text_Buffer::move_gap(int pos) 01394 { 01395 int gapLen = mGapEnd - mGapStart; 01396 01397 if (pos > mGapStart) 01398 memmove(&mBuf[mGapStart], &mBuf[mGapEnd], pos - mGapStart); 01399 else 01400 memmove(&mBuf[pos + gapLen], &mBuf[pos], mGapStart - pos); 01401 mGapEnd += pos - mGapStart; 01402 mGapStart += pos - mGapStart; 01403 } 01404 01405 01406 /* 01407 Create a larger gap. 01408 Unicode safe. Start must be at a character boundary. 01409 */ 01410 void Fl_Text_Buffer::reallocate_with_gap(int newGapStart, int newGapLen) 01411 { 01412 char *newBuf = (char *) malloc(mLength + newGapLen); 01413 int newGapEnd = newGapStart + newGapLen; 01414 01415 if (newGapStart <= mGapStart) { 01416 memcpy(newBuf, mBuf, newGapStart); 01417 memcpy(&newBuf[newGapEnd], &mBuf[newGapStart], 01418 mGapStart - newGapStart); 01419 memcpy(&newBuf[newGapEnd + mGapStart - newGapStart], 01420 &mBuf[mGapEnd], mLength - mGapStart); 01421 } else { /* newGapStart > mGapStart */ 01422 memcpy(newBuf, mBuf, mGapStart); 01423 memcpy(&newBuf[mGapStart], &mBuf[mGapEnd], newGapStart - mGapStart); 01424 memcpy(&newBuf[newGapEnd], 01425 &mBuf[mGapEnd + newGapStart - mGapStart], 01426 mLength - newGapStart); 01427 } 01428 free((void *) mBuf); 01429 mBuf = newBuf; 01430 mGapStart = newGapStart; 01431 mGapEnd = newGapEnd; 01432 } 01433 01434 01435 /* 01436 Update selection range if characters were inserted. 01437 Unicode safe. Pos must be at a character boundary. 01438 */ 01439 void Fl_Text_Buffer::update_selections(int pos, int nDeleted, 01440 int nInserted) 01441 { 01442 mPrimary.update(pos, nDeleted, nInserted); 01443 mSecondary.update(pos, nDeleted, nInserted); 01444 mHighlight.update(pos, nDeleted, nInserted); 01445 } 01446 01447 01448 // unicode safe, assuming the arguments are on character boundaries 01449 void Fl_Text_Selection::update(int pos, int nDeleted, int nInserted) 01450 { 01451 if (!mSelected || pos > mEnd) 01452 return; 01453 if (pos + nDeleted <= mStart) { 01454 mStart += nInserted - nDeleted; 01455 mEnd += nInserted - nDeleted; 01456 } else if (pos <= mStart && pos + nDeleted >= mEnd) { 01457 mStart = pos; 01458 mEnd = pos; 01459 mSelected = 0; 01460 } else if (pos <= mStart && pos + nDeleted < mEnd) { 01461 mStart = pos; 01462 mEnd = nInserted + mEnd - nDeleted; 01463 } else if (pos < mEnd) { 01464 mEnd += nInserted - nDeleted; 01465 if (mEnd <= mStart) 01466 mSelected = 0; 01467 } 01468 } 01469 01470 01471 /* 01472 Find a UCS-4 character. 01473 StartPos must be at a character boundary, searchChar is UCS-4 encoded. 01474 */ 01475 int Fl_Text_Buffer::findchar_forward(int startPos, unsigned searchChar, 01476 int *foundPos) const 01477 { 01478 if (startPos >= mLength) { 01479 *foundPos = mLength; 01480 return 0; 01481 } 01482 01483 if (startPos<0) 01484 startPos = 0; 01485 01486 for ( ; startPos<mLength; startPos = next_char(startPos)) { 01487 if (searchChar == char_at(startPos)) { 01488 *foundPos = startPos; 01489 return 1; 01490 } 01491 } 01492 01493 *foundPos = mLength; 01494 return 0; 01495 } 01496 01497 01498 /* 01499 Find a UCS-4 character. 01500 StartPos must be at a character boundary, searchChar is UCS-4 encoded. 01501 */ 01502 int Fl_Text_Buffer::findchar_backward(int startPos, unsigned int searchChar, 01503 int *foundPos) const { 01504 if (startPos <= 0) { 01505 *foundPos = 0; 01506 return 0; 01507 } 01508 01509 if (startPos > mLength) 01510 startPos = mLength; 01511 01512 for (startPos = prev_char(startPos); startPos>=0; startPos = prev_char(startPos)) { 01513 if (searchChar == char_at(startPos)) { 01514 *foundPos = startPos; 01515 return 1; 01516 } 01517 } 01518 01519 *foundPos = 0; 01520 return 0; 01521 } 01522 01523 //#define EXAMPLE_ENCODING // shows how to process any encoding for which a decoding function exists 01524 #ifdef EXAMPLE_ENCODING 01525 01526 // returns the UCS equivalent of *p in CP1252 and advances p by 1 01527 unsigned cp1252toucs(char* &p) 01528 { 01529 // Codes 0x80..0x9f from the Microsoft CP1252 character set, translated 01530 // to Unicode 01531 static unsigned cp1252[32] = { 01532 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 01533 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, 01534 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 01535 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178 01536 }; 01537 unsigned char uc = *(unsigned char*)p; 01538 p++; 01539 return (uc < 0x80 || uc >= 0xa0 ? uc : cp1252[uc - 0x80]); 01540 } 01541 01542 // returns the UCS equivalent of *p in UTF-16 and advances p by 2 (or more for surrogates) 01543 unsigned utf16toucs(char* &p) 01544 { 01545 union { 01546 #if WORDS_BIGENDIAN 01547 struct { unsigned char a, b;} chars; 01548 #else 01549 struct { unsigned char b, a;} chars; 01550 #endif 01551 U16 short_val; 01552 } u; 01553 u.chars.a = *(unsigned char*)p++; 01554 u.chars.b = *(unsigned char*)p++; 01555 return u.short_val; 01556 } 01557 01558 // filter that produces, from an input stream fed by reading from fp, 01559 // a UTF-8-encoded output stream written in buffer. 01560 // Input can be any (e.g., 8-bit, UTF-16) encoding. 01561 // Output is true UTF-8. 01562 // p_trf points to a function that transforms encoded byte(s) into one UCS 01563 // and that increases the pointer by the adequate quantity 01564 static int general_input_filter(char *buffer, int buflen, 01565 char *line, int sline, char* &endline, 01566 unsigned (*p_trf)(char* &), 01567 FILE *fp) 01568 { 01569 char *p, *q, multibyte[5]; 01570 int lq, r, offset; 01571 p = endline = line; 01572 q = buffer; 01573 while (q < buffer + buflen) { 01574 if (p >= endline) { 01575 r = fread(line, 1, sline, fp); 01576 endline = line + r; 01577 if (r == 0) return q - buffer; 01578 p = line; 01579 } 01580 if (q + 4 /*max width of utf-8 char*/ > buffer + buflen) { 01581 memmove(line, p, endline - p); 01582 endline -= (p - line); 01583 return q - buffer; 01584 } 01585 lq = fl_utf8encode( p_trf(p), multibyte ); 01586 memcpy(q, multibyte, lq); 01587 q += lq; 01588 } 01589 memmove(line, p, endline - p); 01590 endline -= (p - line); 01591 return q - buffer; 01592 } 01593 #endif // EXAMPLE_ENCODING 01594 01595 /* 01596 filter that produces, from an input stream fed by reading from fp, 01597 a UTF-8-encoded output stream written in buffer. 01598 Input can be UTF-8. If it is not, it is decoded with CP1252. 01599 Output is UTF-8. 01600 *input_was_changed is set to true if the input was not strict UTF-8 so output 01601 differs from input. 01602 */ 01603 static int utf8_input_filter(char *buffer, int buflen, char *line, int sline, char* &endline, 01604 FILE *fp, int *input_was_changed) 01605 { 01606 char *p, *q, multibyte[5]; 01607 int l, lp, lq, r; 01608 unsigned u; 01609 p = endline = line; 01610 q = buffer; 01611 while (q < buffer + buflen) { 01612 if (p >= endline) { 01613 r = fread(line, 1, sline, fp); 01614 endline = line + r; 01615 if (r == 0) return q - buffer; 01616 p = line; 01617 } 01618 l = fl_utf8len1(*p); 01619 if (p + l > endline) { 01620 memmove(line, p, endline - p); 01621 endline -= (p - line); 01622 r = fread(endline, 1, sline - (endline - line), fp); 01623 endline += r; 01624 p = line; 01625 if (endline - line < l) break; 01626 } 01627 while ( l > 0) { 01628 u = fl_utf8decode(p, p+l, &lp); 01629 lq = fl_utf8encode(u, multibyte); 01630 if (lp != l || lq != l) *input_was_changed = true; 01631 if (q + lq > buffer + buflen) { 01632 memmove(line, p, endline - p); 01633 endline -= (p - line); 01634 return q - buffer; 01635 } 01636 memcpy(q, multibyte, lq); 01637 q += lq; 01638 p += lp; 01639 l -= lp; 01640 } 01641 } 01642 memmove(line, p, endline - p); 01643 endline -= (p - line); 01644 return q - buffer; 01645 } 01646 01647 const char *Fl_Text_Buffer::file_encoding_warning_message = 01648 "Displayed text contains the UTF-8 transcoding\n" 01649 "of the input file which was not UTF-8 encoded.\n" 01650 "Some changes may have occurred."; 01651 01652 /* 01653 Insert text from a file. 01654 Input file can be of various encodings according to what input fiter is used. 01655 utf8_input_filter accepts UTF-8 or CP1252 as input encoding. 01656 Output is always UTF-8. 01657 */ 01658 int Fl_Text_Buffer::insertfile(const char *file, int pos, int buflen) 01659 { 01660 FILE *fp; 01661 if (!(fp = fl_fopen(file, "r"))) 01662 return 1; 01663 char *buffer = new char[buflen + 1]; 01664 char *endline, line[100]; 01665 int l; 01666 input_file_was_transcoded = false; 01667 endline = line; 01668 while (true) { 01669 #ifdef EXAMPLE_ENCODING 01670 // example of 16-bit encoding: UTF-16 01671 l = general_input_filter(buffer, buflen, 01672 line, sizeof(line), endline, 01673 utf16toucs, // use cp1252toucs to read CP1252-encoded files 01674 fp); 01675 input_file_was_transcoded = true; 01676 #else 01677 l = utf8_input_filter(buffer, buflen, line, sizeof(line), endline, 01678 fp, &input_file_was_transcoded); 01679 #endif 01680 if (l == 0) break; 01681 buffer[l] = 0; 01682 insert(pos, buffer); 01683 pos += l; 01684 } 01685 int e = ferror(fp) ? 2 : 0; 01686 fclose(fp); 01687 delete[]buffer; 01688 if ( (!e) && input_file_was_transcoded && transcoding_warning_action) { 01689 transcoding_warning_action(this); 01690 } 01691 return e; 01692 } 01693 01694 01695 /* 01696 Write text to file. 01697 Unicode safe. 01698 */ 01699 int Fl_Text_Buffer::outputfile(const char *file, 01700 int start, int end, 01701 int buflen) { 01702 FILE *fp; 01703 if (!(fp = fl_fopen(file, "w"))) 01704 return 1; 01705 for (int n; (n = min(end - start, buflen)); start += n) { 01706 const char *p = text_range(start, start + n); 01707 int r = fwrite(p, 1, n, fp); 01708 free((void *) p); 01709 if (r != n) 01710 break; 01711 } 01712 01713 int e = ferror(fp) ? 2 : 0; 01714 fclose(fp); 01715 return e; 01716 } 01717 01718 01719 /* 01720 Return the previous character position. 01721 Unicode safe. 01722 */ 01723 int Fl_Text_Buffer::prev_char_clipped(int pos) const 01724 { 01725 if (pos<=0) 01726 return 0; 01727 01728 IS_UTF8_ALIGNED2(this, (pos)) 01729 01730 char c; 01731 do { 01732 pos--; 01733 if (pos==0) 01734 return 0; 01735 c = byte_at(pos); 01736 } while ( (c&0xc0) == 0x80); 01737 01738 IS_UTF8_ALIGNED2(this, (pos)) 01739 return pos; 01740 } 01741 01742 01743 /* 01744 Return the previous character position. 01745 Returns -1 if the beginning of the buffer is reached. 01746 */ 01747 int Fl_Text_Buffer::prev_char(int pos) const 01748 { 01749 if (pos==0) return -1; 01750 return prev_char_clipped(pos); 01751 } 01752 01753 01754 /* 01755 Return the next character position. 01756 Returns length() if the end of the buffer is reached. 01757 */ 01758 int Fl_Text_Buffer::next_char(int pos) const 01759 { 01760 IS_UTF8_ALIGNED2(this, (pos)) 01761 int n = fl_utf8len1(byte_at(pos)); 01762 pos += n; 01763 if (pos>=mLength) 01764 return mLength; 01765 IS_UTF8_ALIGNED2(this, (pos)) 01766 return pos; 01767 } 01768 01769 01770 /* 01771 Return the next character position. 01772 If the end of the buffer is reached, it returns the current position. 01773 */ 01774 int Fl_Text_Buffer::next_char_clipped(int pos) const 01775 { 01776 return next_char(pos); 01777 } 01778 01779 /* 01780 Align an index to the current UTF-8 boundary. 01781 */ 01782 int Fl_Text_Buffer::utf8_align(int pos) const 01783 { 01784 char c = byte_at(pos); 01785 while ( (c&0xc0) == 0x80) { 01786 pos--; 01787 c = byte_at(pos); 01788 } 01789 return pos; 01790 } 01791 01792 // 01793 // End of "$Id: Fl_Text_Buffer.cxx 8040 2010-12-15 17:38:39Z manolo $". 01794 //