fltk  1.3.5-source
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X.
  Fossies Dox: fltk-1.3.5-source.tar.bz2  ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_Text_Buffer.cxx
Go to the documentation of this file.
1 //
2 // "$Id$"
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. Distribution and use rights are outlined in
9 // the file "COPYING" which should have been included with this file. If this
10 // file is missing or damaged, see the license at:
11 //
12 // http://www.fltk.org/COPYING.php
13 //
14 // Please report all bugs and problems on the following page:
15 //
16 // http://www.fltk.org/str.php
17 //
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <FL/fl_utf8.h>
22 #include "flstring.h"
23 #include <ctype.h>
24 #include <FL/Fl.H>
25 #include <FL/Fl_Text_Buffer.H>
26 #include <FL/fl_ask.H>
27 
28 
29 /*
30  This file is based on a port of NEdit to FLTK many years ago. NEdit at that
31  point was already stretched beyond the task it was designed for which explains
32  why the source code is sometimes pretty convoluted. It still is a very useful
33  widget for FLTK, and we are thankful that the nedit team allowed us to
34  integrate their code.
35 
36  With the introduction of Unicode and UTF-8, Fl_Text_... has to go into a whole
37  new generation of code. Originally designed for monospaced fonts only, many
38  features make less sense in the multibyte and multiwidth world of UTF-8.
39 
40  Columns are a good example. There is simply no such thing. The new Fl_Text_...
41  widget converts columns to pixels by multiplying them with the average
42  character width for a given font.
43 
44  Rectangular selections were rarely used (if at all) and make little sense when
45  using variable width fonts. They have been removed.
46 
47  Using multiple spaces to emulate tab stops has been replaced by pixel counting
48  routines. They are slower, but give the expected result for proportional fonts.
49 
50  And constantly recalculating character widths is just much too expensive. Lines
51  of text are now subdivided into blocks of text which are measured at once
52  instead of individual characters.
53  */
54 
55 
56 #ifndef min
57 
58 static int max(int i1, int i2)
59 {
60  return i1 >= i2 ? i1 : i2;
61 }
62 
63 static int min(int i1, int i2)
64 {
65  return i1 <= i2 ? i1 : i2;
66 }
67 
68 #endif
69 
70 
71 static char *undobuffer;
72 static int undobufferlength;
74 static int undoat; // points after insertion
75 static int undocut; // number of characters deleted there
76 static int undoinsert; // number of characters inserted
77 static int undoyankcut; // length of valid contents of buffer, even if undocut=0
78 
79 /*
80  Resize the undo buffer to match at least the requested size.
81  */
82 static void undobuffersize(int n)
83 {
84  if (n > undobufferlength) {
85  if (undobuffer) {
86  do {
87  undobufferlength *= 2;
88  } while (undobufferlength < n);
89  undobuffer = (char *) realloc(undobuffer, undobufferlength);
90  } else {
91  undobufferlength = n + 9;
93  }
94  }
95 }
96 
98 {
100 }
101 
102 /*
103  Initialize all variables.
104  */
105 Fl_Text_Buffer::Fl_Text_Buffer(int requestedSize, int preferredGapSize)
106 {
107  mLength = 0;
108  mPreferredGapSize = preferredGapSize;
109  mBuf = (char *) malloc(requestedSize + mPreferredGapSize);
110  mGapStart = 0;
111  mGapEnd = requestedSize + mPreferredGapSize;
112  mTabDist = 8;
113  mPrimary.mSelected = 0;
115  mSecondary.mSelected = 0;
117  mHighlight.mSelected = 0;
119  mModifyProcs = NULL;
120  mCbArgs = NULL;
121  mNModifyProcs = 0;
122  mNPredeleteProcs = 0;
125  mCursorPosHint = 0;
126  mCanUndo = 1;
129 }
130 
131 
132 /*
133  Free all resources.
134  */
136 {
137  free(mBuf);
138  if (mNModifyProcs != 0) {
139  delete[]mModifyProcs;
140  delete[]mCbArgs;
141  }
142  if (mNPredeleteProcs > 0) {
143  delete[] mPredeleteProcs;
144  delete[] mPredeleteCbArgs;
145  }
146 }
147 
148 
149 /*
150  This function copies verbose whatever is in front and after the gap into a
151  single buffer.
152  */
153 char *Fl_Text_Buffer::text() const {
154  char *t = (char *) malloc(mLength + 1);
155  memcpy(t, mBuf, mGapStart);
156  memcpy(t+mGapStart, mBuf+mGapEnd, mLength - mGapStart);
157  t[mLength] = '\0';
158  return t;
159 }
160 
161 
162 /*
163  Set the text buffer to a new string.
164  */
165 void Fl_Text_Buffer::text(const char *t)
166 {
167  IS_UTF8_ALIGNED(t)
168 
169  // if t is null then substitute it with an empty string
170  // then don't return so that internal cleanup can happen
171  if (!t) t="";
172 
174 
175  /* Save information for redisplay, and get rid of the old buffer */
176  const char *deletedText = text();
177  int deletedLength = mLength;
178  free((void *) mBuf);
179 
180  /* Start a new buffer with a gap of mPreferredGapSize at the end */
181  int insertedLength = (int) strlen(t);
182  mBuf = (char *) malloc(insertedLength + mPreferredGapSize);
183  mLength = insertedLength;
184  mGapStart = insertedLength;
186  memcpy(mBuf, t, insertedLength);
187 
188  /* Zero all of the existing selections */
189  update_selections(0, deletedLength, 0);
190 
191  /* Call the saved display routine(s) to update the screen */
192  call_modify_callbacks(0, deletedLength, insertedLength, 0, deletedText);
193  free((void *) deletedText);
194 }
195 
196 
197 /*
198  Creates a range of text to a new buffer and copies verbose from around the gap.
199  */
200 char *Fl_Text_Buffer::text_range(int start, int end) const {
201  IS_UTF8_ALIGNED2(this, (start))
202  IS_UTF8_ALIGNED2(this, (end))
203 
204  char *s = NULL;
205 
206  /* Make sure start and end are ok, and allocate memory for returned string.
207  If start is bad, return "", if end is bad, adjust it. */
208  if (start < 0 || start > mLength)
209  {
210  s = (char *) malloc(1);
211  s[0] = '\0';
212  return s;
213  }
214  if (end < start) {
215  int temp = start;
216  start = end;
217  end = temp;
218  }
219  if (end > mLength)
220  end = mLength;
221  int copiedLength = end - start;
222  s = (char *) malloc(copiedLength + 1);
223 
224  /* Copy the text from the buffer to the returned string */
225  if (end <= mGapStart) {
226  memcpy(s, mBuf + start, copiedLength);
227  } else if (start >= mGapStart) {
228  memcpy(s, mBuf + start + (mGapEnd - mGapStart), copiedLength);
229  } else {
230  int part1Length = mGapStart - start;
231  memcpy(s, mBuf + start, part1Length);
232  memcpy(s + part1Length, mBuf + mGapEnd, copiedLength - part1Length);
233  }
234  s[copiedLength] = '\0';
235  return s;
236 }
237 
238 /*
239  Return a UCS-4 character at the given index.
240  Pos must be at a character boundary.
241  */
242 unsigned int Fl_Text_Buffer::char_at(int pos) const {
243  if (pos < 0 || pos >= mLength)
244  return '\0';
245 
246  IS_UTF8_ALIGNED2(this, (pos))
247 
248  const char *src = address(pos);
249  return fl_utf8decode(src, 0, 0);
250 }
251 
252 
253 /*
254  Return the raw byte at the given index.
255  This function ignores all unicode encoding.
256  */
257 char Fl_Text_Buffer::byte_at(int pos) const {
258  if (pos < 0 || pos >= mLength)
259  return '\0';
260  const char *src = address(pos);
261  return *src;
262 }
263 
264 
265 /*
266  Insert some text at the given index.
267  Pos must be at a character boundary.
268 */
269 void Fl_Text_Buffer::insert(int pos, const char *text)
270 {
271  IS_UTF8_ALIGNED2(this, (pos))
273 
274  /* check if there is actually any text */
275  if (!text || !*text)
276  return;
277 
278  /* if pos is not contiguous to existing text, make it */
279  if (pos > mLength)
280  pos = mLength;
281  if (pos < 0)
282  pos = 0;
283 
284  /* Even if nothing is deleted, we must call these callbacks */
285  call_predelete_callbacks(pos, 0);
286 
287  /* insert and redisplay */
288  int nInserted = insert_(pos, text);
289  mCursorPosHint = pos + nInserted;
291  call_modify_callbacks(pos, 0, nInserted, 0, NULL);
292 }
293 
294 
295 /*
296  Replace a range of text with new text.
297  Start and end must be at a character boundary.
298 */
299 void Fl_Text_Buffer::replace(int start, int end, const char *text)
300 {
301  // Range check...
302  if (!text)
303  return;
304  if (start < 0)
305  start = 0;
306  if (end > mLength)
307  end = mLength;
308 
309  IS_UTF8_ALIGNED2(this, (start))
310  IS_UTF8_ALIGNED2(this, (end))
312 
314  const char *deletedText = text_range(start, end);
315  remove_(start, end);
316  int nInserted = insert_(start, text);
317  mCursorPosHint = start + nInserted;
318  call_modify_callbacks(start, end - start, nInserted, 0, deletedText);
319  free((void *) deletedText);
320 }
321 
322 
323 /*
324  Remove a range of text.
325  Start and End must be at a character boundary.
326 */
327 void Fl_Text_Buffer::remove(int start, int end)
328 {
329  /* Make sure the arguments make sense */
330  if (start > end) {
331  int temp = start;
332  start = end;
333  end = temp;
334  }
335  if (start > mLength)
336  start = mLength;
337  if (start < 0)
338  start = 0;
339  if (end > mLength)
340  end = mLength;
341  if (end < 0)
342  end = 0;
343 
344  IS_UTF8_ALIGNED2(this, (start))
345  IS_UTF8_ALIGNED2(this, (end))
346 
347  if (start == end)
348  return;
349 
351  /* Remove and redisplay */
352  const char *deletedText = text_range(start, end);
353  remove_(start, end);
355  call_modify_callbacks(start, end - start, 0, 0, deletedText);
356  free((void *) deletedText);
357 }
358 
359 
360 /*
361  Copy a range of text from another text buffer.
362  fromStart, fromEnd, and toPos must be at a character boundary.
363  */
364 void Fl_Text_Buffer::copy(Fl_Text_Buffer * fromBuf, int fromStart,
365  int fromEnd, int toPos)
366 {
367  IS_UTF8_ALIGNED2(fromBuf, fromStart)
368  IS_UTF8_ALIGNED2(fromBuf, fromEnd)
369  IS_UTF8_ALIGNED2(this, (toPos))
370 
371  int copiedLength = fromEnd - fromStart;
372 
373  /* Prepare the buffer to receive the new text. If the new text fits in
374  the current buffer, just move the gap (if necessary) to where
375  the text should be inserted. If the new text is too large, reallocate
376  the buffer with a gap large enough to accomodate the new text and a
377  gap of mPreferredGapSize */
378  if (copiedLength > mGapEnd - mGapStart)
379  reallocate_with_gap(toPos, copiedLength + mPreferredGapSize);
380  else if (toPos != mGapStart)
381  move_gap(toPos);
382 
383  /* Insert the new text (toPos now corresponds to the start of the gap) */
384  if (fromEnd <= fromBuf->mGapStart) {
385  memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], copiedLength);
386  } else if (fromStart >= fromBuf->mGapStart) {
387  memcpy(&mBuf[toPos],
388  &fromBuf->mBuf[fromStart + (fromBuf->mGapEnd - fromBuf->mGapStart)],
389  copiedLength);
390  } else {
391  int part1Length = fromBuf->mGapStart - fromStart;
392  memcpy(&mBuf[toPos], &fromBuf->mBuf[fromStart], part1Length);
393  memcpy(&mBuf[toPos + part1Length],
394  &fromBuf->mBuf[fromBuf->mGapEnd], copiedLength - part1Length);
395  }
396  mGapStart += copiedLength;
397  mLength += copiedLength;
398  update_selections(toPos, 0, copiedLength);
399 }
400 
401 
402 /*
403  Take the previous changes and undo them. Return the previous
404  cursor position in cursorPos. Returns 1 if the undo was applied.
405  CursorPos will be at a character boundary.
406  */
407 int Fl_Text_Buffer::undo(int *cursorPos)
408 {
409  if (undowidget != this || (!undocut && !undoinsert && !mCanUndo))
410  return 0;
411 
412  int ilen = undocut;
413  int xlen = undoinsert;
414  int b = undoat - xlen;
415 
416  if (xlen && undoyankcut && !ilen) {
417  ilen = undoyankcut;
418  }
419 
420  if (xlen && ilen) {
421  undobuffersize(ilen + 1);
422  undobuffer[ilen] = 0;
423  char *tmp = strdup(undobuffer);
424  replace(b, undoat, tmp);
425  if (cursorPos)
426  *cursorPos = mCursorPosHint;
427  free(tmp);
428  } else if (xlen) {
429  remove(b, undoat);
430  if (cursorPos)
431  *cursorPos = mCursorPosHint;
432  } else if (ilen) {
433  undobuffersize(ilen + 1);
434  undobuffer[ilen] = 0;
436  if (cursorPos)
437  *cursorPos = mCursorPosHint;
438  undoyankcut = 0;
439  }
440 
441  return 1;
442 }
443 
444 
445 /*
446  Set a flag if undo function will work.
447  */
448 void Fl_Text_Buffer::canUndo(char flag)
449 {
450  mCanUndo = flag;
451  // disabling undo also clears the last undo operation!
452  if (!mCanUndo && undowidget==this)
453  undowidget = 0;
454 }
455 
456 
457 /*
458  Change the tab width. This will cause a couple of callbacks and a complete
459  redisplay.
460  Matt: I am not entirely sure why we need to trigger callbacks because
461  tabs are only a graphical hint, not changing any text at all, but I leave
462  this in here for back compatibility.
463  */
465 {
466  /* First call the pre-delete callbacks with the previous tab setting
467  still active. */
469 
470  /* Change the tab setting */
471  mTabDist = tabDist;
472 
473  /* Force any display routines to redisplay everything (unfortunately,
474  this means copying the whole buffer contents to provide "deletedText" */
475  const char *deletedText = text();
476  call_modify_callbacks(0, mLength, mLength, 0, deletedText);
477  free((void *) deletedText);
478 }
479 
480 
481 /*
482  Select a range of text.
483  Start and End must be at a character boundary.
484  */
485 void Fl_Text_Buffer::select(int start, int end)
486 {
487  IS_UTF8_ALIGNED2(this, (start))
488  IS_UTF8_ALIGNED2(this, (end))
489 
490  Fl_Text_Selection oldSelection = mPrimary;
491 
492  mPrimary.set(start, end);
493  redisplay_selection(&oldSelection, &mPrimary);
494 }
495 
496 
497 /*
498  Clear the primary selection.
499  */
501 {
502  Fl_Text_Selection oldSelection = mPrimary;
503 
504  mPrimary.mSelected = 0;
505  redisplay_selection(&oldSelection, &mPrimary);
506 }
507 
508 
509 /*
510  Return the primary selection range.
511  */
513 {
514  return mPrimary.position(start, end);
515 }
516 
517 
518 /*
519  Return a copy of the selected text.
520  */
522 {
523  return selection_text_(&mPrimary);
524 }
525 
526 
527 /*
528  Remove the selected text.
529  */
531 {
533 }
534 
535 
536 /*
537  Replace the selected text.
538  */
539 void Fl_Text_Buffer::replace_selection(const char *text)
540 {
542 }
543 
544 
545 /*
546  Select text.
547  Start and End must be at a character boundary.
548  */
550 {
551  Fl_Text_Selection oldSelection = mSecondary;
552 
553  mSecondary.set(start, end);
554  redisplay_selection(&oldSelection, &mSecondary);
555 }
556 
557 
558 /*
559  Deselect text.
560  */
562 {
563  Fl_Text_Selection oldSelection = mSecondary;
564 
565  mSecondary.mSelected = 0;
566  redisplay_selection(&oldSelection, &mSecondary);
567 }
568 
569 
570 /*
571  Return the selected range.
572  */
574 {
575  return mSecondary.position(start, end);
576 }
577 
578 
579 /*
580  Return a copy of the text in this selection.
581  */
583 {
584  return selection_text_(&mSecondary);
585 }
586 
587 
588 /*
589  Remove the selected text.
590  */
592 {
594 }
595 
596 
597 /*
598  Replace selected text.
599  */
601 {
603 }
604 
605 
606 /*
607  Highlight a range of text.
608  Start and End must be at a character boundary.
609  */
611 {
612  Fl_Text_Selection oldSelection = mHighlight;
613 
614  mHighlight.set(start, end);
615  redisplay_selection(&oldSelection, &mHighlight);
616 }
617 
618 
619 /*
620  Remove text highlighting.
621  */
623 {
624  Fl_Text_Selection oldSelection = mHighlight;
625 
626  mHighlight.mSelected = 0;
627  redisplay_selection(&oldSelection, &mHighlight);
628 }
629 
630 
631 /*
632  Return position of highlight.
633  */
635 {
636  return mHighlight.position(start, end);
637 }
638 
639 
640 /*
641  Return a copy of highlighted text.
642  */
644 {
645  return selection_text_(&mHighlight);
646 }
647 
648 
649 /*
650  Add a callback that is called whenever text is modified.
651  */
653  void *cbArg)
654 {
655  Fl_Text_Modify_Cb *newModifyProcs =
657  void **newCBArgs = new void *[mNModifyProcs + 1];
658  for (int i = 0; i < mNModifyProcs; i++) {
659  newModifyProcs[i + 1] = mModifyProcs[i];
660  newCBArgs[i + 1] = mCbArgs[i];
661  }
662  if (mNModifyProcs != 0) {
663  delete[]mModifyProcs;
664  delete[]mCbArgs;
665  }
666  newModifyProcs[0] = bufModifiedCB;
667  newCBArgs[0] = cbArg;
668  mNModifyProcs++;
669  mModifyProcs = newModifyProcs;
670  mCbArgs = newCBArgs;
671 }
672 
673 
674 /*
675  Remove a callback.
676  */
678  void *cbArg)
679 {
680  int i, toRemove = -1;
681 
682  /* find the matching callback to remove */
683  for (i = 0; i < mNModifyProcs; i++) {
684  if (mModifyProcs[i] == bufModifiedCB && mCbArgs[i] == cbArg) {
685  toRemove = i;
686  break;
687  }
688  }
689  if (toRemove == -1) {
690  Fl::error
691  ("Fl_Text_Buffer::remove_modify_callback(): Can't find modify CB to remove");
692  return;
693  }
694 
695  /* Allocate new lists for remaining callback procs and args (if
696  any are left) */
697  mNModifyProcs--;
698  if (mNModifyProcs == 0) {
699  mNModifyProcs = 0;
700  delete[]mModifyProcs;
701  mModifyProcs = NULL;
702  delete[]mCbArgs;
703  mCbArgs = NULL;
704  return;
705  }
706  Fl_Text_Modify_Cb *newModifyProcs = new Fl_Text_Modify_Cb[mNModifyProcs];
707  void **newCBArgs = new void *[mNModifyProcs];
708 
709  /* copy out the remaining members and free the old lists */
710  for (i = 0; i < toRemove; i++) {
711  newModifyProcs[i] = mModifyProcs[i];
712  newCBArgs[i] = mCbArgs[i];
713  }
714  for (; i < mNModifyProcs; i++) {
715  newModifyProcs[i] = mModifyProcs[i + 1];
716  newCBArgs[i] = mCbArgs[i + 1];
717  }
718  delete[]mModifyProcs;
719  delete[]mCbArgs;
720  mModifyProcs = newModifyProcs;
721  mCbArgs = newCBArgs;
722 }
723 
724 
725 /*
726  Add a callback that is called before deleting text.
727  */
729  void *cbArg)
730 {
731  Fl_Text_Predelete_Cb *newPreDeleteProcs =
733  void **newCBArgs = new void *[mNPredeleteProcs + 1];
734  for (int i = 0; i < mNPredeleteProcs; i++) {
735  newPreDeleteProcs[i + 1] = mPredeleteProcs[i];
736  newCBArgs[i + 1] = mPredeleteCbArgs[i];
737  }
738  if (mNPredeleteProcs > 0) {
739  delete[] mPredeleteProcs;
740  delete[] mPredeleteCbArgs;
741  }
742  newPreDeleteProcs[0] = bufPreDeleteCB;
743  newCBArgs[0] = cbArg;
745  mPredeleteProcs = newPreDeleteProcs;
746  mPredeleteCbArgs = newCBArgs;
747 }
748 
749 
750 /*
751  Remove a callback.
752  */
754 {
755  int i, toRemove = -1;
756  /* find the matching callback to remove */
757  for (i = 0; i < mNPredeleteProcs; i++) {
758  if (mPredeleteProcs[i] == bufPreDeleteCB &&
759  mPredeleteCbArgs[i] == cbArg) {
760  toRemove = i;
761  break;
762  }
763  }
764  if (toRemove == -1) {
765  Fl::error
766  ("Fl_Text_Buffer::remove_predelete_callback(): Can't find pre-delete CB to remove");
767  return;
768  }
769 
770  /* Allocate new lists for remaining callback procs and args (if any are left) */
772  if (mNPredeleteProcs == 0) {
773  delete[]mPredeleteProcs;
775  delete[]mPredeleteCbArgs;
777  return;
778  }
780  void **newCBArgs = new void *[mNPredeleteProcs];
781 
782  /* copy out the remaining members and free the old lists */
783  for (i = 0; i < toRemove; i++) {
784  newPreDeleteProcs[i] = mPredeleteProcs[i];
785  newCBArgs[i] = mPredeleteCbArgs[i];
786  }
787  for (; i < mNPredeleteProcs; i++) {
788  newPreDeleteProcs[i] = mPredeleteProcs[i + 1];
789  newCBArgs[i] = mPredeleteCbArgs[i + 1];
790  }
791  delete[] mPredeleteProcs;
792  delete[] mPredeleteCbArgs;
793  mPredeleteProcs = newPreDeleteProcs;
794  mPredeleteCbArgs = newCBArgs;
795 }
796 
797 
798 /*
799  Return a copy of the line that contains a given index.
800  Pos must be at a character boundary.
801  */
802 char *Fl_Text_Buffer::line_text(int pos) const {
803  return text_range(line_start(pos), line_end(pos));
804 }
805 
806 
807 /*
808  Find the beginning of the line.
809  */
810 int Fl_Text_Buffer::line_start(int pos) const
811 {
812  if (!findchar_backward(pos, '\n', &pos))
813  return 0;
814  return pos + 1;
815 }
816 
817 
818 /*
819  Find the end of the line.
820  */
821 int Fl_Text_Buffer::line_end(int pos) const {
822  if (!findchar_forward(pos, '\n', &pos))
823  pos = mLength;
824  return pos;
825 }
826 
827 
828 /*
829  Find the beginning of a word.
830  NOT UNICODE SAFE.
831  */
832 int Fl_Text_Buffer::word_start(int pos) const {
833  // FIXME: character is ucs-4
834  while (pos>0 && (isalnum(char_at(pos)) || char_at(pos) == '_')) {
835  pos = prev_char(pos);
836  }
837  // FIXME: character is ucs-4
838  if (!(isalnum(char_at(pos)) || char_at(pos) == '_'))
839  pos = next_char(pos);
840  return pos;
841 }
842 
843 
844 /*
845  Find the end of a word.
846  NOT UNICODE SAFE.
847  */
848 int Fl_Text_Buffer::word_end(int pos) const {
849  // FIXME: character is ucs-4
850  while (pos < length() && (isalnum(char_at(pos)) || char_at(pos) == '_'))
851  {
852  pos = next_char(pos);
853  }
854  return pos;
855 }
856 
857 
858 /*
859  Count the number of characters between two positions.
860  */
862  int targetPos) const
863 {
864  IS_UTF8_ALIGNED2(this, (lineStartPos))
865  IS_UTF8_ALIGNED2(this, (targetPos))
866 
867  int charCount = 0;
868 
869  int pos = lineStartPos;
870  while (pos < targetPos) {
871  pos = next_char(pos);
872  charCount++;
873  }
874  return charCount;
875 }
876 
877 
878 /*
879  Skip ahead a number of characters from a given index.
880  This function breaks early if it encounters a newline character.
881  */
882 int Fl_Text_Buffer::skip_displayed_characters(int lineStartPos, int nChars)
883 {
884  IS_UTF8_ALIGNED2(this, (lineStartPos))
885 
886  int pos = lineStartPos;
887 
888  for (int charCount = 0; charCount < nChars && pos < mLength; charCount++) {
889  unsigned int c = char_at(pos);
890  if (c == '\n')
891  return pos;
892  pos = next_char(pos);
893  }
894  return pos;
895 }
896 
897 
898 /*
899  Count the number of newline characters between start and end.
900  startPos and endPos must be at a character boundary.
901  This function is optimized for speed by not using UTF-8 calls.
902  */
903 int Fl_Text_Buffer::count_lines(int startPos, int endPos) const {
904  IS_UTF8_ALIGNED2(this, (startPos))
905  IS_UTF8_ALIGNED2(this, (endPos))
906 
907  int gapLen = mGapEnd - mGapStart;
908  int lineCount = 0;
909 
910  int pos = startPos;
911  while (pos < mGapStart)
912  {
913  if (pos == endPos)
914  return lineCount;
915  if (mBuf[pos++] == '\n')
916  lineCount++;
917  }
918  while (pos < mLength) {
919  if (pos == endPos)
920  return lineCount;
921  if (mBuf[pos++ + gapLen] == '\n')
922  lineCount++;
923  }
924  return lineCount;
925 }
926 
927 
928 /*
929  Skip to the first character, n lines ahead.
930  StartPos must be at a character boundary.
931  This function is optimized for speed by not using UTF-8 calls.
932  */
933 int Fl_Text_Buffer::skip_lines(int startPos, int nLines)
934 {
935  IS_UTF8_ALIGNED2(this, (startPos))
936 
937  if (nLines == 0)
938  return startPos;
939 
940  int gapLen = mGapEnd - mGapStart;
941  int pos = startPos;
942  int lineCount = 0;
943  while (pos < mGapStart) {
944  if (mBuf[pos++] == '\n') {
945  lineCount++;
946  if (lineCount == nLines) {
947  IS_UTF8_ALIGNED2(this, (pos))
948  return pos;
949  }
950  }
951  }
952  while (pos < mLength) {
953  if (mBuf[pos++ + gapLen] == '\n') {
954  lineCount++;
955  if (lineCount >= nLines) {
956  IS_UTF8_ALIGNED2(this, (pos))
957  return pos;
958  }
959  }
960  }
961  IS_UTF8_ALIGNED2(this, (pos))
962  return pos;
963 }
964 
965 
966 /*
967  Skip to the first character, n lines back.
968  StartPos must be at a character boundary.
969  This function is optimized for speed by not using UTF-8 calls.
970  */
971 int Fl_Text_Buffer::rewind_lines(int startPos, int nLines)
972 {
973  IS_UTF8_ALIGNED2(this, (startPos))
974 
975  int pos = startPos - 1;
976  if (pos <= 0)
977  return 0;
978 
979  int gapLen = mGapEnd - mGapStart;
980  int lineCount = -1;
981  while (pos >= mGapStart) {
982  if (mBuf[pos + gapLen] == '\n') {
983  if (++lineCount >= nLines) {
984  IS_UTF8_ALIGNED2(this, (pos+1))
985  return pos + 1;
986  }
987  }
988  pos--;
989  }
990  while (pos >= 0) {
991  if (mBuf[pos] == '\n') {
992  if (++lineCount >= nLines) {
993  IS_UTF8_ALIGNED2(this, (pos+1))
994  return pos + 1;
995  }
996  }
997  pos--;
998  }
999  return 0;
1000 }
1001 
1002 
1003 /*
1004  Find a matching string in the buffer.
1005  */
1006 int Fl_Text_Buffer::search_forward(int startPos, const char *searchString,
1007  int *foundPos, int matchCase) const
1008 {
1009  IS_UTF8_ALIGNED2(this, (startPos))
1010  IS_UTF8_ALIGNED(searchString)
1011 
1012  if (!searchString)
1013  return 0;
1014  int bp;
1015  const char *sp;
1016  if (matchCase) {
1017  while (startPos < length()) {
1018  bp = startPos;
1019  sp = searchString;
1020  for (;;) {
1021  char c = *sp;
1022  // we reached the end of the "needle", so we found the string!
1023  if (!c) {
1024  *foundPos = startPos;
1025  return 1;
1026  }
1027  int l = fl_utf8len1(c);
1028  if (memcmp(sp, address(bp), l))
1029  break;
1030  sp += l; bp += l;
1031  }
1032  startPos = next_char(startPos);
1033  }
1034  } else {
1035  while (startPos < length()) {
1036  bp = startPos;
1037  sp = searchString;
1038  for (;;) {
1039  // we reached the end of the "needle", so we found the string!
1040  if (!*sp) {
1041  *foundPos = startPos;
1042  return 1;
1043  }
1044  int l;
1045  unsigned int b = char_at(bp);
1046  unsigned int s = fl_utf8decode(sp, 0, &l);
1047  if (fl_tolower(b)!=fl_tolower(s))
1048  break;
1049  sp += l;
1050  bp = next_char(bp);
1051  }
1052  startPos = next_char(startPos);
1053  }
1054  }
1055  return 0;
1056 }
1057 
1058 int Fl_Text_Buffer::search_backward(int startPos, const char *searchString,
1059  int *foundPos, int matchCase) const
1060 {
1061  IS_UTF8_ALIGNED2(this, (startPos))
1062  IS_UTF8_ALIGNED(searchString)
1063 
1064  if (!searchString)
1065  return 0;
1066  int bp;
1067  const char *sp;
1068  if (matchCase) {
1069  while (startPos >= 0) {
1070  bp = startPos;
1071  sp = searchString;
1072  for (;;) {
1073  char c = *sp;
1074  // we reached the end of the "needle", so we found the string!
1075  if (!c) {
1076  *foundPos = startPos;
1077  return 1;
1078  }
1079  int l = fl_utf8len1(c);
1080  if (memcmp(sp, address(bp), l))
1081  break;
1082  sp += l; bp += l;
1083  }
1084  startPos = prev_char(startPos);
1085  }
1086  } else {
1087  while (startPos >= 0) {
1088  bp = startPos;
1089  sp = searchString;
1090  for (;;) {
1091  // we reached the end of the "needle", so we found the string!
1092  if (!*sp) {
1093  *foundPos = startPos;
1094  return 1;
1095  }
1096  int l;
1097  unsigned int b = char_at(bp);
1098  unsigned int s = fl_utf8decode(sp, 0, &l);
1099  if (fl_tolower(b)!=fl_tolower(s))
1100  break;
1101  sp += l;
1102  bp = next_char(bp);
1103  }
1104  startPos = prev_char(startPos);
1105  }
1106  }
1107  return 0;
1108 }
1109 
1110 
1111 
1112 /*
1113  Insert a string into the buffer.
1114  Pos must be at a character boundary. Text must be a correct UTF-8 string.
1115  */
1116 int Fl_Text_Buffer::insert_(int pos, const char *text)
1117 {
1118  if (!text || !*text)
1119  return 0;
1120 
1121  int insertedLength = (int) strlen(text);
1122 
1123  /* Prepare the buffer to receive the new text. If the new text fits in
1124  the current buffer, just move the gap (if necessary) to where
1125  the text should be inserted. If the new text is too large, reallocate
1126  the buffer with a gap large enough to accomodate the new text and a
1127  gap of mPreferredGapSize */
1128  if (insertedLength > mGapEnd - mGapStart)
1129  reallocate_with_gap(pos, insertedLength + mPreferredGapSize);
1130  else if (pos != mGapStart)
1131  move_gap(pos);
1132 
1133  /* Insert the new text (pos now corresponds to the start of the gap) */
1134  memcpy(&mBuf[pos], text, insertedLength);
1135  mGapStart += insertedLength;
1136  mLength += insertedLength;
1137  update_selections(pos, 0, insertedLength);
1138 
1139  if (mCanUndo) {
1140  if (undowidget == this && undoat == pos && undoinsert) {
1141  undoinsert += insertedLength;
1142  } else {
1143  undoinsert = insertedLength;
1144  undoyankcut = (undoat == pos) ? undocut : 0;
1145  }
1146  undoat = pos + insertedLength;
1147  undocut = 0;
1148  undowidget = this;
1149  }
1150 
1151  return insertedLength;
1152 }
1153 
1154 
1155 /*
1156  Remove a string from the buffer.
1157  Unicode safe. Start and end must be at a character boundary.
1158  */
1159 void Fl_Text_Buffer::remove_(int start, int end)
1160 {
1161  /* if the gap is not contiguous to the area to remove, move it there */
1162 
1163  if (mCanUndo) {
1164  if (undowidget == this && undoat == end && undocut) {
1165  undobuffersize(undocut + end - start + 1);
1166  memmove(undobuffer + end - start, undobuffer, undocut);
1167  undocut += end - start;
1168  } else {
1169  undocut = end - start;
1171  }
1172  undoat = start;
1173  undoinsert = 0;
1174  undoyankcut = 0;
1175  undowidget = this;
1176  }
1177 
1178  if (start > mGapStart) {
1179  if (mCanUndo)
1180  memcpy(undobuffer, mBuf + (mGapEnd - mGapStart) + start,
1181  end - start);
1182  move_gap(start);
1183  } else if (end < mGapStart) {
1184  if (mCanUndo)
1185  memcpy(undobuffer, mBuf + start, end - start);
1186  move_gap(end);
1187  } else {
1188  int prelen = mGapStart - start;
1189  if (mCanUndo) {
1190  memcpy(undobuffer, mBuf + start, prelen);
1191  memcpy(undobuffer + prelen, mBuf + mGapEnd, end - start - prelen);
1192  }
1193  }
1194 
1195  /* expand the gap to encompass the deleted characters */
1196  mGapEnd += end - mGapStart;
1197  mGapStart -= mGapStart - start;
1198 
1199  /* update the length */
1200  mLength -= end - start;
1201 
1202  /* fix up any selections which might be affected by the change */
1203  update_selections(start, end - start, 0);
1204 }
1205 
1206 
1207 /*
1208  simple setter.
1209  Unicode safe. Start and end must be at a character boundary.
1210  */
1211 void Fl_Text_Selection::set(int startpos, int endpos)
1212 {
1213  mSelected = startpos != endpos;
1214  mStart = min(startpos, endpos);
1215  mEnd = max(startpos, endpos);
1216 }
1217 
1218 
1219 /*
1220  simple getter.
1221  Unicode safe. Start and end will be at a character boundary.
1222  */
1223 int Fl_Text_Selection::position(int *startpos, int *endpos) const {
1224  if (!mSelected)
1225  return 0;
1226  *startpos = mStart;
1227  *endpos = mEnd;
1228 
1229  return 1;
1230 }
1231 
1232 
1233 /*
1234  Return if a position is inside the selected area.
1235  Unicode safe. Pos must be at a character boundary.
1236  */
1237 int Fl_Text_Selection::includes(int pos) const {
1238  return (selected() && pos >= start() && pos < end() );
1239 }
1240 
1241 
1242 /*
1243  Return a duplicate of the selected text, or an empty string.
1244  Unicode safe.
1245  */
1247  int start, end;
1248 
1249  /* If there's no selection, return an allocated empty string */
1250  if (!sel->position(&start, &end))
1251  {
1252  char *s = (char *) malloc(1);
1253  *s = '\0';
1254  return s;
1255  }
1256 
1257  /* Return the selected range */
1258  return text_range(start, end);
1259 }
1260 
1261 
1262 /*
1263  Remove the selected text.
1264  Unicode safe.
1265  */
1267 {
1268  int start, end;
1269 
1270  if (!sel->position(&start, &end))
1271  return;
1272  remove(start, end);
1273  //undoyankcut = undocut;
1274 }
1275 
1276 
1277 /*
1278  Replace selection with text.
1279  Unicode safe.
1280  */
1282  const char *text)
1283 {
1284  Fl_Text_Selection oldSelection = *sel;
1285 
1286  /* If there's no selection, return */
1287  int start, end;
1288  if (!sel->position(&start, &end))
1289  return;
1290 
1291  /* Do the appropriate type of replace */
1292  replace(start, end, text);
1293 
1294  /* Unselect (happens automatically in BufReplace, but BufReplaceRect
1295  can't detect when the contents of a selection goes away) */
1296  sel->mSelected = 0;
1297  redisplay_selection(&oldSelection, sel);
1298 }
1299 
1300 
1301 /*
1302  Call all callbacks.
1303  Unicode safe.
1304  */
1305 void Fl_Text_Buffer::call_modify_callbacks(int pos, int nDeleted,
1306  int nInserted, int nRestyled,
1307  const char *deletedText) const {
1308  IS_UTF8_ALIGNED2(this, pos)
1309  for (int i = 0; i < mNModifyProcs; i++)
1310  (*mModifyProcs[i]) (pos, nInserted, nDeleted, nRestyled,
1311  deletedText, mCbArgs[i]);
1312 }
1313 
1314 
1315 /*
1316  Call all callbacks.
1317  Unicode safe.
1318  */
1319 void Fl_Text_Buffer::call_predelete_callbacks(int pos, int nDeleted) const {
1320  for (int i = 0; i < mNPredeleteProcs; i++)
1321  (*mPredeleteProcs[i]) (pos, nDeleted, mPredeleteCbArgs[i]);
1322 }
1323 
1324 
1325 /*
1326  Redisplay a new selected area.
1327  Unicode safe.
1328  */
1330  oldSelection,
1332  newSelection) const
1333 {
1334  int oldStart, oldEnd, newStart, newEnd, ch1Start, ch1End, ch2Start,
1335  ch2End;
1336 
1337  /* If either selection is rectangular, add an additional character to
1338  the end of the selection to request the redraw routines to wipe out
1339  the parts of the selection beyond the end of the line */
1340  oldStart = oldSelection->mStart;
1341  newStart = newSelection->mStart;
1342  oldEnd = oldSelection->mEnd;
1343  newEnd = newSelection->mEnd;
1344 
1345  /* If the old or new selection is unselected, just redisplay the
1346  single area that is (was) selected and return */
1347  if (!oldSelection->mSelected && !newSelection->mSelected)
1348  return;
1349  if (!oldSelection->mSelected)
1350  {
1351  call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL);
1352  return;
1353  }
1354  if (!newSelection->mSelected) {
1355  call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL);
1356  return;
1357  }
1358 
1359  /* If the selections are non-contiguous, do two separate updates
1360  and return */
1361  if (oldEnd < newStart || newEnd < oldStart) {
1362  call_modify_callbacks(oldStart, 0, 0, oldEnd - oldStart, NULL);
1363  call_modify_callbacks(newStart, 0, 0, newEnd - newStart, NULL);
1364  return;
1365  }
1366 
1367  /* Otherwise, separate into 3 separate regions: ch1, and ch2 (the two
1368  changed areas), and the unchanged area of their intersection,
1369  and update only the changed area(s) */
1370  ch1Start = min(oldStart, newStart);
1371  ch2End = max(oldEnd, newEnd);
1372  ch1End = max(oldStart, newStart);
1373  ch2Start = min(oldEnd, newEnd);
1374  if (ch1Start != ch1End)
1375  call_modify_callbacks(ch1Start, 0, 0, ch1End - ch1Start, NULL);
1376  if (ch2Start != ch2End)
1377  call_modify_callbacks(ch2Start, 0, 0, ch2End - ch2Start, NULL);
1378 }
1379 
1380 
1381 /*
1382  Move the gap around without changing buffer content.
1383  Unicode safe. Pos must be at a character boundary.
1384  */
1386 {
1387  int gapLen = mGapEnd - mGapStart;
1388 
1389  if (pos > mGapStart)
1390  memmove(&mBuf[mGapStart], &mBuf[mGapEnd], pos - mGapStart);
1391  else
1392  memmove(&mBuf[pos + gapLen], &mBuf[pos], mGapStart - pos);
1393  mGapEnd += pos - mGapStart;
1394  mGapStart += pos - mGapStart;
1395 }
1396 
1397 
1398 /*
1399  Create a larger gap.
1400  Unicode safe. Start must be at a character boundary.
1401  */
1402 void Fl_Text_Buffer::reallocate_with_gap(int newGapStart, int newGapLen)
1403 {
1404  char *newBuf = (char *) malloc(mLength + newGapLen);
1405  int newGapEnd = newGapStart + newGapLen;
1406 
1407  if (newGapStart <= mGapStart) {
1408  memcpy(newBuf, mBuf, newGapStart);
1409  memcpy(&newBuf[newGapEnd], &mBuf[newGapStart],
1410  mGapStart - newGapStart);
1411  memcpy(&newBuf[newGapEnd + mGapStart - newGapStart],
1412  &mBuf[mGapEnd], mLength - mGapStart);
1413  } else { /* newGapStart > mGapStart */
1414  memcpy(newBuf, mBuf, mGapStart);
1415  memcpy(&newBuf[mGapStart], &mBuf[mGapEnd], newGapStart - mGapStart);
1416  memcpy(&newBuf[newGapEnd],
1417  &mBuf[mGapEnd + newGapStart - mGapStart],
1418  mLength - newGapStart);
1419  }
1420  free((void *) mBuf);
1421  mBuf = newBuf;
1422  mGapStart = newGapStart;
1423  mGapEnd = newGapEnd;
1424 }
1425 
1426 
1427 /*
1428  Update selection range if characters were inserted.
1429  Unicode safe. Pos must be at a character boundary.
1430  */
1431 void Fl_Text_Buffer::update_selections(int pos, int nDeleted,
1432  int nInserted)
1433 {
1434  mPrimary.update(pos, nDeleted, nInserted);
1435  mSecondary.update(pos, nDeleted, nInserted);
1436  mHighlight.update(pos, nDeleted, nInserted);
1437 }
1438 
1439 
1440 // unicode safe, assuming the arguments are on character boundaries
1441 void Fl_Text_Selection::update(int pos, int nDeleted, int nInserted)
1442 {
1443  if (!mSelected || pos > mEnd)
1444  return;
1445  if (pos + nDeleted <= mStart) {
1446  mStart += nInserted - nDeleted;
1447  mEnd += nInserted - nDeleted;
1448  } else if (pos <= mStart && pos + nDeleted >= mEnd) {
1449  mStart = pos;
1450  mEnd = pos;
1451  mSelected = 0;
1452  } else if (pos <= mStart && pos + nDeleted < mEnd) {
1453  mStart = pos;
1454  mEnd = nInserted + mEnd - nDeleted;
1455  } else if (pos < mEnd) {
1456  mEnd += nInserted - nDeleted;
1457  if (mEnd <= mStart)
1458  mSelected = 0;
1459  }
1460 }
1461 
1462 
1463 /*
1464  Find a UCS-4 character.
1465  StartPos must be at a character boundary, searchChar is UCS-4 encoded.
1466  */
1467 int Fl_Text_Buffer::findchar_forward(int startPos, unsigned searchChar,
1468  int *foundPos) const
1469 {
1470  if (startPos >= mLength) {
1471  *foundPos = mLength;
1472  return 0;
1473  }
1474 
1475  if (startPos<0)
1476  startPos = 0;
1477 
1478  for ( ; startPos<mLength; startPos = next_char(startPos)) {
1479  if (searchChar == char_at(startPos)) {
1480  *foundPos = startPos;
1481  return 1;
1482  }
1483  }
1484 
1485  *foundPos = mLength;
1486  return 0;
1487 }
1488 
1489 
1490 /*
1491  Find a UCS-4 character.
1492  StartPos must be at a character boundary, searchChar is UCS-4 encoded.
1493  */
1494 int Fl_Text_Buffer::findchar_backward(int startPos, unsigned int searchChar,
1495  int *foundPos) const {
1496  if (startPos <= 0) {
1497  *foundPos = 0;
1498  return 0;
1499  }
1500 
1501  if (startPos > mLength)
1502  startPos = mLength;
1503 
1504  for (startPos = prev_char(startPos); startPos>=0; startPos = prev_char(startPos)) {
1505  if (searchChar == char_at(startPos)) {
1506  *foundPos = startPos;
1507  return 1;
1508  }
1509  }
1510 
1511  *foundPos = 0;
1512  return 0;
1513 }
1514 
1515 //#define EXAMPLE_ENCODING // shows how to process any encoding for which a decoding function exists
1516 #ifdef EXAMPLE_ENCODING
1517 
1518 // returns the UCS equivalent of *p in CP1252 and advances p by 1
1519 unsigned cp1252toucs(char* &p)
1520 {
1521  // Codes 0x80..0x9f from the Microsoft CP1252 character set, translated
1522  // to Unicode
1523  static unsigned cp1252[32] = {
1524  0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
1525  0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
1526  0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
1527  0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178
1528  };
1529  unsigned char uc = *(unsigned char*)p;
1530  p++;
1531  return (uc < 0x80 || uc >= 0xa0 ? uc : cp1252[uc - 0x80]);
1532 }
1533 
1534 // returns the UCS equivalent of *p in UTF-16 and advances p by 2 (or more for surrogates)
1535 unsigned utf16toucs(char* &p)
1536 {
1537  union {
1538 #if WORDS_BIGENDIAN
1539  struct { unsigned char a, b;} chars;
1540 #else
1541  struct { unsigned char b, a;} chars;
1542 #endif
1543  U16 short_val;
1544  } u;
1545  u.chars.a = *(unsigned char*)p++;
1546  u.chars.b = *(unsigned char*)p++;
1547  return u.short_val;
1548 }
1549 
1550 // filter that produces, from an input stream fed by reading from fp,
1551 // a UTF-8-encoded output stream written in buffer.
1552 // Input can be any (e.g., 8-bit, UTF-16) encoding.
1553 // Output is true UTF-8.
1554 // p_trf points to a function that transforms encoded byte(s) into one UCS
1555 // and that increases the pointer by the adequate quantity
1556 static int general_input_filter(char *buffer, int buflen,
1557  char *line, int sline, char* &endline,
1558  unsigned (*p_trf)(char* &),
1559  FILE *fp)
1560 {
1561  char *p, *q, multibyte[5];
1562  int lq, r, offset;
1563  p = line;
1564  q = buffer;
1565  while (q < buffer + buflen) {
1566  if (p >= endline) {
1567  r = fread(line, 1, sline, fp);
1568  endline = line + r;
1569  if (r == 0) return q - buffer;
1570  p = line;
1571  }
1572  if (q + 4 /*max width of utf-8 char*/ > buffer + buflen) {
1573  memmove(line, p, endline - p);
1574  endline -= (p - line);
1575  return q - buffer;
1576  }
1577  lq = fl_utf8encode( p_trf(p), multibyte );
1578  memcpy(q, multibyte, lq);
1579  q += lq;
1580  }
1581  memmove(line, p, endline - p);
1582  endline -= (p - line);
1583  return q - buffer;
1584 }
1585 #endif // EXAMPLE_ENCODING
1586 
1587 /*
1588  filter that produces, from an input stream fed by reading from fp,
1589  a UTF-8-encoded output stream written in buffer.
1590  Returns #bytes read into 'buffer'.
1591 
1592  Input can be UTF-8. If it is not, it is decoded with CP1252.
1593  Output is UTF-8.
1594 
1595  *input_was_changed returns true if input was not strict UTF-8, so output
1596  differs from input.
1597  */
1598 static int utf8_input_filter(char *buffer, // result buffer we fill with utf8 encoded text
1599  int buflen, // max size of buffer from caller
1600  char *line, // file line buffer caller wants us to use
1601  int sline, // max size of line buffer
1602  char* &endline, // keeps track of leftovers in line[] buffer between calls
1603  FILE *fp, // open file we're reading data from
1604  int *input_was_changed) // returned flag: 'true' if buffer[] different from file due to utf8 encoding
1605 {
1606  // p - work pointer to line[]
1607  // q - work pointer to buffer[]
1608  // l - length of utf8 sequence being worked on
1609  // lp - fl_utf8decode() length of utf8 sequence being worked on
1610  // lq - fl_utf8encode() length of utf8 sequence being worked on
1611  // r - bytes read from last fread()
1612  // u - utf8 decoded sequence as a single multibyte unsigned integer
1613  char *p, *q, multibyte[5];
1614  int l, lp, lq, r;
1615  unsigned u;
1616  p = line;
1617  q = buffer;
1618  while (q < buffer + buflen) {
1619  if (p >= endline) { // walked off end of input file's line buffer?
1620  r = (int) fread(line, 1, sline, fp); // read another block of sline bytes from file
1621  endline = line + r;
1622  if (r == 0) return (int) (q - buffer); // EOF? return bytes read into buffer[]
1623  p = line;
1624  }
1625  // Predict length of utf8 sequence
1626  // See if utf8 seq we're working on would extend off end of line buffer,
1627  // and if so, adjust + load more data so that it doesn't.
1628  //
1629  l = fl_utf8len1(*p); // anticipate length of utf8 sequence
1630  if (p + l > endline) { // would walk off end of line buffer?
1631  memmove(line, p, endline - p); // re-jigger line buffer to get some room
1632  endline -= (p - line);
1633  r = (int) fread(endline, 1, sline - (endline - line), fp); // re-fill line buffer
1634  endline += r;
1635  p = line;
1636  if (endline - line < l) break; // sequence *still* extends past end? stop loop
1637  }
1638  while ( l > 0) {
1639  u = fl_utf8decode(p, p+l, &lp); // get single utf8 encoded char as a Unicode value
1640  lq = fl_utf8encode(u, multibyte); // re-encode Unicode value to utf8 in multibyte[]
1641  if (lp != l || lq != l) *input_was_changed = true;
1642 
1643  if (q + lq > buffer + buflen) { // encoding would walk off end of buffer[]?
1644  memmove(line, p, endline - p); // re-jigger line[] buffer for next call
1645  endline -= (p - line); // adjust end of line[] buffer for next call
1646  return (int) (q - buffer); // return what's decoded so far, caller will consume buffer
1647  }
1648  memcpy(q, multibyte, lq);
1649  q += lq;
1650  p += lp;
1651  l -= lp;
1652  }
1653  }
1654  memmove(line, p, endline - p);
1655  endline -= (p - line);
1656  return (int) (q - buffer);
1657 }
1658 
1660 "Displayed text contains the UTF-8 transcoding\n"
1661 "of the input file which was not UTF-8 encoded.\n"
1662 "Some changes may have occurred.";
1663 
1664 /*
1665  Insert text from a file.
1666  Input file can be of various encodings according to what input fiter is used.
1667  utf8_input_filter accepts UTF-8 or CP1252 as input encoding.
1668  Output is always UTF-8.
1669  */
1670  int Fl_Text_Buffer::insertfile(const char *file, int pos, int buflen)
1671 {
1672  FILE *fp;
1673  if (!(fp = fl_fopen(file, "r")))
1674  return 1;
1675  char *buffer = new char[buflen + 1];
1676  char *endline, line[100];
1677  int l;
1678  input_file_was_transcoded = false;
1679  endline = line;
1680  while (true) {
1681 #ifdef EXAMPLE_ENCODING
1682  // example of 16-bit encoding: UTF-16
1683  l = general_input_filter(buffer, buflen,
1684  line, sizeof(line), endline,
1685  utf16toucs, // use cp1252toucs to read CP1252-encoded files
1686  fp);
1688 #else
1689  l = utf8_input_filter(buffer, buflen, line, sizeof(line), endline,
1691 #endif
1692  if (l == 0) break;
1693  buffer[l] = 0;
1694  insert(pos, buffer);
1695  pos += l;
1696  }
1697  int e = ferror(fp) ? 2 : 0;
1698  fclose(fp);
1699  delete[]buffer;
1702  }
1703  return e;
1704 }
1705 
1706 
1707 /*
1708  Write text to file.
1709  Unicode safe.
1710  */
1711 int Fl_Text_Buffer::outputfile(const char *file,
1712  int start, int end,
1713  int buflen) {
1714  FILE *fp;
1715  if (!(fp = fl_fopen(file, "w")))
1716  return 1;
1717  for (int n; (n = min(end - start, buflen)); start += n) {
1718  const char *p = text_range(start, start + n);
1719  int r = (int) fwrite(p, 1, n, fp);
1720  free((void *) p);
1721  if (r != n)
1722  break;
1723  }
1724 
1725  int e = ferror(fp) ? 2 : 0;
1726  fclose(fp);
1727  return e;
1728 }
1729 
1730 
1731 /*
1732  Return the previous character position.
1733  Unicode safe.
1734  */
1736 {
1737  if (pos<=0)
1738  return 0;
1739 
1740  IS_UTF8_ALIGNED2(this, (pos))
1741 
1742  char c;
1743  do {
1744  pos--;
1745  if (pos==0)
1746  return 0;
1747  c = byte_at(pos);
1748  } while ( (c&0xc0) == 0x80);
1749 
1750  IS_UTF8_ALIGNED2(this, (pos))
1751  return pos;
1752 }
1753 
1754 
1755 /*
1756  Return the previous character position.
1757  Returns -1 if the beginning of the buffer is reached.
1758  */
1759 int Fl_Text_Buffer::prev_char(int pos) const
1760 {
1761  if (pos==0) return -1;
1762  return prev_char_clipped(pos);
1763 }
1764 
1765 
1766 /*
1767  Return the next character position.
1768  Returns length() if the end of the buffer is reached.
1769  */
1770 int Fl_Text_Buffer::next_char(int pos) const
1771 {
1772  IS_UTF8_ALIGNED2(this, (pos))
1773  int n = fl_utf8len1(byte_at(pos));
1774  pos += n;
1775  if (pos>=mLength)
1776  return mLength;
1777  IS_UTF8_ALIGNED2(this, (pos))
1778  return pos;
1779 }
1780 
1781 
1782 /*
1783  Return the next character position.
1784  If the end of the buffer is reached, it returns the current position.
1785  */
1787 {
1788  return next_char(pos);
1789 }
1790 
1791 /*
1792  Align an index to the current UTF-8 boundary.
1793  */
1794 int Fl_Text_Buffer::utf8_align(int pos) const
1795 {
1796  char c = byte_at(pos);
1797  while ( (c&0xc0) == 0x80) {
1798  pos--;
1799  c = byte_at(pos);
1800  }
1801  return pos;
1802 }
1803 
1804 //
1805 // End of "$Id$".
1806 //
min
static int min(int i1, int i2)
Definition: Fl_Text_Buffer.cxx:63
Fl_Text_Buffer::transcoding_warning_action
void(* transcoding_warning_action)(Fl_Text_Buffer *)
Pointer to a function called after reading a non UTF-8 encoded file.
Definition: Fl_Text_Buffer.H:701
Fl_Text_Buffer::Fl_Text_Buffer
Fl_Text_Buffer(int requestedSize=0, int preferredGapSize=1024)
Definition: Fl_Text_Buffer.cxx:105
Fl_Text_Buffer::line_text
char * line_text(int pos) const
Definition: Fl_Text_Buffer.cxx:802
Fl_Text_Buffer::secondary_selection_position
int secondary_selection_position(int *start, int *end)
Definition: Fl_Text_Buffer.cxx:573
Fl_Text_Buffer::secondary_unselect
void secondary_unselect()
Definition: Fl_Text_Buffer.cxx:561
Fl_Text_Selection::start
int start() const
Return the byte offset to the first selected character.
Definition: Fl_Text_Buffer.H:95
IS_UTF8_ALIGNED2
#define IS_UTF8_ALIGNED2(a, b)
Definition: Fl_Text_Buffer.H:34
Fl_Text_Selection
This is an internal class for Fl_Text_Buffer to manage text selections. This class works correctly wi...
Definition: Fl_Text_Buffer.H:69
Fl.H
Fl_Text_Buffer::line_end
int line_end(int pos) const
Definition: Fl_Text_Buffer.cxx:821
Fl_Text_Buffer::mGapStart
int mGapStart
Definition: Fl_Text_Buffer.H:779
Fl_Text_Buffer::mCanUndo
char mCanUndo
Definition: Fl_Text_Buffer.H:794
Fl_Text_Buffer::findchar_backward
int findchar_backward(int startPos, unsigned int searchChar, int *foundPos) const
Definition: Fl_Text_Buffer.cxx:1494
Fl_Text_Buffer::mTabDist
int mTabDist
Definition: Fl_Text_Buffer.H:783
chars
static const Fl_Glut_StrokeChar * chars[]
Definition: freeglut_stroke_mono_roman.cxx:2828
Fl_Text_Buffer::~Fl_Text_Buffer
~Fl_Text_Buffer()
Definition: Fl_Text_Buffer.cxx:135
undoat
static int undoat
Definition: Fl_Text_Buffer.cxx:74
Fl_Text_Buffer::mPredeleteProcs
Fl_Text_Predelete_Cb * mPredeleteProcs
Definition: Fl_Text_Buffer.H:789
Fl_Text_Buffer::skip_lines
int skip_lines(int startPos, int nLines)
Definition: Fl_Text_Buffer.cxx:933
Fl_Text_Buffer::undo
int undo(int *cp=0)
Definition: Fl_Text_Buffer.cxx:407
Fl_Text_Buffer::text_range
char * text_range(int start, int end) const
Get a copy of a part of the text buffer. Return a copy of the text between start and end character po...
Definition: Fl_Text_Buffer.cxx:200
Fl_Text_Buffer::canUndo
void canUndo(char flag=1)
Definition: Fl_Text_Buffer.cxx:448
endline
#define endline()
Definition: gl2opengl.h:17
fl_ask.H
Fl_Text_Buffer::insert
void insert(int pos, const char *text)
Definition: Fl_Text_Buffer.cxx:269
Fl_Text_Buffer::input_file_was_transcoded
int input_file_was_transcoded
true if the loaded file has been transcoded to UTF-8.
Definition: Fl_Text_Buffer.H:685
U16
#define U16
Definition: config.h:200
undobuffer
static char * undobuffer
Definition: Fl_Text_Buffer.cxx:71
undoyankcut
static int undoyankcut
Definition: Fl_Text_Buffer.cxx:77
free
void free()
offset
static double offset[5]
Definition: fl_rounded_box.cxx:32
Fl_Text_Buffer::mNPredeleteProcs
int mNPredeleteProcs
Definition: Fl_Text_Buffer.H:788
Fl_Text_Buffer::rewind_lines
int rewind_lines(int startPos, int nLines)
Definition: Fl_Text_Buffer.cxx:971
NULL
#define NULL
Definition: forms.H:34
Fl_Text_Buffer::mHighlight
Fl_Text_Selection mHighlight
Definition: Fl_Text_Buffer.H:774
Fl_Text_Buffer::address
const char * address(int pos) const
Definition: Fl_Text_Buffer.H:229
Fl_Text_Buffer::replace_selection
void replace_selection(const char *text)
Definition: Fl_Text_Buffer.cxx:539
Fl_Text_Buffer::mBuf
char * mBuf
Definition: Fl_Text_Buffer.H:778
Fl_Text_Buffer::highlight_text
char * highlight_text()
Definition: Fl_Text_Buffer.cxx:643
Fl_Text_Buffer::prev_char
int prev_char(int ix) const
Definition: Fl_Text_Buffer.cxx:1759
Fl_Text_Buffer::add_modify_callback
void add_modify_callback(Fl_Text_Modify_Cb bufModifiedCB, void *cbArg)
Definition: Fl_Text_Buffer.cxx:652
Fl_Text_Buffer.H
undoinsert
static int undoinsert
Definition: Fl_Text_Buffer.cxx:76
fl_fopen
FILE * fl_fopen(const char *f, const char *mode)
Definition: fl_utf8.cxx:498
Fl_Text_Buffer::length
int length() const
Returns the number of bytes in the buffer.
Definition: Fl_Text_Buffer.H:180
b
long b
Definition: jpegint.h:397
Fl_Text_Buffer::word_start
int word_start(int pos) const
Definition: Fl_Text_Buffer.cxx:832
Fl_Text_Buffer::unhighlight
void unhighlight()
Definition: Fl_Text_Buffer.cxx:622
Fl_Text_Buffer::char_at
unsigned int char_at(int pos) const
Definition: Fl_Text_Buffer.cxx:242
Fl_Text_Buffer::remove_modify_callback
void remove_modify_callback(Fl_Text_Modify_Cb bufModifiedCB, void *cbArg)
Definition: Fl_Text_Buffer.cxx:677
undobuffersize
static void undobuffersize(int n)
Definition: Fl_Text_Buffer.cxx:82
buffer
static char * buffer
Definition: file.cxx:215
fl_utf8decode
unsigned fl_utf8decode(const char *p, const char *end, int *len)
Definition: fl_utf.c:137
Fl_Text_Selection::position
int position(int *start, int *end) const
Return the positions of this selection.
Definition: Fl_Text_Buffer.cxx:1223
Fl_Text_Buffer::remove
void remove(int start, int end)
Definition: Fl_Text_Buffer.cxx:327
Fl_Text_Buffer::utf8_align
int utf8_align(int) const
Definition: Fl_Text_Buffer.cxx:1794
p
static menustate * p
Definition: Fl_Menu.cxx:606
Fl_Text_Buffer::search_backward
int search_backward(int startPos, const char *searchString, int *foundPos, int matchCase=0) const
Definition: Fl_Text_Buffer.cxx:1058
Fl_Text_Buffer::count_displayed_characters
int count_displayed_characters(int lineStartPos, int targetPos) const
Definition: Fl_Text_Buffer.cxx:861
Fl_Text_Buffer::move_gap
void move_gap(int pos)
Definition: Fl_Text_Buffer.cxx:1385
Fl_Text_Predelete_Cb
void(* Fl_Text_Predelete_Cb)(int pos, int nDeleted, void *cbArg)
Definition: Fl_Text_Buffer.H:143
Fl_Text_Buffer::highlight
int highlight()
Definition: Fl_Text_Buffer.H:444
fl_utf8.h
header for Unicode and UTF-8 character handling
Fl_Text_Buffer::mSecondary
Fl_Text_Selection mSecondary
Definition: Fl_Text_Buffer.H:773
Fl_Text_Buffer::word_end
int word_end(int pos) const
Definition: Fl_Text_Buffer.cxx:848
fl_utf8len1
int fl_utf8len1(char c)
Definition: fl_utf8.cxx:141
Fl_Text_Buffer::outputfile
int outputfile(const char *file, int start, int end, int buflen=128 *1024)
Definition: Fl_Text_Buffer.cxx:1711
Fl_Text_Selection::mEnd
int mEnd
byte offset to the character after the last selected character
Definition: Fl_Text_Buffer.H:133
Fl_Text_Buffer::remove_selection_
void remove_selection_(Fl_Text_Selection *sel)
Definition: Fl_Text_Buffer.cxx:1266
Fl_Text_Buffer::skip_displayed_characters
int skip_displayed_characters(int lineStartPos, int nChars)
Definition: Fl_Text_Buffer.cxx:882
Fl_Text_Buffer::mCbArgs
void ** mCbArgs
Definition: Fl_Text_Buffer.H:787
max
static int max(int i1, int i2)
Definition: Fl_Text_Buffer.cxx:58
Fl_Text_Buffer::update_selections
void update_selections(int pos, int nDeleted, int nInserted)
Definition: Fl_Text_Buffer.cxx:1431
Fl_Text_Buffer::remove_
void remove_(int start, int end)
Definition: Fl_Text_Buffer.cxx:1159
Fl_Text_Buffer::next_char
int next_char(int ix) const
Definition: Fl_Text_Buffer.cxx:1770
Fl_Text_Selection::selected
bool selected() const
Returns true if any text is selected.
Definition: Fl_Text_Buffer.H:108
Fl_Text_Buffer::selection_position
int selection_position(int *start, int *end)
Definition: Fl_Text_Buffer.cxx:512
Fl_Text_Buffer::tab_distance
int tab_distance() const
Definition: Fl_Text_Buffer.H:349
Fl_Text_Buffer::byte_at
char byte_at(int pos) const
Definition: Fl_Text_Buffer.cxx:257
Fl_Text_Selection::update
void update(int pos, int nDeleted, int nInserted)
Updates a selection after text was modified.
Definition: Fl_Text_Buffer.cxx:1441
Fl_Text_Buffer::mGapEnd
int mGapEnd
Definition: Fl_Text_Buffer.H:780
Fl_Text_Buffer::mPredeleteCbArgs
void ** mPredeleteCbArgs
Definition: Fl_Text_Buffer.H:791
Fl_Text_Buffer::call_predelete_callbacks
void call_predelete_callbacks()
Definition: Fl_Text_Buffer.H:502
Fl_Text_Buffer::replace_selection_
void replace_selection_(Fl_Text_Selection *sel, const char *text)
Definition: Fl_Text_Buffer.cxx:1281
Fl_Text_Buffer::select
void select(int start, int end)
Definition: Fl_Text_Buffer.cxx:485
Fl_Text_Buffer::secondary_select
void secondary_select(int start, int end)
Definition: Fl_Text_Buffer.cxx:549
Fl_Text_Buffer::mLength
int mLength
Definition: Fl_Text_Buffer.H:775
Fl_Text_Buffer::text
char * text() const
Get a copy of the entire contents of the text buffer. Memory is allocated to contain the returned str...
Definition: Fl_Text_Buffer.cxx:153
Fl_Text_Buffer::add_predelete_callback
void add_predelete_callback(Fl_Text_Predelete_Cb bufPredelCB, void *cbArg)
Definition: Fl_Text_Buffer.cxx:728
Fl_Text_Modify_Cb
void(* Fl_Text_Modify_Cb)(int pos, int nInserted, int nDeleted, int nRestyled, const char *deletedText, void *cbArg)
Definition: Fl_Text_Buffer.H:138
Fl_Text_Buffer::count_lines
int count_lines(int startPos, int endPos) const
Definition: Fl_Text_Buffer.cxx:903
Fl_Text_Buffer::remove_secondary_selection
void remove_secondary_selection()
Definition: Fl_Text_Buffer.cxx:591
Fl_Text_Buffer::replace_secondary_selection
void replace_secondary_selection(const char *text)
Definition: Fl_Text_Buffer.cxx:600
Fl_Text_Buffer::mNModifyProcs
int mNModifyProcs
Definition: Fl_Text_Buffer.H:784
Fl_Text_Buffer::highlight_position
int highlight_position(int *start, int *end)
Definition: Fl_Text_Buffer.cxx:634
Fl_Text_Buffer::secondary_selection_text
char * secondary_selection_text()
Definition: Fl_Text_Buffer.cxx:582
Fl_Text_Buffer::next_char_clipped
int next_char_clipped(int ix) const
Definition: Fl_Text_Buffer.cxx:1786
fl_utf8encode
int fl_utf8encode(unsigned ucs, char *buf)
Definition: fl_utf.c:309
Fl_Text_Selection::end
int end() const
Return the byte offset to the character after the last selected character.
Definition: Fl_Text_Buffer.H:101
Fl_Text_Buffer::unselect
void unselect()
Definition: Fl_Text_Buffer.cxx:500
Fl_Text_Selection::includes
int includes(int pos) const
Definition: Fl_Text_Buffer.cxx:1237
Fl_Text_Buffer::mPrimary
Fl_Text_Selection mPrimary
Definition: Fl_Text_Buffer.H:772
Fl_Text_Selection::set
void set(int start, int end)
Set the selection range.
Definition: Fl_Text_Buffer.cxx:1211
Fl_Text_Selection::mStart
int mStart
byte offset to the first selected character
Definition: Fl_Text_Buffer.H:132
Fl_Text_Buffer::prev_char_clipped
int prev_char_clipped(int ix) const
Definition: Fl_Text_Buffer.cxx:1735
malloc
voidp malloc()
Fl_Text_Buffer::selection_text
char * selection_text()
Definition: Fl_Text_Buffer.cxx:521
Fl_Text_Buffer::redisplay_selection
void redisplay_selection(Fl_Text_Selection *oldSelection, Fl_Text_Selection *newSelection) const
Definition: Fl_Text_Buffer.cxx:1329
Fl_Text_Buffer::replace
void replace(int start, int end, const char *text)
Definition: Fl_Text_Buffer.cxx:299
Fl_Text_Buffer::remove_selection
void remove_selection()
Definition: Fl_Text_Buffer.cxx:530
utf8_input_filter
static int utf8_input_filter(char *buffer, int buflen, char *line, int sline, char *&endline, FILE *fp, int *input_was_changed)
Definition: Fl_Text_Buffer.cxx:1598
flstring.h
cp1252
static unsigned short cp1252[32]
Definition: fl_utf.c:98
Fl_Text_Buffer::selection_text_
char * selection_text_(Fl_Text_Selection *sel) const
Definition: Fl_Text_Buffer.cxx:1246
undowidget
static Fl_Text_Buffer * undowidget
Definition: Fl_Text_Buffer.cxx:73
Fl_Text_Buffer::reallocate_with_gap
void reallocate_with_gap(int newGapStart, int newGapLen)
Definition: Fl_Text_Buffer.cxx:1402
Fl_Text_Buffer::remove_predelete_callback
void remove_predelete_callback(Fl_Text_Predelete_Cb predelCB, void *cbArg)
Definition: Fl_Text_Buffer.cxx:753
undobufferlength
static int undobufferlength
Definition: Fl_Text_Buffer.cxx:72
Fl_Text_Buffer::mModifyProcs
Fl_Text_Modify_Cb * mModifyProcs
Definition: Fl_Text_Buffer.H:785
Fl_Text_Buffer::mPreferredGapSize
int mPreferredGapSize
Definition: Fl_Text_Buffer.H:796
Fl_Text_Buffer::insertfile
int insertfile(const char *file, int pos, int buflen=128 *1024)
Definition: Fl_Text_Buffer.cxx:1670
Fl_Text_Buffer::insert_
int insert_(int pos, const char *text)
Definition: Fl_Text_Buffer.cxx:1116
Fl_Text_Buffer::search_forward
int search_forward(int startPos, const char *searchString, int *foundPos, int matchCase=0) const
Definition: Fl_Text_Buffer.cxx:1006
Fl::error
static void(* error)(const char *,...)
Definition: Fl.H:513
Fl_Text_Buffer::line_start
int line_start(int pos) const
Definition: Fl_Text_Buffer.cxx:810
Fl_Text_Buffer::file_encoding_warning_message
static const char * file_encoding_warning_message
Definition: Fl_Text_Buffer.H:690
IS_UTF8_ALIGNED
#define IS_UTF8_ALIGNED(a)
Definition: Fl_Text_Buffer.H:33
Fl_Text_Buffer
This class manages Unicode text displayed in one or more Fl_Text_Display widgets.
Definition: Fl_Text_Buffer.H:158
start
static int start(Fl_RGB_Image *img, int XP, int YP, int WP, int HP, int w, int h, int &cx, int &cy, int &X, int &Y, int &W, int &H)
Definition: Fl_Image.cxx:655
Fl_Text_Buffer::copy
void copy(Fl_Text_Buffer *fromBuf, int fromStart, int fromEnd, int toPos)
Definition: Fl_Text_Buffer.cxx:364
Fl_Text_Buffer::mCursorPosHint
int mCursorPosHint
Definition: Fl_Text_Buffer.H:792
fl_alert
void fl_alert(const char *,...)
Definition: fl_ask.cxx:361
def_transcoding_warning_action
static void def_transcoding_warning_action(Fl_Text_Buffer *text)
Definition: Fl_Text_Buffer.cxx:97
fl_tolower
int fl_tolower(unsigned int ucs)
Definition: fl_utf8.cxx:229
Fl_Text_Selection::mSelected
bool mSelected
this flag is set if any text is selected
Definition: Fl_Text_Buffer.H:134
undocut
static int undocut
Definition: Fl_Text_Buffer.cxx:75
Fl_Text_Buffer::call_modify_callbacks
void call_modify_callbacks()
Definition: Fl_Text_Buffer.H:485
buflen
static int buflen
Definition: file.cxx:216
Fl_Text_Buffer::findchar_forward
int findchar_forward(int startPos, unsigned searchChar, int *foundPos) const
Definition: Fl_Text_Buffer.cxx:1467