themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_Text_Buffer.cxx
blob9714f51e5975723d7fda2bc03da2baa63c4de8eb
1 //
2 // "$Id: Fl_Text_Buffer.cxx 8040 2010-12-15 17:38:39Z manolo $"
3 //
4 // Copyright 2001-2010 by Bill Spitzak and others.
5 // Original code Copyright Mark Edel. Permission to distribute under
6 // the LGPL for the FLTK library granted by Mark Edel.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <FL/fl_utf8.h>
31 #include "flstring.h"
32 #include <ctype.h>
33 #include <FL/Fl.H>
34 #include <FL/Fl_Text_Buffer.H>
35 #include <FL/fl_ask.H>
39 This file is based on a port of NEdit to FLTK many years ago. NEdit at that
40 point was already stretched beyond the task it was designed for which explains
41 why the source code is sometimes pretty convoluted. It still is a very useful
42 widget for FLTK, and we are thankful that the nedit team allowed us to
43 integrate their code.
45 With the introduction of Unicode and UTF-8, Fl_Text_... has to go into a whole
46 new generation of code. Originally designed for monospaced fonts only, many
47 features make less sense in the multibyte and multiwidth world of UTF-8.
49 Columns are a good example. There is simply no such thing. The new Fl_Text_...
50 widget converts columns to pixels by multiplying them with the average
51 character width for a given font.
53 Rectangular selections were rarely used (if at all) and make little sense when
54 using variable width fonts. They have been removed.
56 Using multiple spaces to emulate tab stops has been replaced by pixel counting
57 routines. They are slower, but give the expected result for proportional fonts.
59 And constantly recalculating character widths is just much too expensive. Lines
60 of text are now subdivided into blocks of text which are measured at once
61 instead of individual characters.
65 #ifndef min
67 static int max(int i1, int i2)
69 return i1 >= i2 ? i1 : i2;
72 static int min(int i1, int i2)
74 return i1 <= i2 ? i1 : i2;
77 #endif
80 static char *undobuffer;
81 static int undobufferlength;
82 static Fl_Text_Buffer *undowidget;
83 static int undoat; // points after insertion
84 static int undocut; // number of characters deleted there
85 static int undoinsert; // number of characters inserted
86 static int undoyankcut; // length of valid contents of buffer, even if undocut=0
89 Resize the undo buffer to match at least the requested size.
91 static void undobuffersize(int n)
93 if (n > undobufferlength) {
94 if (undobuffer) {
95 do {
96 undobufferlength *= 2;
97 } while (undobufferlength < n);
98 undobuffer = (char *) realloc(undobuffer, undobufferlength);
99 } else {
100 undobufferlength = n + 9;
101 undobuffer = (char *) malloc(undobufferlength);
106 static void def_transcoding_warning_action(Fl_Text_Buffer *text)
108 fl_alert("%s", text->file_encoding_warning_message);
112 Initialize all variables.
114 Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize)
116 mLength = 0;
117 mPreferredGapSize = preferredGapSize;
118 mBuf = (char *) malloc(requestedSize + mPreferredGapSize);
119 mGapStart = 0;
120 mGapEnd = mPreferredGapSize;
121 mTabDist = 8;
122 mPrimary.mSelected = 0;
123 mPrimary.mStart = mPrimary.mEnd = 0;
124 mSecondary.mSelected = 0;
125 mSecondary.mStart = mSecondary.mEnd = 0;
126 mHighlight.mSelected = 0;
127 mHighlight.mStart = mHighlight.mEnd = 0;
128 mModifyProcs = NULL;
129 mCbArgs = NULL;
130 mNModifyProcs = 0;
131 mNPredeleteProcs = 0;
132 mPredeleteProcs = NULL;
133 mPredeleteCbArgs = NULL;
134 mCursorPosHint = 0;
135 mCanUndo = 1;
136 input_file_was_transcoded = 0;
137 transcoding_warning_action = def_transcoding_warning_action;
142 Free all resources.
144 Fl_Text_Buffer::~Fl_Text_Buffer()
146 free(mBuf);
147 if (mNModifyProcs != 0) {
148 delete[]mModifyProcs;
149 delete[]mCbArgs;
151 if (mNPredeleteProcs != 0) {
152 delete[]mPredeleteProcs;
153 delete[]mPredeleteCbArgs;
159 This function copies verbose whatever is in front and after the gap into a
160 single buffer.
162 char *Fl_Text_Buffer::text() const {
163 char *t = (char *) malloc(mLength + 1);
164 memcpy(t, mBuf, mGapStart);
165 memcpy(t+mGapStart, mBuf+mGapEnd, mLength - mGapStart);
166 t[mLength] = '\0';
167 return t;
172 Set the text buffer to a new string.
174 void Fl_Text_Buffer::text(const char *t)
176 IS_UTF8_ALIGNED(t)
178 call_predelete_callbacks(0, length());
180 /* Save information for redisplay, and get rid of the old buffer */
181 const char *deletedText = text();
182 int deletedLength = mLength;
183 free((void *) mBuf);
185 /* Start a new buffer with a gap of mPreferredGapSize at the end */
186 int insertedLength = strlen(t);
187 mBuf = (char *) malloc(insertedLength + mPreferredGapSize);
188 mLength = insertedLength;
189 mGapStart = insertedLength;
190 mGapEnd = mGapStart + mPreferredGapSize;
191 memcpy(mBuf, t, insertedLength);
193 /* Zero all of the existing selections */
194 update_selections(0, deletedLength, 0);
196 /* Call the saved display routine(s) to update the screen */
197 call_modify_callbacks(0, deletedLength, insertedLength, 0, deletedText);
198 free((void *) deletedText);
203 Creates a range of text to a new buffer and copies verbose from around the gap.
205 char *Fl_Text_Buffer::text_range(int start, int end) const {
206 IS_UTF8_ALIGNED2(this, (start))
207 IS_UTF8_ALIGNED2(this, (end))
209 char *s = NULL;
211 /* Make sure start and end are ok, and allocate memory for returned string.
212 If start is bad, return "", if end is bad, adjust it. */
213 if (start < 0 || start > mLength)
215 s = (char *) malloc(1);
216 s[0] = '\0';
217 return s;
219 if (end < start) {
220 int temp = start;
221 start = end;
222 end = temp;
224 if (end > mLength)
225 end = mLength;
226 int copiedLength = end - start;
227 s = (char *) malloc(copiedLength + 1);
229 /* Copy the text from the buffer to the returned string */
230 if (end <= mGapStart) {
231 memcpy(s, mBuf + start, copiedLength);
232 } else if (start >= mGapStart) {
233 memcpy(s, mBuf + start + (mGapEnd - mGapStart), copiedLength);
234 } else {
235 int part1Length = mGapStart - start;
236 memcpy(s, mBuf + start, part1Length);
237 memcpy(s + part1Length, mBuf + mGapEnd, copiedLength - part1Length);
239 s[copiedLength] = '\0';
240 return s;
244 Return a UCS-4 character at the given index.
245 Pos must be at a character boundary.
247 unsigned int Fl_Text_Buffer::char_at(int pos) const {
248 if (pos < 0 || pos >= mLength)
249 return '\0';
251 IS_UTF8_ALIGNED2(this, (pos))
253 const char *src = address(pos);
254 return fl_utf8decode(src, 0, 0);
259 Return the raw byte at the given index.
260 This function ignores all unicode encoding.
262 char Fl_Text_Buffer::byte_at(int pos) const {
263 if (pos < 0 || pos >= mLength)
264 return '\0';
265 const char *src = address(pos);
266 return *src;
271 Insert some text at the given index.
272 Pos must be at a character boundary.
274 void Fl_Text_Buffer::insert(int pos, const char *text)
276 IS_UTF8_ALIGNED2(this, (pos))
277 IS_UTF8_ALIGNED(text)
279 /* check if there is actually any text */
280 if (!text || !*text)
281 return;
283 /* if pos is not contiguous to existing text, make it */
284 if (pos > mLength)
285 pos = mLength;
286 if (pos < 0)
287 pos = 0;
289 /* Even if nothing is deleted, we must call these callbacks */
290 call_predelete_callbacks(pos, 0);
292 /* insert and redisplay */
293 int nInserted = insert_(pos, text);
294 mCursorPosHint = pos + nInserted;
295 IS_UTF8_ALIGNED2(this, (mCursorPosHint))
296 call_modify_callbacks(pos, 0, nInserted, 0, NULL);
301 Replace a range of text with new text.
302 Start and end must be at a character boundary.
304 void Fl_Text_Buffer::replace(int start, int end, const char *text)
306 // Range check...
307 if (!text)
308 return;
309 if (start < 0)
310 start = 0;
311 if (end > mLength)
312 end = mLength;
314 IS_UTF8_ALIGNED2(this, (start))
315 IS_UTF8_ALIGNED2(this, (end))
316 IS_UTF8_ALIGNED(text)
318 call_predelete_callbacks(start, end - start);
319 const char *deletedText = text_range(start, end);
320 remove_(start, end);
321 int nInserted = insert_(start, text);
322 mCursorPosHint = start + nInserted;
323 call_modify_callbacks(start, end - start, nInserted, 0, deletedText);
324 free((void *) deletedText);
329 Remove a range of text.
330 Start and End must be at a character boundary.
332 void Fl_Text_Buffer::remove(int start, int end)
334 /* Make sure the arguments make sense */
335 if (start > end) {
336 int temp = start;
337 start = end;
338 end = temp;
340 if (start > mLength)
341 start = mLength;
342 if (start < 0)
343 start = 0;
344 if (end > mLength)
345 end = mLength;
346 if (end < 0)
347 end = 0;
349 IS_UTF8_ALIGNED2(this, (start))
350 IS_UTF8_ALIGNED2(this, (end))
352 if (start == end)
353 return;
355 call_predelete_callbacks(start, end - start);
356 /* Remove and redisplay */
357 const char *deletedText = text_range(start, end);
358 remove_(start, end);
359 mCursorPosHint = start;
360 call_modify_callbacks(start, end - start, 0, 0, deletedText);
361 free((void *) deletedText);
366 Copy a range of text from another text buffer.
367 fromStart, fromEnd, and toPos must be at a character boundary.
369 void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart,
370 int fromEnd, int toPos)
372 IS_UTF8_ALIGNED2(fromBuf, fromStart)
373 IS_UTF8_ALIGNED2(fromBuf, fromEnd)
374 IS_UTF8_ALIGNED2(this, (toPos))
376 int copiedLength = fromEnd - fromStart;
378 /* Prepare the buffer to receive the new text. If the new text fits in
379 the current buffer, just move the gap (if necessary) to where
380 the text should be inserted. If the new text is too large, reallocate
381 the buffer with a gap large enough to accomodate the new text and a
382 gap of mPreferredGapSize */
383 if (copiedLength > mGapEnd - mGapStart)
384 reallocate_with_gap(toPos, copiedLength + mPreferredGapSize);
385 else if (toPos != mGapStart)
386 move_gap(toPos);
388 /* Insert the new text (toPos now corresponds to the start of the gap) */
389 if (fromEnd <= fromBuf->mGapStart) {
390 memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], copiedLength);
391 } else if (fromStart >= fromBuf->mGapStart) {
392 memcpy(&mBuf[toPos],
393 &fromBuf->mBuf[fromStart + (fromBuf->mGapEnd - fromBuf->mGapStart)],
394 copiedLength);
395 } else {
396 int part1Length = fromBuf->mGapStart - fromStart;
397 memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], part1Length);
398 memcpy(&mBuf[toPos + part1Length],
399 &fromBuf->mBuf[fromBuf->mGapEnd], copiedLength - part1Length);
401 mGapStart += copiedLength;
402 mLength += copiedLength;
403 update_selections(toPos, 0, copiedLength);
408 Take the previous changes and undo them. Return the previous
409 cursor position in cursorPos. Returns 1 if the undo was applied.
410 CursorPos will be at a character boundary.
412 int Fl_Text_Buffer::undo(int *cursorPos)
414 if (undowidget != this || (!undocut && !undoinsert && !mCanUndo))
415 return 0;
417 int ilen = undocut;
418 int xlen = undoinsert;
419 int b = undoat - xlen;
421 if (xlen && undoyankcut && !ilen) {
422 ilen = undoyankcut;
425 if (xlen && ilen) {
426 undobuffersize(ilen + 1);
427 undobuffer[ilen] = 0;
428 char *tmp = strdup(undobuffer);
429 replace(b, undoat, tmp);
430 if (cursorPos)
431 *cursorPos = mCursorPosHint;
432 free(tmp);
433 } else if (xlen) {
434 remove(b, undoat);
435 if (cursorPos)
436 *cursorPos = mCursorPosHint;
437 } else if (ilen) {
438 undobuffersize(ilen + 1);
439 undobuffer[ilen] = 0;
440 insert(undoat, undobuffer);
441 if (cursorPos)
442 *cursorPos = mCursorPosHint;
443 undoyankcut = 0;
446 return 1;
451 Set a flag if undo function will work.
453 void Fl_Text_Buffer::canUndo(char flag)
455 mCanUndo = flag;
456 // disabling undo also clears the last undo operation!
457 if (!mCanUndo && undowidget==this)
458 undowidget = 0;
463 Change the tab width. This will cause a couple of callbacks and a complete
464 redisplay.
465 Matt: I am not entirely sure why we need to trigger callbacks because
466 tabs are only a graphical hint, not changing any text at all, but I leave
467 this in here for back compatibility.
469 void Fl_Text_Buffer::tab_distance(int tabDist)
471 /* First call the pre-delete callbacks with the previous tab setting
472 still active. */
473 call_predelete_callbacks(0, mLength);
475 /* Change the tab setting */
476 mTabDist = tabDist;
478 /* Force any display routines to redisplay everything (unfortunately,
479 this means copying the whole buffer contents to provide "deletedText" */
480 const char *deletedText = text();
481 call_modify_callbacks(0, mLength, mLength, 0, deletedText);
482 free((void *) deletedText);
487 Select a range of text.
488 Start and End must be at a character boundary.
490 void Fl_Text_Buffer::select(int start, int end)
492 IS_UTF8_ALIGNED2(this, (start))
493 IS_UTF8_ALIGNED2(this, (end))
495 Fl_Text_Selection oldSelection = mPrimary;
497 mPrimary.set(start, end);
498 redisplay_selection(&oldSelection, &mPrimary);
503 Clear the primary selection.
505 void Fl_Text_Buffer::unselect()
507 Fl_Text_Selection oldSelection = mPrimary;
509 mPrimary.mSelected = 0;
510 redisplay_selection(&oldSelection, &mPrimary);
515 Return the primary selection range.
517 int Fl_Text_Buffer::selection_position(int *start, int *end)
519 return mPrimary.position(start, end);
524 Return a copy of the selected text.
526 char *Fl_Text_Buffer::selection_text()
528 return selection_text_(&mPrimary);
533 Remove the selected text.
535 void Fl_Text_Buffer::remove_selection()
537 remove_selection_(&mPrimary);
542 Replace the selected text.
544 void Fl_Text_Buffer::replace_selection(const char *text)
546 replace_selection_(&mPrimary, text);
551 Select text.
552 Start and End must be at a character boundary.
554 void Fl_Text_Buffer::secondary_select(int start, int end)
556 Fl_Text_Selection oldSelection = mSecondary;
558 mSecondary.set(start, end);
559 redisplay_selection(&oldSelection, &mSecondary);
564 Deselect text.
566 void Fl_Text_Buffer::secondary_unselect()
568 Fl_Text_Selection oldSelection = mSecondary;
570 mSecondary.mSelected = 0;
571 redisplay_selection(&oldSelection, &mSecondary);
576 Return the selected range.
578 int Fl_Text_Buffer::secondary_selection_position(int *start, int *end)
580 return mSecondary.position(start, end);
585 Return a copy of the text in this selection.
587 char *Fl_Text_Buffer::secondary_selection_text()
589 return selection_text_(&mSecondary);
594 Remove the selected text.
596 void Fl_Text_Buffer::remove_secondary_selection()
598 remove_selection_(&mSecondary);
603 Replace selected text.
605 void Fl_Text_Buffer::replace_secondary_selection(const char *text)
607 replace_selection_(&mSecondary, text);
612 Highlight a range of text.
613 Start and End must be at a character boundary.
615 void Fl_Text_Buffer::highlight(int start, int end)
617 Fl_Text_Selection oldSelection = mHighlight;
619 mHighlight.set(start, end);
620 redisplay_selection(&oldSelection, &mHighlight);
625 Remove text highlighting.
627 void Fl_Text_Buffer::unhighlight()
629 Fl_Text_Selection oldSelection = mHighlight;
631 mHighlight.mSelected = 0;
632 redisplay_selection(&oldSelection, &mHighlight);
637 Return position of highlight.
639 int Fl_Text_Buffer::highlight_position(int *start, int *end)
641 return mHighlight.position(start, end);
646 Return a copy of highlighted text.
648 char *Fl_Text_Buffer::highlight_text()
650 return selection_text_(&mHighlight);
655 Add a callback that is called whenever text is modified.
657 void Fl_Text_Buffer::add_modify_callback(Fl_Text_Modify_Cb bufModifiedCB,
658 void *cbArg)
660 Fl_Text_Modify_Cb *newModifyProcs =
661 new Fl_Text_Modify_Cb[mNModifyProcs + 1];
662 void **newCBArgs = new void *[mNModifyProcs + 1];
663 for (int i = 0; i < mNModifyProcs; i++) {
664 newModifyProcs[i + 1] = mModifyProcs[i];
665 newCBArgs[i + 1] = mCbArgs[i];
667 if (mNModifyProcs != 0) {
668 delete[]mModifyProcs;
669 delete[]mCbArgs;
671 newModifyProcs[0] = bufModifiedCB;
672 newCBArgs[0] = cbArg;
673 mNModifyProcs++;
674 mModifyProcs = newModifyProcs;
675 mCbArgs = newCBArgs;
680 Remove a callback.
682 void Fl_Text_Buffer::remove_modify_callback(Fl_Text_Modify_Cb bufModifiedCB,
683 void *cbArg)
685 int i, toRemove = -1;
687 /* find the matching callback to remove */
688 for (i = 0; i < mNModifyProcs; i++) {
689 if (mModifyProcs[i] == bufModifiedCB && mCbArgs[i] == cbArg) {
690 toRemove = i;
691 break;
694 if (toRemove == -1) {
695 Fl::error
696 ("Fl_Text_Buffer::remove_modify_callback(): Can't find modify CB to remove");
697 return;
700 /* Allocate new lists for remaining callback procs and args (if
701 any are left) */
702 mNModifyProcs--;
703 if (mNModifyProcs == 0) {
704 mNModifyProcs = 0;
705 delete[]mModifyProcs;
706 mModifyProcs = NULL;
707 delete[]mCbArgs;
708 mCbArgs = NULL;
709 return;
711 Fl_Text_Modify_Cb *newModifyProcs = new Fl_Text_Modify_Cb[mNModifyProcs];
712 void **newCBArgs = new void *[mNModifyProcs];
714 /* copy out the remaining members and free the old lists */
715 for (i = 0; i < toRemove; i++) {
716 newModifyProcs[i] = mModifyProcs[i];
717 newCBArgs[i] = mCbArgs[i];
719 for (; i < mNModifyProcs; i++) {
720 newModifyProcs[i] = mModifyProcs[i + 1];
721 newCBArgs[i] = mCbArgs[i + 1];
723 delete[]mModifyProcs;
724 delete[]mCbArgs;
725 mModifyProcs = newModifyProcs;
726 mCbArgs = newCBArgs;
731 Add a callback that is called before deleting text.
733 void Fl_Text_Buffer::add_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB,
734 void *cbArg)
736 Fl_Text_Predelete_Cb *newPreDeleteProcs =
737 new Fl_Text_Predelete_Cb[mNPredeleteProcs + 1];
738 void **newCBArgs = new void *[mNPredeleteProcs + 1];
739 for (int i = 0; i < mNPredeleteProcs; i++) {
740 newPreDeleteProcs[i + 1] = mPredeleteProcs[i];
741 newCBArgs[i + 1] = mPredeleteCbArgs[i];
743 if (!mNPredeleteProcs != 0) {
744 delete[]mPredeleteProcs;
745 delete[]mPredeleteCbArgs;
747 newPreDeleteProcs[0] = bufPreDeleteCB;
748 newCBArgs[0] = cbArg;
749 mNPredeleteProcs++;
750 mPredeleteProcs = newPreDeleteProcs;
751 mPredeleteCbArgs = newCBArgs;
756 Remove a callback.
758 void Fl_Text_Buffer::remove_predelete_callback(Fl_Text_Predelete_Cb bufPreDeleteCB, void *cbArg)
760 int i, toRemove = -1;
761 /* find the matching callback to remove */
762 for (i = 0; i < mNPredeleteProcs; i++) {
763 if (mPredeleteProcs[i] == bufPreDeleteCB &&
764 mPredeleteCbArgs[i] == cbArg) {
765 toRemove = i;
766 break;
769 if (toRemove == -1) {
770 Fl::error
771 ("Fl_Text_Buffer::remove_predelete_callback(): Can't find pre-delete CB to remove");
772 return;
775 /* Allocate new lists for remaining callback procs and args (if
776 any are left) */
777 mNPredeleteProcs--;
778 if (mNPredeleteProcs == 0) {
779 mNPredeleteProcs = 0;
780 delete[]mPredeleteProcs;
781 mPredeleteProcs = NULL;
782 delete[]mPredeleteCbArgs;
783 mPredeleteCbArgs = NULL;
784 return;
786 Fl_Text_Predelete_Cb *newPreDeleteProcs =
787 new Fl_Text_Predelete_Cb[mNPredeleteProcs];
788 void **newCBArgs = new void *[mNPredeleteProcs];
790 /* copy out the remaining members and free the old lists */
791 for (i = 0; i < toRemove; i++) {
792 newPreDeleteProcs[i] = mPredeleteProcs[i];
793 newCBArgs[i] = mPredeleteCbArgs[i];
795 for (; i < mNPredeleteProcs; i++) {
796 newPreDeleteProcs[i] = mPredeleteProcs[i + 1];
797 newCBArgs[i] = mPredeleteCbArgs[i + 1];
799 delete[]mPredeleteProcs;
800 delete[]mPredeleteCbArgs;
801 mPredeleteProcs = newPreDeleteProcs;
802 mPredeleteCbArgs = newCBArgs;
807 Return a copy of the line that contains a given index.
808 Pos must be at a character boundary.
810 char *Fl_Text_Buffer::line_text(int pos) const {
811 return text_range(line_start(pos), line_end(pos));
816 Find the beginning of the line.
818 int Fl_Text_Buffer::line_start(int pos) const
820 if (!findchar_backward(pos, '\n', &pos))
821 return 0;
822 return pos + 1;
827 Find the end of the line.
829 int Fl_Text_Buffer::line_end(int pos) const {
830 if (!findchar_forward(pos, '\n', &pos))
831 pos = mLength;
832 return pos;
837 Find the beginning of a word.
838 NOT UNICODE SAFE.
840 int Fl_Text_Buffer::word_start(int pos) const {
841 // FIXME: character is ucs-4
842 while (pos>0 && (isalnum(char_at(pos)) || char_at(pos) == '_')) {
843 pos = prev_char(pos);
845 // FIXME: character is ucs-4
846 if (!(isalnum(char_at(pos)) || char_at(pos) == '_'))
847 pos = next_char(pos);
848 return pos;
853 Find the end of a word.
854 NOT UNICODE SAFE.
856 int Fl_Text_Buffer::word_end(int pos) const {
857 // FIXME: character is ucs-4
858 while (pos < length() && (isalnum(char_at(pos)) || char_at(pos) == '_'))
860 pos = next_char(pos);
862 return pos;
867 Count the number of characters between two positions.
869 int Fl_Text_Buffer::count_displayed_characters(int lineStartPos,
870 int targetPos) const
872 IS_UTF8_ALIGNED2(this, (lineStartPos))
873 IS_UTF8_ALIGNED2(this, (targetPos))
875 int charCount = 0;
877 int pos = lineStartPos;
878 while (pos < targetPos) {
879 pos = next_char(pos);
880 charCount++;
882 return charCount;
887 Skip ahead a number of characters from a given index.
888 This function breaks early if it encounters a newline character.
890 int Fl_Text_Buffer::skip_displayed_characters(int lineStartPos, int nChars)
892 IS_UTF8_ALIGNED2(this, (lineStartPos))
894 int pos = lineStartPos;
896 for (int charCount = 0; charCount < nChars && pos < mLength; charCount++) {
897 unsigned int c = char_at(pos);
898 if (c == '\n')
899 return pos;
900 pos = next_char(pos);
902 return pos;
907 Count the number of newline characters between start and end.
908 startPos and endPos must be at a character boundary.
909 This function is optimized for speed by not using UTF-8 calls.
911 int Fl_Text_Buffer::count_lines(int startPos, int endPos) const {
912 IS_UTF8_ALIGNED2(this, (startPos))
913 IS_UTF8_ALIGNED2(this, (endPos))
915 int gapLen = mGapEnd - mGapStart;
916 int lineCount = 0;
918 int pos = startPos;
919 while (pos < mGapStart)
921 if (pos == endPos)
922 return lineCount;
923 if (mBuf[pos++] == '\n')
924 lineCount++;
926 while (pos < mLength) {
927 if (pos == endPos)
928 return lineCount;
929 if (mBuf[pos++ + gapLen] == '\n')
930 lineCount++;
932 return lineCount;
937 Skip to the first character, n lines ahead.
938 StartPos must be at a character boundary.
939 This function is optimized for speed by not using UTF-8 calls.
941 int Fl_Text_Buffer::skip_lines(int startPos, int nLines)
943 IS_UTF8_ALIGNED2(this, (startPos))
945 if (nLines == 0)
946 return startPos;
948 int gapLen = mGapEnd - mGapStart;
949 int pos = startPos;
950 int lineCount = 0;
951 while (pos < mGapStart) {
952 if (mBuf[pos++] == '\n') {
953 lineCount++;
954 if (lineCount == nLines) {
955 IS_UTF8_ALIGNED2(this, (pos))
956 return pos;
960 while (pos < mLength) {
961 if (mBuf[pos++ + gapLen] == '\n') {
962 lineCount++;
963 if (lineCount >= nLines) {
964 IS_UTF8_ALIGNED2(this, (pos))
965 return pos;
969 IS_UTF8_ALIGNED2(this, (pos))
970 return pos;
975 Skip to the first character, n lines back.
976 StartPos must be at a character boundary.
977 This function is optimized for speed by not using UTF-8 calls.
979 int Fl_Text_Buffer::rewind_lines(int startPos, int nLines)
981 IS_UTF8_ALIGNED2(this, (startPos))
983 int pos = startPos - 1;
984 if (pos <= 0)
985 return 0;
987 int gapLen = mGapEnd - mGapStart;
988 int lineCount = -1;
989 while (pos >= mGapStart) {
990 if (mBuf[pos + gapLen] == '\n') {
991 if (++lineCount >= nLines) {
992 IS_UTF8_ALIGNED2(this, (pos+1))
993 return pos + 1;
996 pos--;
998 while (pos >= 0) {
999 if (mBuf[pos] == '\n') {
1000 if (++lineCount >= nLines) {
1001 IS_UTF8_ALIGNED2(this, (pos+1))
1002 return pos + 1;
1005 pos--;
1007 return 0;
1012 Find a matching string in the buffer.
1014 int Fl_Text_Buffer::search_forward(int startPos, const char *searchString,
1015 int *foundPos, int matchCase) const
1017 IS_UTF8_ALIGNED2(this, (startPos))
1018 IS_UTF8_ALIGNED(searchString)
1020 if (!searchString)
1021 return 0;
1022 int bp;
1023 const char *sp;
1024 if (matchCase) {
1025 while (startPos < length()) {
1026 bp = startPos;
1027 sp = searchString;
1028 for (;;) {
1029 char c = *sp;
1030 // we reached the end of the "needle", so we found the string!
1031 if (!c) {
1032 *foundPos = startPos;
1033 return 1;
1035 int l = fl_utf8len1(c);
1036 if (memcmp(sp, address(bp), l))
1037 break;
1038 sp += l; bp += l;
1040 startPos = next_char(startPos);
1042 } else {
1043 while (startPos < length()) {
1044 bp = startPos;
1045 sp = searchString;
1046 for (;;) {
1047 // we reached the end of the "needle", so we found the string!
1048 if (!*sp) {
1049 *foundPos = startPos;
1050 return 1;
1052 int l;
1053 unsigned int b = char_at(bp);
1054 unsigned int s = fl_utf8decode(sp, 0, &l);
1055 if (fl_tolower(b)!=fl_tolower(s))
1056 break;
1057 sp += l;
1058 bp = next_char(bp);
1060 startPos = next_char(startPos);
1063 return 0;
1066 int Fl_Text_Buffer::search_backward(int startPos, const char *searchString,
1067 int *foundPos, int matchCase) const
1069 IS_UTF8_ALIGNED2(this, (startPos))
1070 IS_UTF8_ALIGNED(searchString)
1072 if (!searchString)
1073 return 0;
1074 int bp;
1075 const char *sp;
1076 if (matchCase) {
1077 while (startPos >= 0) {
1078 bp = startPos;
1079 sp = searchString;
1080 for (;;) {
1081 char c = *sp;
1082 // we reached the end of the "needle", so we found the string!
1083 if (!c) {
1084 *foundPos = startPos;
1085 return 1;
1087 int l = fl_utf8len1(c);
1088 if (memcmp(sp, address(bp), l))
1089 break;
1090 sp += l; bp += l;
1092 startPos = prev_char(startPos);
1094 } else {
1095 while (startPos >= 0) {
1096 bp = startPos;
1097 sp = searchString;
1098 for (;;) {
1099 // we reached the end of the "needle", so we found the string!
1100 if (!*sp) {
1101 *foundPos = startPos;
1102 return 1;
1104 int l;
1105 unsigned int b = char_at(bp);
1106 unsigned int s = fl_utf8decode(sp, 0, &l);
1107 if (fl_tolower(b)!=fl_tolower(s))
1108 break;
1109 sp += l;
1110 bp = next_char(bp);
1112 startPos = prev_char(startPos);
1115 return 0;
1121 Insert a string into the buffer.
1122 Pos must be at a character boundary. Text must be a correct UTF-8 string.
1124 int Fl_Text_Buffer::insert_(int pos, const char *text)
1126 if (!text || !*text)
1127 return 0;
1129 int insertedLength = strlen(text);
1131 /* Prepare the buffer to receive the new text. If the new text fits in
1132 the current buffer, just move the gap (if necessary) to where
1133 the text should be inserted. If the new text is too large, reallocate
1134 the buffer with a gap large enough to accomodate the new text and a
1135 gap of mPreferredGapSize */
1136 if (insertedLength > mGapEnd - mGapStart)
1137 reallocate_with_gap(pos, insertedLength + mPreferredGapSize);
1138 else if (pos != mGapStart)
1139 move_gap(pos);
1141 /* Insert the new text (pos now corresponds to the start of the gap) */
1142 memcpy(&mBuf[pos], text, insertedLength);
1143 mGapStart += insertedLength;
1144 mLength += insertedLength;
1145 update_selections(pos, 0, insertedLength);
1147 if (mCanUndo) {
1148 if (undowidget == this && undoat == pos && undoinsert) {
1149 undoinsert += insertedLength;
1150 } else {
1151 undoinsert = insertedLength;
1152 undoyankcut = (undoat == pos) ? undocut : 0;
1154 undoat = pos + insertedLength;
1155 undocut = 0;
1156 undowidget = this;
1159 return insertedLength;
1164 Remove a string from the buffer.
1165 Unicode safe. Start and end must be at a character boundary.
1167 void Fl_Text_Buffer::remove_(int start, int end)
1169 /* if the gap is not contiguous to the area to remove, move it there */
1171 if (mCanUndo) {
1172 if (undowidget == this && undoat == end && undocut) {
1173 undobuffersize(undocut + end - start + 1);
1174 memmove(undobuffer + end - start, undobuffer, undocut);
1175 undocut += end - start;
1176 } else {
1177 undocut = end - start;
1178 undobuffersize(undocut);
1180 undoat = start;
1181 undoinsert = 0;
1182 undoyankcut = 0;
1183 undowidget = this;
1186 if (start > mGapStart) {
1187 if (mCanUndo)
1188 memcpy(undobuffer, mBuf + (mGapEnd - mGapStart) + start,
1189 end - start);
1190 move_gap(start);
1191 } else if (end < mGapStart) {
1192 if (mCanUndo)
1193 memcpy(undobuffer, mBuf + start, end - start);
1194 move_gap(end);
1195 } else {
1196 int prelen = mGapStart - start;
1197 if (mCanUndo) {
1198 memcpy(undobuffer, mBuf + start, prelen);
1199 memcpy(undobuffer + prelen, mBuf + mGapEnd, end - start - prelen);
1203 /* expand the gap to encompass the deleted characters */
1204 mGapEnd += end - mGapStart;
1205 mGapStart -= mGapStart - start;
1207 /* update the length */
1208 mLength -= end - start;
1210 /* fix up any selections which might be affected by the change */
1211 update_selections(start, end - start, 0);
1216 simple setter.
1217 Unicode safe. Start and end must be at a character boundary.
1219 void Fl_Text_Selection::set(int startpos, int endpos)
1221 mSelected = startpos != endpos;
1222 mStart = min(startpos, endpos);
1223 mEnd = max(startpos, endpos);
1228 simple getter.
1229 Unicode safe. Start and end will be at a character boundary.
1231 int Fl_Text_Selection::position(int *startpos, int *endpos) const {
1232 if (!mSelected)
1233 return 0;
1234 *startpos = mStart;
1235 *endpos = mEnd;
1237 return 1;
1242 Return if a position is inside the selected area.
1243 Unicode safe. Pos must be at a character boundary.
1245 int Fl_Text_Selection::includes(int pos) const {
1246 return (selected() && pos >= start() && pos < end() );
1251 Return a duplicate of the selected text, or an empty string.
1252 Unicode safe.
1254 char *Fl_Text_Buffer::selection_text_(Fl_Text_Selection * sel) const {
1255 int start, end;
1257 /* If there's no selection, return an allocated empty string */
1258 if (!sel->position(&start, &end))
1260 char *s = (char *) malloc(1);
1261 *s = '\0';
1262 return s;
1265 /* Return the selected range */
1266 return text_range(start, end);
1271 Remove the selected text.
1272 Unicode safe.
1274 void Fl_Text_Buffer::remove_selection_(Fl_Text_Selection * sel)
1276 int start, end;
1278 if (!sel->position(&start, &end))
1279 return;
1280 remove(start, end);
1281 //undoyankcut = undocut;
1286 Replace selection with text.
1287 Unicode safe.
1289 void Fl_Text_Buffer::replace_selection_(Fl_Text_Selection * sel,
1290 const char *text)
1292 Fl_Text_Selection oldSelection = *sel;
1294 /* If there's no selection, return */
1295 int start, end;
1296 if (!sel->position(&start, &end))
1297 return;
1299 /* Do the appropriate type of replace */
1300 replace(start, end, text);
1302 /* Unselect (happens automatically in BufReplace, but BufReplaceRect
1303 can't detect when the contents of a selection goes away) */
1304 sel->mSelected = 0;
1305 redisplay_selection(&oldSelection, sel);
1310 Call all callbacks.
1311 Unicode safe.
1313 void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted,
1314 int nInserted, int nRestyled,
1315 const char *deletedText) const {
1316 IS_UTF8_ALIGNED2(this, pos)
1317 for (int i = 0; i < mNModifyProcs; i++)
1318 (*mModifyProcs[i]) (pos, nInserted, nDeleted, nRestyled,
1319 deletedText, mCbArgs[i]);
1324 Call all callbacks.
1325 Unicode safe.
1327 void Fl_Text_Buffer::call_predelete_callbacks(int pos, int nDeleted) const {
1328 for (int i = 0; i < mNPredeleteProcs; i++)
1329 (*mPredeleteProcs[i]) (pos, nDeleted, mPredeleteCbArgs[i]);
1334 Redisplay a new selected area.
1335 Unicode safe.
1337 void Fl_Text_Buffer::redisplay_selection(Fl_Text_Selection *
1338 oldSelection,
1339 Fl_Text_Selection *
1340 newSelection) const
1342 int oldStart, oldEnd, newStart, newEnd, ch1Start, ch1End, ch2Start,
1343 ch2End;
1345 /* If either selection is rectangular, add an additional character to
1346 the end of the selection to request the redraw routines to wipe out
1347 the parts of the selection beyond the end of the line */
1348 oldStart = oldSelection->mStart;
1349 newStart = newSelection->mStart;
1350 oldEnd = oldSelection->mEnd;
1351 newEnd = newSelection->mEnd;
1353 /* If the old or new selection is unselected, just redisplay the
1354 single area that is (was) selected and return */
1355 if (!oldSelection->mSelected && !newSelection->mSelected)
1356 return;
1357 if (!oldSelection->mSelected)
1359 call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL);
1360 return;
1362 if (!newSelection->mSelected) {
1363 call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL);
1364 return;
1367 /* If the selections are non-contiguous, do two separate updates
1368 and return */
1369 if (oldEnd < newStart || newEnd < oldStart) {
1370 call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL);
1371 call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL);
1372 return;
1375 /* Otherwise, separate into 3 separate regions: ch1, and ch2 (the two
1376 changed areas), and the unchanged area of their intersection,
1377 and update only the changed area(s) */
1378 ch1Start = min(oldStart, newStart);
1379 ch2End = max(oldEnd, newEnd);
1380 ch1End = max(oldStart, newStart);
1381 ch2Start = min(oldEnd, newEnd);
1382 if (ch1Start != ch1End)
1383 call_modify_callbacks(ch1Start, 0, 0, ch1End - ch1Start, NULL);
1384 if (ch2Start != ch2End)
1385 call_modify_callbacks(ch2Start, 0, 0, ch2End - ch2Start, NULL);
1390 Move the gap around without changing buffer content.
1391 Unicode safe. Pos must be at a character boundary.
1393 void Fl_Text_Buffer::move_gap(int pos)
1395 int gapLen = mGapEnd - mGapStart;
1397 if (pos > mGapStart)
1398 memmove(&mBuf[mGapStart], &mBuf[mGapEnd], pos - mGapStart);
1399 else
1400 memmove(&mBuf[pos + gapLen], &mBuf[pos], mGapStart - pos);
1401 mGapEnd += pos - mGapStart;
1402 mGapStart += pos - mGapStart;
1407 Create a larger gap.
1408 Unicode safe. Start must be at a character boundary.
1410 void Fl_Text_Buffer::reallocate_with_gap(int newGapStart, int newGapLen)
1412 char *newBuf = (char *) malloc(mLength + newGapLen);
1413 int newGapEnd = newGapStart + newGapLen;
1415 if (newGapStart <= mGapStart) {
1416 memcpy(newBuf, mBuf, newGapStart);
1417 memcpy(&newBuf[newGapEnd], &mBuf[newGapStart],
1418 mGapStart - newGapStart);
1419 memcpy(&newBuf[newGapEnd + mGapStart - newGapStart],
1420 &mBuf[mGapEnd], mLength - mGapStart);
1421 } else { /* newGapStart > mGapStart */
1422 memcpy(newBuf, mBuf, mGapStart);
1423 memcpy(&newBuf[mGapStart], &mBuf[mGapEnd], newGapStart - mGapStart);
1424 memcpy(&newBuf[newGapEnd],
1425 &mBuf[mGapEnd + newGapStart - mGapStart],
1426 mLength - newGapStart);
1428 free((void *) mBuf);
1429 mBuf = newBuf;
1430 mGapStart = newGapStart;
1431 mGapEnd = newGapEnd;
1436 Update selection range if characters were inserted.
1437 Unicode safe. Pos must be at a character boundary.
1439 void Fl_Text_Buffer::update_selections(int pos, int nDeleted,
1440 int nInserted)
1442 mPrimary.update(pos, nDeleted, nInserted);
1443 mSecondary.update(pos, nDeleted, nInserted);
1444 mHighlight.update(pos, nDeleted, nInserted);
1448 // unicode safe, assuming the arguments are on character boundaries
1449 void Fl_Text_Selection::update(int pos, int nDeleted, int nInserted)
1451 if (!mSelected || pos > mEnd)
1452 return;
1453 if (pos + nDeleted <= mStart) {
1454 mStart += nInserted - nDeleted;
1455 mEnd += nInserted - nDeleted;
1456 } else if (pos <= mStart && pos + nDeleted >= mEnd) {
1457 mStart = pos;
1458 mEnd = pos;
1459 mSelected = 0;
1460 } else if (pos <= mStart && pos + nDeleted < mEnd) {
1461 mStart = pos;
1462 mEnd = nInserted + mEnd - nDeleted;
1463 } else if (pos < mEnd) {
1464 mEnd += nInserted - nDeleted;
1465 if (mEnd <= mStart)
1466 mSelected = 0;
1472 Find a UCS-4 character.
1473 StartPos must be at a character boundary, searchChar is UCS-4 encoded.
1475 int Fl_Text_Buffer::findchar_forward(int startPos, unsigned searchChar,
1476 int *foundPos) const
1478 if (startPos >= mLength) {
1479 *foundPos = mLength;
1480 return 0;
1483 if (startPos<0)
1484 startPos = 0;
1486 for ( ; startPos<mLength; startPos = next_char(startPos)) {
1487 if (searchChar == char_at(startPos)) {
1488 *foundPos = startPos;
1489 return 1;
1493 *foundPos = mLength;
1494 return 0;
1499 Find a UCS-4 character.
1500 StartPos must be at a character boundary, searchChar is UCS-4 encoded.
1502 int Fl_Text_Buffer::findchar_backward(int startPos, unsigned int searchChar,
1503 int *foundPos) const {
1504 if (startPos <= 0) {
1505 *foundPos = 0;
1506 return 0;
1509 if (startPos > mLength)
1510 startPos = mLength;
1512 for (startPos = prev_char(startPos); startPos>=0; startPos = prev_char(startPos)) {
1513 if (searchChar == char_at(startPos)) {
1514 *foundPos = startPos;
1515 return 1;
1519 *foundPos = 0;
1520 return 0;
1523 //#define EXAMPLE_ENCODING // shows how to process any encoding for which a decoding function exists
1524 #ifdef EXAMPLE_ENCODING
1526 // returns the UCS equivalent of *p in CP1252 and advances p by 1
1527 unsigned cp1252toucs(char* &p)
1529 // Codes 0x80..0x9f from the Microsoft CP1252 character set, translated
1530 // to Unicode
1531 static unsigned cp1252[32] = {
1532 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
1533 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
1534 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
1535 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178
1537 unsigned char uc = *(unsigned char*)p;
1538 p++;
1539 return (uc < 0x80 || uc >= 0xa0 ? uc : cp1252[uc - 0x80]);
1542 // returns the UCS equivalent of *p in UTF-16 and advances p by 2 (or more for surrogates)
1543 unsigned utf16toucs(char* &p)
1545 union {
1546 #if WORDS_BIGENDIAN
1547 struct { unsigned char a, b;} chars;
1548 #else
1549 struct { unsigned char b, a;} chars;
1550 #endif
1551 U16 short_val;
1552 } u;
1553 u.chars.a = *(unsigned char*)p++;
1554 u.chars.b = *(unsigned char*)p++;
1555 return u.short_val;
1558 // filter that produces, from an input stream fed by reading from fp,
1559 // a UTF-8-encoded output stream written in buffer.
1560 // Input can be any (e.g., 8-bit, UTF-16) encoding.
1561 // Output is true UTF-8.
1562 // p_trf points to a function that transforms encoded byte(s) into one UCS
1563 // and that increases the pointer by the adequate quantity
1564 static int general_input_filter(char *buffer, int buflen,
1565 char *line, int sline, char* &endline,
1566 unsigned (*p_trf)(char* &),
1567 FILE *fp)
1569 char *p, *q, multibyte[5];
1570 int lq, r, offset;
1571 p = endline = line;
1572 q = buffer;
1573 while (q < buffer + buflen) {
1574 if (p >= endline) {
1575 r = fread(line, 1, sline, fp);
1576 endline = line + r;
1577 if (r == 0) return q - buffer;
1578 p = line;
1580 if (q + 4 /*max width of utf-8 char*/ > buffer + buflen) {
1581 memmove(line, p, endline - p);
1582 endline -= (p - line);
1583 return q - buffer;
1585 lq = fl_utf8encode( p_trf(p), multibyte );
1586 memcpy(q, multibyte, lq);
1587 q += lq;
1589 memmove(line, p, endline - p);
1590 endline -= (p - line);
1591 return q - buffer;
1593 #endif // EXAMPLE_ENCODING
1596 filter that produces, from an input stream fed by reading from fp,
1597 a UTF-8-encoded output stream written in buffer.
1598 Input can be UTF-8. If it is not, it is decoded with CP1252.
1599 Output is UTF-8.
1600 *input_was_changed is set to true if the input was not strict UTF-8 so output
1601 differs from input.
1603 static int utf8_input_filter(char *buffer, int buflen, char *line, int sline, char* &endline,
1604 FILE *fp, int *input_was_changed)
1606 char *p, *q, multibyte[5];
1607 int l, lp, lq, r;
1608 unsigned u;
1609 p = endline = line;
1610 q = buffer;
1611 while (q < buffer + buflen) {
1612 if (p >= endline) {
1613 r = fread(line, 1, sline, fp);
1614 endline = line + r;
1615 if (r == 0) return q - buffer;
1616 p = line;
1618 l = fl_utf8len1(*p);
1619 if (p + l > endline) {
1620 memmove(line, p, endline - p);
1621 endline -= (p - line);
1622 r = fread(endline, 1, sline - (endline - line), fp);
1623 endline += r;
1624 p = line;
1625 if (endline - line < l) break;
1627 while ( l > 0) {
1628 u = fl_utf8decode(p, p+l, &lp);
1629 lq = fl_utf8encode(u, multibyte);
1630 if (lp != l || lq != l) *input_was_changed = true;
1631 if (q + lq > buffer + buflen) {
1632 memmove(line, p, endline - p);
1633 endline -= (p - line);
1634 return q - buffer;
1636 memcpy(q, multibyte, lq);
1637 q += lq;
1638 p += lp;
1639 l -= lp;
1642 memmove(line, p, endline - p);
1643 endline -= (p - line);
1644 return q - buffer;
1647 const char *Fl_Text_Buffer::file_encoding_warning_message =
1648 "Displayed text contains the UTF-8 transcoding\n"
1649 "of the input file which was not UTF-8 encoded.\n"
1650 "Some changes may have occurred.";
1653 Insert text from a file.
1654 Input file can be of various encodings according to what input fiter is used.
1655 utf8_input_filter accepts UTF-8 or CP1252 as input encoding.
1656 Output is always UTF-8.
1658 int Fl_Text_Buffer::insertfile(const char *file, int pos, int buflen)
1660 FILE *fp;
1661 if (!(fp = fl_fopen(file, "r")))
1662 return 1;
1663 char *buffer = new char[buflen + 1];
1664 char *endline, line[100];
1665 int l;
1666 input_file_was_transcoded = false;
1667 endline = line;
1668 while (true) {
1669 #ifdef EXAMPLE_ENCODING
1670 // example of 16-bit encoding: UTF-16
1671 l = general_input_filter(buffer, buflen,
1672 line, sizeof(line), endline,
1673 utf16toucs, // use cp1252toucs to read CP1252-encoded files
1674 fp);
1675 input_file_was_transcoded = true;
1676 #else
1677 l = utf8_input_filter(buffer, buflen, line, sizeof(line), endline,
1678 fp, &input_file_was_transcoded);
1679 #endif
1680 if (l == 0) break;
1681 buffer[l] = 0;
1682 insert(pos, buffer);
1683 pos += l;
1685 int e = ferror(fp) ? 2 : 0;
1686 fclose(fp);
1687 delete[]buffer;
1688 if ( (!e) && input_file_was_transcoded && transcoding_warning_action) {
1689 transcoding_warning_action(this);
1691 return e;
1696 Write text to file.
1697 Unicode safe.
1699 int Fl_Text_Buffer::outputfile(const char *file,
1700 int start, int end,
1701 int buflen) {
1702 FILE *fp;
1703 if (!(fp = fl_fopen(file, "w")))
1704 return 1;
1705 for (int n; (n = min(end - start, buflen)); start += n) {
1706 const char *p = text_range(start, start + n);
1707 int r = fwrite(p, 1, n, fp);
1708 free((void *) p);
1709 if (r != n)
1710 break;
1713 int e = ferror(fp) ? 2 : 0;
1714 fclose(fp);
1715 return e;
1720 Return the previous character position.
1721 Unicode safe.
1723 int Fl_Text_Buffer::prev_char_clipped(int pos) const
1725 if (pos<=0)
1726 return 0;
1728 IS_UTF8_ALIGNED2(this, (pos))
1730 char c;
1731 do {
1732 pos--;
1733 if (pos==0)
1734 return 0;
1735 c = byte_at(pos);
1736 } while ( (c&0xc0) == 0x80);
1738 IS_UTF8_ALIGNED2(this, (pos))
1739 return pos;
1744 Return the previous character position.
1745 Returns -1 if the beginning of the buffer is reached.
1747 int Fl_Text_Buffer::prev_char(int pos) const
1749 if (pos==0) return -1;
1750 return prev_char_clipped(pos);
1755 Return the next character position.
1756 Returns length() if the end of the buffer is reached.
1758 int Fl_Text_Buffer::next_char(int pos) const
1760 IS_UTF8_ALIGNED2(this, (pos))
1761 int n = fl_utf8len1(byte_at(pos));
1762 pos += n;
1763 if (pos>=mLength)
1764 return mLength;
1765 IS_UTF8_ALIGNED2(this, (pos))
1766 return pos;
1771 Return the next character position.
1772 If the end of the buffer is reached, it returns the current position.
1774 int Fl_Text_Buffer::next_char_clipped(int pos) const
1776 return next_char(pos);
1780 Align an index to the current UTF-8 boundary.
1782 int Fl_Text_Buffer::utf8_align(int pos) const
1784 char c = byte_at(pos);
1785 while ( (c&0xc0) == 0x80) {
1786 pos--;
1787 c = byte_at(pos);
1789 return pos;
1793 // End of "$Id: Fl_Text_Buffer.cxx 8040 2010-12-15 17:38:39Z manolo $".