1 static const char CVSID
[] = "$Id: textBuf.c,v 1.31 2004/03/03 13:28:20 edg Exp $";
2 /*******************************************************************************
4 * textBuf.c - Manage source text for one or more text areas *
6 * Copyright (C) 1999 Mark Edel *
8 * This is free software; you can redistribute it and/or modify it under the *
9 * terms of the GNU General Public License as published by the Free Software *
10 * Foundation; either version 2 of the License, or (at your option) any later *
13 * This software is distributed in the hope that it will be useful, but WITHOUT *
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
18 * You should have received a copy of the GNU General Public License along with *
19 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
20 * Place, Suite 330, Boston, MA 02111-1307 USA *
22 * Nirvana Text Editor *
25 * Written by Mark Edel *
27 *******************************************************************************/
30 #include "../config.h"
45 #define PREFERRED_GAP_SIZE 80 /* Initial size for the buffer gap (empty space
46 in the buffer where text might be inserted
47 if the user is typing sequential chars) */
49 static void histogramCharacters(const char *string
, int length
, char hist
[256],
51 static void subsChars(char *string
, int length
, char fromChar
, char toChar
);
52 static char chooseNullSubsChar(char hist
[256]);
53 static int insert(textBuffer
*buf
, int pos
, const char *text
);
54 static void delete(textBuffer
*buf
, int start
, int end
);
55 static void deleteRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
56 int rectEnd
, int *replaceLen
, int *endPos
);
57 static void insertCol(textBuffer
*buf
, int column
, int startPos
, const char *insText
,
58 int *nDeleted
, int *nInserted
, int *endPos
);
59 static void overlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
60 int rectEnd
, const char *insText
, int *nDeleted
, int *nInserted
, int *endPos
);
61 static void insertColInLine(const char *line
, const char *insLine
, int column
, int insWidth
,
62 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
64 static void deleteRectFromLine(const char *line
, int rectStart
, int rectEnd
,
65 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
67 static void overlayRectInLine(const char *line
, const char *insLine
, int rectStart
,
68 int rectEnd
, int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
,
69 int *outLen
, int *endOffset
);
70 static void callPreDeleteCBs(textBuffer
*buf
, int pos
, int nDeleted
);
71 static void callModifyCBs(textBuffer
*buf
, int pos
, int nDeleted
,
72 int nInserted
, int nRestyled
, char *deletedText
);
73 static void redisplaySelection(textBuffer
*buf
, selection
*oldSelection
,
74 selection
*newSelection
);
75 static void moveGap(textBuffer
*buf
, int pos
);
76 static void reallocateBuf(textBuffer
*buf
, int newGapStart
, int newGapLen
);
77 static void setSelection(selection
*sel
, int start
, int end
);
78 static void setRectSelect(selection
*sel
, int start
, int end
,
79 int rectStart
, int rectEnd
);
80 static void updateSelections(textBuffer
*buf
, int pos
, int nDeleted
,
82 static void updateSelection(selection
*sel
, int pos
, int nDeleted
,
84 static int getSelectionPos(selection
*sel
, int *start
, int *end
,
85 int *isRect
, int *rectStart
, int *rectEnd
);
86 static char *getSelectionText(textBuffer
*buf
, selection
*sel
);
87 static void removeSelected(textBuffer
*buf
, selection
*sel
);
88 static void replaceSelected(textBuffer
*buf
, selection
*sel
, const char *text
);
89 static void addPadding(char *string
, int startIndent
, int toIndent
,
90 int tabDist
, int useTabs
, char nullSubsChar
, int *charsAdded
);
91 static int searchForward(textBuffer
*buf
, int startPos
, char searchChar
,
93 static int searchBackward(textBuffer
*buf
, int startPos
, char searchChar
,
95 static char *copyLine(const char *text
, int *lineLen
);
96 static int countLines(const char *string
);
97 static int textWidth(const char *text
, int tabDist
, char nullSubsChar
);
98 static void findRectSelBoundariesForCopy(textBuffer
*buf
, int lineStartPos
,
99 int rectStart
, int rectEnd
, int *selStart
, int *selEnd
);
100 static char *realignTabs(const char *text
, int origIndent
, int newIndent
,
101 int tabDist
, int useTabs
, char nullSubsChar
, int *newLength
);
102 static char *expandTabs(const char *text
, int startIndent
, int tabDist
,
103 char nullSubsChar
, int *newLen
);
104 static char *unexpandTabs(const char *text
, int startIndent
, int tabDist
,
105 char nullSubsChar
, int *newLen
);
106 static int max(int i1
, int i2
);
107 static int min(int i1
, int i2
);
110 static const char *ControlCodeTable
[64] = {
111 "nul", "soh", "stx", "etx", "sel", "ht", "rnl", "del",
112 "ge", "sps", "rpt", "vt", "ff", "cr", "so", "si",
113 "dle", "dc1", "dc2", "dc3", "res", "nl", "bs", "poc",
114 "can", "em", "ubs", "cu1", "ifs", "igs", "irs", "ius",
115 "ds", "sos", "fs", "wus", "byp", "lf", "etb", "esc",
116 "sa", "sfe", "sm", "csp", "mfa", "enq", "ack", "bel",
117 "x30", "x31", "syn", "ir", "pp", "trn", "nbs", "eot",
118 "sbs", "it", "rff", "cu3", "dc4", "nak", "x3e", "sub"};
120 static const char *ControlCodeTable
[32] = {
121 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
122 "bs", "ht", "nl", "vt", "np", "cr", "so", "si",
123 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
124 "can", "em", "sub", "esc", "fs", "gs", "rs", "us"};
128 ** Create an empty text buffer
130 textBuffer
*BufCreate(void)
132 textBuffer
*buf
= BufCreatePreallocated(0);
137 ** Create an empty text buffer of a pre-determined size (use this to
138 ** avoid unnecessary re-allocation if you know exactly how much the buffer
141 textBuffer
*BufCreatePreallocated(int requestedSize
)
145 buf
= (textBuffer
*)XtMalloc(sizeof(textBuffer
));
147 buf
->buf
= XtMalloc(requestedSize
+ PREFERRED_GAP_SIZE
);
149 buf
->gapEnd
= PREFERRED_GAP_SIZE
;
152 buf
->primary
.selected
= False
;
153 buf
->primary
.zeroWidth
= False
;
154 buf
->primary
.rectangular
= False
;
155 buf
->primary
.start
= buf
->primary
.end
= 0;
156 buf
->secondary
.selected
= False
;
157 buf
->secondary
.zeroWidth
= False
;
158 buf
->secondary
.start
= buf
->secondary
.end
= 0;
159 buf
->secondary
.rectangular
= False
;
160 buf
->highlight
.selected
= False
;
161 buf
->highlight
.zeroWidth
= False
;
162 buf
->highlight
.start
= buf
->highlight
.end
= 0;
163 buf
->highlight
.rectangular
= False
;
164 buf
->modifyProcs
= NULL
;
166 buf
->nModifyProcs
= 0;
167 buf
->preDeleteProcs
= NULL
;
168 buf
->preDeleteCbArgs
= NULL
;
169 buf
->nPreDeleteProcs
= 0;
170 buf
->nullSubsChar
= '\0';
172 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
174 buf
->rangesetTable
= NULL
;
179 ** Free a text buffer
181 void BufFree(textBuffer
*buf
)
184 if (buf
->nModifyProcs
!= 0) {
185 XtFree((char *)buf
->modifyProcs
);
186 XtFree((char *)buf
->cbArgs
);
188 if (buf
->rangesetTable
)
189 RangesetTableFree(buf
->rangesetTable
);
190 if (buf
->nPreDeleteProcs
!= 0) {
191 XtFree((char *)buf
->preDeleteProcs
);
192 XtFree((char *)buf
->preDeleteCbArgs
);
198 ** Get the entire contents of a text buffer. Memory is allocated to contain
199 ** the returned string, which the caller must free.
201 char *BufGetAll(textBuffer
*buf
)
205 text
= XtMalloc(buf
->length
+1);
206 memcpy(text
, buf
->buf
, buf
->gapStart
);
207 memcpy(&text
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
208 buf
->length
- buf
->gapStart
);
209 text
[buf
->length
] = '\0';
214 ** Replace the entire contents of the text buffer
216 void BufSetAll(textBuffer
*buf
, const char *text
)
218 int length
, deletedLength
;
220 length
= strlen(text
);
222 callPreDeleteCBs(buf
, 0, buf
->length
);
224 /* Save information for redisplay, and get rid of the old buffer */
225 deletedText
= BufGetAll(buf
);
226 deletedLength
= buf
->length
;
229 /* Start a new buffer with a gap of PREFERRED_GAP_SIZE in the center */
230 buf
->buf
= XtMalloc(length
+ PREFERRED_GAP_SIZE
);
231 buf
->length
= length
;
232 buf
->gapStart
= length
/2;
233 buf
->gapEnd
= buf
->gapStart
+ PREFERRED_GAP_SIZE
;
234 memcpy(buf
->buf
, text
, buf
->gapStart
);
235 memcpy(&buf
->buf
[buf
->gapEnd
], &text
[buf
->gapStart
], length
-buf
->gapStart
);
237 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
240 /* Zero all of the existing selections */
241 updateSelections(buf
, 0, deletedLength
, 0);
243 /* Call the saved display routine(s) to update the screen */
244 callModifyCBs(buf
, 0, deletedLength
, length
, 0, deletedText
);
249 ** Return a copy of the text between "start" and "end" character positions
250 ** from text buffer "buf". Positions start at 0, and the range does not
251 ** include the character pointed to by "end"
253 char *BufGetRange(textBuffer
*buf
, int start
, int end
)
256 int length
, part1Length
;
258 /* Make sure start and end are ok, and allocate memory for returned string.
259 If start is bad, return "", if end is bad, adjust it. */
260 if (start
< 0 || start
> buf
->length
) {
270 if (end
> buf
->length
)
272 length
= end
- start
;
273 text
= XtMalloc(length
+1);
275 /* Copy the text from the buffer to the returned string */
276 if (end
<= buf
->gapStart
) {
277 memcpy(text
, &buf
->buf
[start
], length
);
278 } else if (start
>= buf
->gapStart
) {
279 memcpy(text
, &buf
->buf
[start
+(buf
->gapEnd
-buf
->gapStart
)], length
);
281 part1Length
= buf
->gapStart
- start
;
282 memcpy(text
, &buf
->buf
[start
], part1Length
);
283 memcpy(&text
[part1Length
], &buf
->buf
[buf
->gapEnd
], length
-part1Length
);
290 ** Return the character at buffer position "pos". Positions start at 0.
292 char BufGetCharacter(textBuffer
*buf
, int pos
)
294 if (pos
< 0 || pos
>= buf
->length
)
296 if (pos
< buf
->gapStart
)
297 return buf
->buf
[pos
];
299 return buf
->buf
[pos
+ buf
->gapEnd
-buf
->gapStart
];
303 ** Insert null-terminated string "text" at position "pos" in "buf"
305 void BufInsert(textBuffer
*buf
, int pos
, const char *text
)
309 /* if pos is not contiguous to existing text, make it */
310 if (pos
> buf
->length
) pos
= buf
->length
;
311 if (pos
< 0 ) pos
= 0;
313 /* Even if nothing is deleted, we must call these callbacks */
314 callPreDeleteCBs(buf
, pos
, 0);
316 /* insert and redisplay */
317 nInserted
= insert(buf
, pos
, text
);
318 buf
->cursorPosHint
= pos
+ nInserted
;
319 callModifyCBs(buf
, pos
, 0, nInserted
, 0, NULL
);
323 ** Delete the characters between "start" and "end", and insert the
324 ** null-terminated string "text" in their place in in "buf"
326 void BufReplace(textBuffer
*buf
, int start
, int end
, const char *text
)
329 int nInserted
= strlen(text
);
331 callPreDeleteCBs(buf
, start
, end
-start
);
332 deletedText
= BufGetRange(buf
, start
, end
);
333 delete(buf
, start
, end
);
334 insert(buf
, start
, text
);
335 buf
->cursorPosHint
= start
+ nInserted
;
336 callModifyCBs(buf
, start
, end
-start
, nInserted
, 0, deletedText
);
340 void BufRemove(textBuffer
*buf
, int start
, int end
)
344 /* Make sure the arguments make sense */
350 if (start
> buf
->length
) start
= buf
->length
;
351 if (start
< 0) start
= 0;
352 if (end
> buf
->length
) end
= buf
->length
;
353 if (end
< 0) end
= 0;
355 callPreDeleteCBs(buf
, start
, end
-start
);
356 /* Remove and redisplay */
357 deletedText
= BufGetRange(buf
, start
, end
);
358 delete(buf
, start
, end
);
359 buf
->cursorPosHint
= start
;
360 callModifyCBs(buf
, start
, end
-start
, 0, 0, deletedText
);
364 void BufCopyFromBuf(textBuffer
*fromBuf
, textBuffer
*toBuf
, int fromStart
,
365 int fromEnd
, int toPos
)
367 int length
= fromEnd
- fromStart
;
370 /* Prepare the buffer to receive the new text. If the new text fits in
371 the current buffer, just move the gap (if necessary) to where
372 the text should be inserted. If the new text is too large, reallocate
373 the buffer with a gap large enough to accomodate the new text and a
374 gap of PREFERRED_GAP_SIZE */
375 if (length
> toBuf
->gapEnd
- toBuf
->gapStart
)
376 reallocateBuf(toBuf
, toPos
, length
+ PREFERRED_GAP_SIZE
);
377 else if (toPos
!= toBuf
->gapStart
)
378 moveGap(toBuf
, toPos
);
380 /* Insert the new text (toPos now corresponds to the start of the gap) */
381 if (fromEnd
<= fromBuf
->gapStart
) {
382 memcpy(&toBuf
->buf
[toPos
], &fromBuf
->buf
[fromStart
], length
);
383 } else if (fromStart
>= fromBuf
->gapStart
) {
384 memcpy(&toBuf
->buf
[toPos
],
385 &fromBuf
->buf
[fromStart
+(fromBuf
->gapEnd
-fromBuf
->gapStart
)],
388 part1Length
= fromBuf
->gapStart
- fromStart
;
389 memcpy(&toBuf
->buf
[toPos
], &fromBuf
->buf
[fromStart
], part1Length
);
390 memcpy(&toBuf
->buf
[toPos
+part1Length
], &fromBuf
->buf
[fromBuf
->gapEnd
],
393 toBuf
->gapStart
+= length
;
394 toBuf
->length
+= length
;
395 updateSelections(toBuf
, toPos
, 0, length
);
399 ** Insert "text" columnwise into buffer starting at displayed character
400 ** position "column" on the line beginning at "startPos". Opens a rectangular
401 ** space the width and height of "text", by moving all text to the right of
402 ** "column" right. If charsInserted and charsDeleted are not NULL, the
403 ** number of characters inserted and deleted in the operation (beginning
404 ** at startPos) are returned in these arguments
406 void BufInsertCol(textBuffer
*buf
, int column
, int startPos
, const char *text
,
407 int *charsInserted
, int *charsDeleted
)
409 int nLines
, lineStartPos
, nDeleted
, insertDeleted
, nInserted
;
412 nLines
= countLines(text
);
413 lineStartPos
= BufStartOfLine(buf
, startPos
);
414 nDeleted
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, startPos
, nLines
)) -
416 callPreDeleteCBs(buf
, lineStartPos
, nDeleted
);
417 deletedText
= BufGetRange(buf
, lineStartPos
, lineStartPos
+ nDeleted
);
418 insertCol(buf
, column
, lineStartPos
, text
, &insertDeleted
, &nInserted
,
419 &buf
->cursorPosHint
);
420 if (nDeleted
!= insertDeleted
)
421 fprintf(stderr
, "NEdit internal consistency check ins1 failed");
422 callModifyCBs(buf
, lineStartPos
, nDeleted
, nInserted
, 0, deletedText
);
424 if (charsInserted
!= NULL
)
425 *charsInserted
= nInserted
;
426 if (charsDeleted
!= NULL
)
427 *charsDeleted
= nDeleted
;
431 ** Overlay "text" between displayed character positions "rectStart" and
432 ** "rectEnd" on the line beginning at "startPos". If charsInserted and
433 ** charsDeleted are not NULL, the number of characters inserted and deleted
434 ** in the operation (beginning at startPos) are returned in these arguments.
435 ** If rectEnd equals -1, the width of the inserted text is measured first.
437 void BufOverlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
438 int rectEnd
, const char *text
, int *charsInserted
, int *charsDeleted
)
440 int nLines
, lineStartPos
, nDeleted
, insertDeleted
, nInserted
;
443 nLines
= countLines(text
);
444 lineStartPos
= BufStartOfLine(buf
, startPos
);
446 rectEnd
= rectStart
+ textWidth(text
, buf
->tabDist
, buf
->nullSubsChar
);
447 lineStartPos
= BufStartOfLine(buf
, startPos
);
448 nDeleted
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, startPos
, nLines
)) -
450 callPreDeleteCBs(buf
, lineStartPos
, nDeleted
);
451 deletedText
= BufGetRange(buf
, lineStartPos
, lineStartPos
+ nDeleted
);
452 overlayRect(buf
, lineStartPos
, rectStart
, rectEnd
, text
, &insertDeleted
,
453 &nInserted
, &buf
->cursorPosHint
);
454 if (nDeleted
!= insertDeleted
)
455 fprintf(stderr
, "NEdit internal consistency check ovly1 failed");
456 callModifyCBs(buf
, lineStartPos
, nDeleted
, nInserted
, 0, deletedText
);
458 if (charsInserted
!= NULL
)
459 *charsInserted
= nInserted
;
460 if (charsDeleted
!= NULL
)
461 *charsDeleted
= nDeleted
;
465 ** Replace a rectangular area in buf, given by "start", "end", "rectStart",
466 ** and "rectEnd", with "text". If "text" is vertically longer than the
467 ** rectangle, add extra lines to make room for it.
469 void BufReplaceRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
470 int rectEnd
, const char *text
)
474 int i
, nInsertedLines
, nDeletedLines
, insLen
, hint
;
475 int insertDeleted
, insertInserted
, deleteInserted
;
478 /* Make sure start and end refer to complete lines, since the
479 columnar delete and insert operations will replace whole lines */
480 start
= BufStartOfLine(buf
, start
);
481 end
= BufEndOfLine(buf
, end
);
483 callPreDeleteCBs(buf
, start
, end
-start
);
485 /* If more lines will be deleted than inserted, pad the inserted text
486 with newlines to make it as long as the number of deleted lines. This
487 will indent all of the text to the right of the rectangle to the same
488 column. If more lines will be inserted than deleted, insert extra
489 lines in the buffer at the end of the rectangle to make room for the
490 additional lines in "text" */
491 nInsertedLines
= countLines(text
);
492 nDeletedLines
= BufCountLines(buf
, start
, end
);
493 if (nInsertedLines
< nDeletedLines
) {
496 insLen
= strlen(text
);
497 insText
= XtMalloc(insLen
+ nDeletedLines
- nInsertedLines
+ 1);
498 strcpy(insText
, text
);
499 insPtr
= insText
+ insLen
;
500 for (i
=0; i
<nDeletedLines
-nInsertedLines
; i
++)
503 } else if (nDeletedLines
< nInsertedLines
) {
504 linesPadded
= nInsertedLines
-nDeletedLines
;
505 for (i
=0; i
<linesPadded
; i
++)
506 insert(buf
, end
, "\n");
507 } else /* nDeletedLines == nInsertedLines */ {
510 /* Save a copy of the text which will be modified for the modify CBs */
511 deletedText
= BufGetRange(buf
, start
, end
);
513 /* Delete then insert */
514 deleteRect(buf
, start
, end
, rectStart
, rectEnd
, &deleteInserted
, &hint
);
516 insertCol(buf
, rectStart
, start
, insText
, &insertDeleted
, &insertInserted
,
517 &buf
->cursorPosHint
);
521 insertCol(buf
, rectStart
, start
, text
, &insertDeleted
, &insertInserted
,
522 &buf
->cursorPosHint
);
524 /* Figure out how many chars were inserted and call modify callbacks */
525 if (insertDeleted
!= deleteInserted
+ linesPadded
)
526 fprintf(stderr
, "NEdit: internal consistency check repl1 failed\n");
527 callModifyCBs(buf
, start
, end
-start
, insertInserted
, 0, deletedText
);
532 ** Remove a rectangular swath of characters between character positions start
533 ** and end and horizontal displayed-character offsets rectStart and rectEnd.
535 void BufRemoveRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
541 start
= BufStartOfLine(buf
, start
);
542 end
= BufEndOfLine(buf
, end
);
543 callPreDeleteCBs(buf
, start
, end
-start
);
544 deletedText
= BufGetRange(buf
, start
, end
);
545 deleteRect(buf
, start
, end
, rectStart
, rectEnd
, &nInserted
,
546 &buf
->cursorPosHint
);
547 callModifyCBs(buf
, start
, end
-start
, nInserted
, 0, deletedText
);
552 ** Clear a rectangular "hole" out of the buffer between character positions
553 ** start and end and horizontal displayed-character offsets rectStart and
556 void BufClearRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
562 nLines
= BufCountLines(buf
, start
, end
);
563 newlineString
= XtMalloc(nLines
+1);
564 for (i
=0; i
<nLines
; i
++)
565 newlineString
[i
] = '\n';
566 newlineString
[i
] = '\0';
567 BufOverlayRect(buf
, start
, rectStart
, rectEnd
, newlineString
,
569 XtFree(newlineString
);
572 char *BufGetTextInRect(textBuffer
*buf
, int start
, int end
,
573 int rectStart
, int rectEnd
)
575 int lineStart
, selLeft
, selRight
, len
;
576 char *textOut
, *textIn
, *outPtr
, *retabbedStr
;
578 start
= BufStartOfLine(buf
, start
);
579 end
= BufEndOfLine(buf
, end
);
580 textOut
= XtMalloc((end
- start
) + 1);
583 while (lineStart
<= end
) {
584 findRectSelBoundariesForCopy(buf
, lineStart
, rectStart
, rectEnd
,
585 &selLeft
, &selRight
);
586 textIn
= BufGetRange(buf
, selLeft
, selRight
);
587 len
= selRight
- selLeft
;
588 memcpy(outPtr
, textIn
, len
);
591 lineStart
= BufEndOfLine(buf
, selRight
) + 1;
594 if (outPtr
!= textOut
)
595 outPtr
--; /* don't leave trailing newline */
598 /* If necessary, realign the tabs in the selection as if the text were
599 positioned at the left margin */
600 retabbedStr
= realignTabs(textOut
, rectStart
, 0, buf
->tabDist
,
601 buf
->useTabs
, buf
->nullSubsChar
, &len
);
607 ** Get the hardware tab distance used by all displays for this buffer,
608 ** and used in computing offsets for rectangular selection operations.
610 int BufGetTabDistance(textBuffer
*buf
)
616 ** Set the hardware tab distance used by all displays for this buffer,
617 ** and used in computing offsets for rectangular selection operations.
619 void BufSetTabDistance(textBuffer
*buf
, int tabDist
)
623 /* First call the pre-delete callbacks with the previous tab setting
625 callPreDeleteCBs(buf
, 0, buf
->length
);
627 /* Change the tab setting */
628 buf
->tabDist
= tabDist
;
630 /* Force any display routines to redisplay everything (unfortunately,
631 this means copying the whole buffer contents to provide "deletedText" */
632 deletedText
= BufGetAll(buf
);
633 callModifyCBs(buf
, 0, buf
->length
, buf
->length
, 0, deletedText
);
637 void BufCheckDisplay(textBuffer
*buf
, int start
, int end
)
639 /* just to make sure colors in the selected region are up to date */
640 callModifyCBs(buf
, start
, 0, 0, end
-start
, NULL
);
643 void BufSelect(textBuffer
*buf
, int start
, int end
)
645 selection oldSelection
= buf
->primary
;
647 setSelection(&buf
->primary
, start
, end
);
648 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
651 void BufUnselect(textBuffer
*buf
)
653 selection oldSelection
= buf
->primary
;
655 buf
->primary
.selected
= False
;
656 buf
->primary
.zeroWidth
= False
;
657 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
660 void BufRectSelect(textBuffer
*buf
, int start
, int end
, int rectStart
,
663 selection oldSelection
= buf
->primary
;
665 setRectSelect(&buf
->primary
, start
, end
, rectStart
, rectEnd
);
666 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
669 int BufGetSelectionPos(textBuffer
*buf
, int *start
, int *end
,
670 int *isRect
, int *rectStart
, int *rectEnd
)
672 return getSelectionPos(&buf
->primary
, start
, end
, isRect
, rectStart
,
676 /* Same as above, but also returns TRUE for empty selections */
677 int BufGetEmptySelectionPos(textBuffer
*buf
, int *start
, int *end
,
678 int *isRect
, int *rectStart
, int *rectEnd
)
680 return getSelectionPos(&buf
->primary
, start
, end
, isRect
, rectStart
,
681 rectEnd
) || buf
->primary
.zeroWidth
;
684 char *BufGetSelectionText(textBuffer
*buf
)
686 return getSelectionText(buf
, &buf
->primary
);
689 void BufRemoveSelected(textBuffer
*buf
)
691 removeSelected(buf
, &buf
->primary
);
694 void BufReplaceSelected(textBuffer
*buf
, const char *text
)
696 replaceSelected(buf
, &buf
->primary
, text
);
699 void BufSecondarySelect(textBuffer
*buf
, int start
, int end
)
701 selection oldSelection
= buf
->secondary
;
703 setSelection(&buf
->secondary
, start
, end
);
704 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
707 void BufSecondaryUnselect(textBuffer
*buf
)
709 selection oldSelection
= buf
->secondary
;
711 buf
->secondary
.selected
= False
;
712 buf
->secondary
.zeroWidth
= False
;
713 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
716 void BufSecRectSelect(textBuffer
*buf
, int start
, int end
,
717 int rectStart
, int rectEnd
)
719 selection oldSelection
= buf
->secondary
;
721 setRectSelect(&buf
->secondary
, start
, end
, rectStart
, rectEnd
);
722 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
725 int BufGetSecSelectPos(textBuffer
*buf
, int *start
, int *end
,
726 int *isRect
, int *rectStart
, int *rectEnd
)
728 return getSelectionPos(&buf
->secondary
, start
, end
, isRect
, rectStart
,
732 char *BufGetSecSelectText(textBuffer
*buf
)
734 return getSelectionText(buf
, &buf
->secondary
);
737 void BufRemoveSecSelect(textBuffer
*buf
)
739 removeSelected(buf
, &buf
->secondary
);
742 void BufReplaceSecSelect(textBuffer
*buf
, const char *text
)
744 replaceSelected(buf
, &buf
->secondary
, text
);
747 void BufHighlight(textBuffer
*buf
, int start
, int end
)
749 selection oldSelection
= buf
->highlight
;
751 setSelection(&buf
->highlight
, start
, end
);
752 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
755 void BufUnhighlight(textBuffer
*buf
)
757 selection oldSelection
= buf
->highlight
;
759 buf
->highlight
.selected
= False
;
760 buf
->highlight
.zeroWidth
= False
;
761 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
764 void BufRectHighlight(textBuffer
*buf
, int start
, int end
,
765 int rectStart
, int rectEnd
)
767 selection oldSelection
= buf
->highlight
;
769 setRectSelect(&buf
->highlight
, start
, end
, rectStart
, rectEnd
);
770 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
773 int BufGetHighlightPos(textBuffer
*buf
, int *start
, int *end
,
774 int *isRect
, int *rectStart
, int *rectEnd
)
776 return getSelectionPos(&buf
->highlight
, start
, end
, isRect
, rectStart
,
780 char *BufGetHighlightText(textBuffer
*buf
)
782 return getSelectionText(buf
, &buf
->highlight
);
786 ** Add a callback routine to be called when the buffer is modified
788 void BufAddModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
791 bufModifyCallbackProc
*newModifyProcs
;
795 newModifyProcs
= (bufModifyCallbackProc
*)
796 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
+1));
797 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
+1));
798 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
799 newModifyProcs
[i
] = buf
->modifyProcs
[i
];
800 newCBArgs
[i
] = buf
->cbArgs
[i
];
802 if (buf
->nModifyProcs
!= 0) {
803 XtFree((char *)buf
->modifyProcs
);
804 XtFree((char *)buf
->cbArgs
);
806 newModifyProcs
[buf
->nModifyProcs
] = bufModifiedCB
;
807 newCBArgs
[buf
->nModifyProcs
] = cbArg
;
809 buf
->modifyProcs
= newModifyProcs
;
810 buf
->cbArgs
= newCBArgs
;
814 ** Similar to the above, but makes sure that the callback is called before
815 ** normal priority callbacks.
817 void BufAddHighPriorityModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
820 bufModifyCallbackProc
*newModifyProcs
;
824 newModifyProcs
= (bufModifyCallbackProc
*)
825 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
+1));
826 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
+1));
827 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
828 newModifyProcs
[i
+1] = buf
->modifyProcs
[i
];
829 newCBArgs
[i
+1] = buf
->cbArgs
[i
];
831 if (buf
->nModifyProcs
!= 0) {
832 XtFree((char *)buf
->modifyProcs
);
833 XtFree((char *)buf
->cbArgs
);
835 newModifyProcs
[0] = bufModifiedCB
;
836 newCBArgs
[0] = cbArg
;
838 buf
->modifyProcs
= newModifyProcs
;
839 buf
->cbArgs
= newCBArgs
;
842 void BufRemoveModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
845 int i
, toRemove
= -1;
846 bufModifyCallbackProc
*newModifyProcs
;
849 /* find the matching callback to remove */
850 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
851 if (buf
->modifyProcs
[i
] == bufModifiedCB
&& buf
->cbArgs
[i
] == cbArg
) {
856 if (toRemove
== -1) {
857 fprintf(stderr
, "NEdit Internal Error: Can't find modify CB to remove\n");
861 /* Allocate new lists for remaining callback procs and args (if
864 if (buf
->nModifyProcs
== 0) {
865 buf
->nModifyProcs
= 0;
866 XtFree((char *)buf
->modifyProcs
);
867 buf
->modifyProcs
= NULL
;
868 XtFree((char *)buf
->cbArgs
);
872 newModifyProcs
= (bufModifyCallbackProc
*)
873 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
));
874 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
));
876 /* copy out the remaining members and free the old lists */
877 for (i
=0; i
<toRemove
; i
++) {
878 newModifyProcs
[i
] = buf
->modifyProcs
[i
];
879 newCBArgs
[i
] = buf
->cbArgs
[i
];
881 for (; i
<buf
->nModifyProcs
; i
++) {
882 newModifyProcs
[i
] = buf
->modifyProcs
[i
+1];
883 newCBArgs
[i
] = buf
->cbArgs
[i
+1];
885 XtFree((char *)buf
->modifyProcs
);
886 XtFree((char *)buf
->cbArgs
);
887 buf
->modifyProcs
= newModifyProcs
;
888 buf
->cbArgs
= newCBArgs
;
892 ** Add a callback routine to be called before text is deleted from the buffer.
894 void BufAddPreDeleteCB(textBuffer
*buf
, bufPreDeleteCallbackProc bufPreDeleteCB
,
897 bufPreDeleteCallbackProc
*newPreDeleteProcs
;
901 newPreDeleteProcs
= (bufPreDeleteCallbackProc
*)
902 XtMalloc(sizeof(bufPreDeleteCallbackProc
*) * (buf
->nPreDeleteProcs
+1));
903 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nPreDeleteProcs
+1));
904 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++) {
905 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
];
906 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
];
908 if (buf
->nPreDeleteProcs
!= 0) {
909 XtFree((char *)buf
->preDeleteProcs
);
910 XtFree((char *)buf
->preDeleteCbArgs
);
912 newPreDeleteProcs
[buf
->nPreDeleteProcs
] = bufPreDeleteCB
;
913 newCBArgs
[buf
->nPreDeleteProcs
] = cbArg
;
914 buf
->nPreDeleteProcs
++;
915 buf
->preDeleteProcs
= newPreDeleteProcs
;
916 buf
->preDeleteCbArgs
= newCBArgs
;
919 void BufRemovePreDeleteCB(textBuffer
*buf
, bufPreDeleteCallbackProc bufPreDeleteCB
,
922 int i
, toRemove
= -1;
923 bufPreDeleteCallbackProc
*newPreDeleteProcs
;
926 /* find the matching callback to remove */
927 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++) {
928 if (buf
->preDeleteProcs
[i
] == bufPreDeleteCB
&&
929 buf
->preDeleteCbArgs
[i
] == cbArg
) {
934 if (toRemove
== -1) {
935 fprintf(stderr
, "NEdit Internal Error: Can't find pre-delete CB to remove\n");
939 /* Allocate new lists for remaining callback procs and args (if
941 buf
->nPreDeleteProcs
--;
942 if (buf
->nPreDeleteProcs
== 0) {
943 buf
->nPreDeleteProcs
= 0;
944 XtFree((char *)buf
->preDeleteProcs
);
945 buf
->preDeleteProcs
= NULL
;
946 XtFree((char *)buf
->preDeleteCbArgs
);
947 buf
->preDeleteCbArgs
= NULL
;
950 newPreDeleteProcs
= (bufPreDeleteCallbackProc
*)
951 XtMalloc(sizeof(bufPreDeleteCallbackProc
*) * (buf
->nPreDeleteProcs
));
952 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nPreDeleteProcs
));
954 /* copy out the remaining members and free the old lists */
955 for (i
=0; i
<toRemove
; i
++) {
956 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
];
957 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
];
959 for (; i
<buf
->nPreDeleteProcs
; i
++) {
960 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
+1];
961 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
+1];
963 XtFree((char *)buf
->preDeleteProcs
);
964 XtFree((char *)buf
->preDeleteCbArgs
);
965 buf
->preDeleteProcs
= newPreDeleteProcs
;
966 buf
->preDeleteCbArgs
= newCBArgs
;
970 ** Return the text from the entire line containing position "pos"
972 char *BufGetLineText(textBuffer
*buf
, int pos
)
974 return BufGetRange(buf
, BufStartOfLine(buf
, pos
), BufEndOfLine(buf
, pos
));
978 ** Find the position of the start of the line containing position "pos"
980 int BufStartOfLine(textBuffer
*buf
, int pos
)
984 if (!searchBackward(buf
, pos
, '\n', &startPos
))
991 ** Find the position of the end of the line containing position "pos"
992 ** (which is either a pointer to the newline character ending the line,
993 ** or a pointer to one character beyond the end of the buffer)
995 int BufEndOfLine(textBuffer
*buf
, int pos
)
999 if (!searchForward(buf
, pos
, '\n', &endPos
))
1000 endPos
= buf
->length
;
1005 ** Get a character from the text buffer expanded into it's screen
1006 ** representation (which may be several characters for a tab or a
1007 ** control code). Returns the number of characters written to "outStr".
1008 ** "indent" is the number of characters from the start of the line
1009 ** for figuring tabs. Output string is guranteed to be shorter or
1010 ** equal in length to MAX_EXP_CHAR_LEN
1012 int BufGetExpandedChar(textBuffer
*buf
, int pos
, int indent
, char *outStr
)
1014 return BufExpandCharacter(BufGetCharacter(buf
, pos
), indent
, outStr
,
1015 buf
->tabDist
, buf
->nullSubsChar
);
1019 ** Expand a single character from the text buffer into it's screen
1020 ** representation (which may be several characters for a tab or a
1021 ** control code). Returns the number of characters added to "outStr".
1022 ** "indent" is the number of characters from the start of the line
1023 ** for figuring tabs. Output string is guranteed to be shorter or
1024 ** equal in length to MAX_EXP_CHAR_LEN
1026 int BufExpandCharacter(char c
, int indent
, char *outStr
, int tabDist
,
1031 /* Convert tabs to spaces */
1033 nSpaces
= tabDist
- (indent
% tabDist
);
1034 for (i
=0; i
<nSpaces
; i
++)
1039 /* Convert ASCII (and EBCDIC in the __MVS__ (OS/390) case) control
1040 codes to readable character sequences */
1041 if (c
== nullSubsChar
) {
1042 sprintf(outStr
, "<nul>");
1046 if (((unsigned char)c
) <= 63) {
1047 sprintf(outStr
, "<%s>", ControlCodeTable
[(unsigned char)c
]);
1048 return strlen(outStr
);
1051 if (((unsigned char)c
) <= 31) {
1052 sprintf(outStr
, "<%s>", ControlCodeTable
[(unsigned char)c
]);
1053 return strlen(outStr
);
1054 } else if (c
== 127) {
1055 sprintf(outStr
, "<del>");
1060 /* Otherwise, just return the character */
1066 ** Return the length in displayed characters of character "c" expanded
1067 ** for display (as discussed above in BufGetExpandedChar). If the
1068 ** buffer for which the character width is being measured is doing null
1069 ** substitution, nullSubsChar should be passed as that character (or nul
1072 int BufCharWidth(char c
, int indent
, int tabDist
, char nullSubsChar
)
1074 /* Note, this code must parallel that in BufExpandCharacter */
1075 if (c
== nullSubsChar
)
1078 return tabDist
- (indent
% tabDist
);
1079 else if (((unsigned char)c
) <= 31)
1080 return strlen(ControlCodeTable
[(unsigned char)c
]) + 2;
1087 ** Count the number of displayed characters between buffer position
1088 ** "lineStartPos" and "targetPos". (displayed characters are the characters
1089 ** shown on the screen to represent characters in the buffer, where tabs and
1090 ** control characters are expanded)
1092 int BufCountDispChars(textBuffer
*buf
, int lineStartPos
, int targetPos
)
1094 int pos
, charCount
= 0;
1095 char expandedChar
[MAX_EXP_CHAR_LEN
];
1098 while (pos
< targetPos
&& pos
< buf
->length
)
1099 charCount
+= BufGetExpandedChar(buf
, pos
++, charCount
, expandedChar
);
1104 ** Count forward from buffer position "startPos" in displayed characters
1105 ** (displayed characters are the characters shown on the screen to represent
1106 ** characters in the buffer, where tabs and control characters are expanded)
1108 int BufCountForwardDispChars(textBuffer
*buf
, int lineStartPos
, int nChars
)
1110 int pos
, charCount
= 0;
1114 while (charCount
< nChars
&& pos
< buf
->length
) {
1115 c
= BufGetCharacter(buf
, pos
);
1118 charCount
+= BufCharWidth(c
, charCount
, buf
->tabDist
,buf
->nullSubsChar
);
1125 ** Count the number of newlines between startPos and endPos in buffer "buf".
1126 ** The character at position "endPos" is not counted.
1128 int BufCountLines(textBuffer
*buf
, int startPos
, int endPos
)
1130 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1134 while (pos
< buf
->gapStart
) {
1137 if (buf
->buf
[pos
++] == '\n')
1140 while (pos
< buf
->length
) {
1143 if (buf
->buf
[pos
++ + gapLen
] == '\n')
1150 ** Find the first character of the line "nLines" forward from "startPos"
1151 ** in "buf" and return its position
1153 int BufCountForwardNLines(textBuffer
*buf
, int startPos
, int nLines
)
1155 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1162 while (pos
< buf
->gapStart
) {
1163 if (buf
->buf
[pos
++] == '\n') {
1165 if (lineCount
== nLines
)
1169 while (pos
< buf
->length
) {
1170 if (buf
->buf
[pos
++ + gapLen
] == '\n') {
1172 if (lineCount
>= nLines
)
1180 ** Find the position of the first character of the line "nLines" backwards
1181 ** from "startPos" (not counting the character pointed to by "startpos" if
1182 ** that is a newline) in "buf". nLines == 0 means find the beginning of
1185 int BufCountBackwardNLines(textBuffer
*buf
, int startPos
, int nLines
)
1187 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1194 while (pos
>= buf
->gapStart
) {
1195 if (buf
->buf
[pos
+ gapLen
] == '\n') {
1196 if (++lineCount
>= nLines
)
1202 if (buf
->buf
[pos
] == '\n') {
1203 if (++lineCount
>= nLines
)
1212 ** Search forwards in buffer "buf" for characters in "searchChars", starting
1213 ** with the character "startPos", and returning the result in "foundPos"
1214 ** returns True if found, False if not.
1216 int BufSearchForward(textBuffer
*buf
, int startPos
, const char *searchChars
,
1219 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1223 while (pos
< buf
->gapStart
) {
1224 for (c
=searchChars
; *c
!='\0'; c
++) {
1225 if (buf
->buf
[pos
] == *c
) {
1232 while (pos
< buf
->length
) {
1233 for (c
=searchChars
; *c
!='\0'; c
++) {
1234 if (buf
->buf
[pos
+ gapLen
] == *c
) {
1241 *foundPos
= buf
->length
;
1246 ** Search backwards in buffer "buf" for characters in "searchChars", starting
1247 ** with the character BEFORE "startPos", returning the result in "foundPos"
1248 ** returns True if found, False if not.
1250 int BufSearchBackward(textBuffer
*buf
, int startPos
, const char *searchChars
,
1253 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1256 if (startPos
== 0) {
1260 pos
= startPos
== 0 ? 0 : startPos
- 1;
1261 while (pos
>= buf
->gapStart
) {
1262 for (c
=searchChars
; *c
!='\0'; c
++) {
1263 if (buf
->buf
[pos
+ gapLen
] == *c
) {
1271 for (c
=searchChars
; *c
!='\0'; c
++) {
1272 if (buf
->buf
[pos
] == *c
) {
1284 ** A horrible design flaw in NEdit (from the very start, before we knew that
1285 ** NEdit would become so popular), is that it uses C NULL terminated strings
1286 ** to hold text. This means editing text containing NUL characters is not
1287 ** possible without special consideration. Here is the special consideration.
1288 ** The routines below maintain a special substitution-character which stands
1289 ** in for a null, and translates strings an buffers back and forth from/to
1290 ** the substituted form, figure out what to substitute, and figure out
1291 ** when we're in over our heads and no translation is possible.
1295 ** The primary routine for integrating new text into a text buffer with
1296 ** substitution of another character for ascii nuls. This substitutes null
1297 ** characters in the string in preparation for being copied or replaced
1298 ** into the buffer, and if neccessary, adjusts the buffer as well, in the
1299 ** event that the string contains the character it is currently using for
1300 ** substitution. Returns False, if substitution is no longer possible
1301 ** because all non-printable characters are already in use.
1303 int BufSubstituteNullChars(char *string
, int length
, textBuffer
*buf
)
1305 char histogram
[256];
1307 /* Find out what characters the string contains */
1308 histogramCharacters(string
, length
, histogram
, True
);
1310 /* Does the string contain the null-substitute character? If so, re-
1311 histogram the buffer text to find a character which is ok in both the
1312 string and the buffer, and change the buffer's null-substitution
1313 character. If none can be found, give up and return False */
1314 if (histogram
[(unsigned char)buf
->nullSubsChar
] != 0) {
1315 char *bufString
, newSubsChar
;
1316 bufString
= BufGetAll(buf
);
1317 histogramCharacters(bufString
, buf
->length
, histogram
, False
);
1318 newSubsChar
= chooseNullSubsChar(histogram
);
1319 if (newSubsChar
== '\0') {
1323 subsChars(bufString
, buf
->length
, buf
->nullSubsChar
, newSubsChar
);
1324 delete(buf
, 0, buf
->length
);
1325 insert(buf
, 0, bufString
);
1327 buf
->nullSubsChar
= newSubsChar
;
1330 /* If the string contains null characters, substitute them with the
1331 buffer's null substitution character */
1332 if (histogram
[0] != 0)
1333 subsChars(string
, length
, '\0', buf
->nullSubsChar
);
1338 ** Convert strings obtained from buffers which contain null characters, which
1339 ** have been substituted for by a special substitution character, back to
1340 ** a null-containing string. There is no time penalty for calling this
1341 ** routine if no substitution has been done.
1343 void BufUnsubstituteNullChars(char *string
, textBuffer
*buf
)
1345 register char *c
, subsChar
= buf
->nullSubsChar
;
1347 if (subsChar
== '\0')
1349 for (c
=string
; *c
!= '\0'; c
++)
1355 ** Compares len Bytes contained in buf starting at Position pos with
1356 ** the contens of cmpText. Returns 0 if there are no differences,
1360 int BufCmp(textBuffer
* buf
, int pos
, int len
, const char *cmpText
)
1367 if (posEnd
> buf
->length
) {
1374 if (posEnd
<= buf
->gapStart
) {
1375 return (strncmp(&(buf
->buf
[pos
]), cmpText
, len
));
1376 } else if (pos
>= buf
->gapStart
) {
1377 return (strncmp (&buf
->buf
[pos
+ (buf
->gapEnd
- buf
->gapStart
)],
1380 part1Length
= buf
->gapStart
- pos
;
1381 result
= strncmp(&buf
->buf
[pos
], cmpText
, part1Length
);
1385 return (strncmp(&buf
->buf
[buf
->gapEnd
], &cmpText
[part1Length
],
1386 len
- part1Length
));
1391 ** Create a pseudo-histogram of the characters in a string (don't actually
1392 ** count, because we don't want overflow, just mark the character's presence
1393 ** with a 1). If init is true, initialize the histogram before acumulating.
1394 ** if not, add the new data to an existing histogram.
1396 static void histogramCharacters(const char *string
, int length
, char hist
[256],
1403 for (i
=0; i
<256; i
++)
1405 for (c
=string
; c
< &string
[length
]; c
++)
1406 hist
[*((unsigned char *)c
)] |= 1;
1410 ** Substitute fromChar with toChar in string.
1412 static void subsChars(char *string
, int length
, char fromChar
, char toChar
)
1416 for (c
=string
; c
< &string
[length
]; c
++)
1417 if (*c
== fromChar
) *c
= toChar
;
1421 ** Search through ascii control characters in histogram in order of least
1422 ** likelihood of use, find an unused character to use as a stand-in for a
1423 ** null. If the character set is full (no available characters outside of
1424 ** the printable set, return the null character.
1426 static char chooseNullSubsChar(char hist
[256])
1428 #define N_REPLACEMENTS 25
1429 static char replacements
[N_REPLACEMENTS
] = {1,2,3,4,5,6,14,15,16,17,18,19,
1430 20,21,22,23,24,25,26,28,29,30,31,11,7};
1432 for (i
= 0; i
< N_REPLACEMENTS
; i
++)
1433 if (hist
[(unsigned char)replacements
[i
]] == 0)
1434 return replacements
[i
];
1439 ** Internal (non-redisplaying) version of BufInsert. Returns the length of
1440 ** text inserted (this is just strlen(text), however this calculation can be
1441 ** expensive and the length will be required by any caller who will continue
1442 ** on to call redisplay). pos must be contiguous with the existing text in
1443 ** the buffer (i.e. not past the end).
1445 static int insert(textBuffer
*buf
, int pos
, const char *text
)
1447 int length
= strlen(text
);
1449 /* Prepare the buffer to receive the new text. If the new text fits in
1450 the current buffer, just move the gap (if necessary) to where
1451 the text should be inserted. If the new text is too large, reallocate
1452 the buffer with a gap large enough to accomodate the new text and a
1453 gap of PREFERRED_GAP_SIZE */
1454 if (length
> buf
->gapEnd
- buf
->gapStart
)
1455 reallocateBuf(buf
, pos
, length
+ PREFERRED_GAP_SIZE
);
1456 else if (pos
!= buf
->gapStart
)
1459 /* Insert the new text (pos now corresponds to the start of the gap) */
1460 memcpy(&buf
->buf
[pos
], text
, length
);
1461 buf
->gapStart
+= length
;
1462 buf
->length
+= length
;
1463 updateSelections(buf
, pos
, 0, length
);
1469 ** Internal (non-redisplaying) version of BufRemove. Removes the contents
1470 ** of the buffer between start and end (and moves the gap to the site of
1473 static void delete(textBuffer
*buf
, int start
, int end
)
1475 /* if the gap is not contiguous to the area to remove, move it there */
1476 if (start
> buf
->gapStart
)
1477 moveGap(buf
, start
);
1478 else if (end
< buf
->gapStart
)
1481 /* expand the gap to encompass the deleted characters */
1482 buf
->gapEnd
+= end
- buf
->gapStart
;
1483 buf
->gapStart
-= buf
->gapStart
- start
;
1485 /* update the length */
1486 buf
->length
-= end
- start
;
1488 /* fix up any selections which might be affected by the change */
1489 updateSelections(buf
, start
, end
-start
, 0);
1493 ** Insert a column of text without calling the modify callbacks. Note that
1494 ** in some pathological cases, inserting can actually decrease the size of
1495 ** the buffer because of spaces being coalesced into tabs. "nDeleted" and
1496 ** "nInserted" return the number of characters deleted and inserted beginning
1497 ** at the start of the line containing "startPos". "endPos" returns buffer
1498 ** position of the lower left edge of the inserted column (as a hint for
1499 ** routines which need to set a cursor position).
1501 static void insertCol(textBuffer
*buf
, int column
, int startPos
,
1502 const char *insText
, int *nDeleted
, int *nInserted
, int *endPos
)
1504 int nLines
, start
, end
, insWidth
, lineStart
, lineEnd
;
1505 int expReplLen
, expInsLen
, len
, endOffset
;
1506 char *outStr
, *outPtr
, *line
, *replText
, *expText
, *insLine
;
1512 /* Allocate a buffer for the replacement string large enough to hold
1513 possibly expanded tabs in both the inserted text and the replaced
1514 area, as well as per line: 1) an additional 2*MAX_EXP_CHAR_LEN
1515 characters for padding where tabs and control characters cross the
1516 column of the selection, 2) up to "column" additional spaces per
1517 line for padding out to the position of "column", 3) padding up
1518 to the width of the inserted text if that must be padded to align
1519 the text beyond the inserted column. (Space for additional
1520 newlines if the inserted text extends beyond the end of the buffer
1521 is counted with the length of insText) */
1522 start
= BufStartOfLine(buf
, startPos
);
1523 nLines
= countLines(insText
) + 1;
1524 insWidth
= textWidth(insText
, buf
->tabDist
, buf
->nullSubsChar
);
1525 end
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, start
, nLines
-1));
1526 replText
= BufGetRange(buf
, start
, end
);
1527 expText
= expandTabs(replText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1531 expText
= expandTabs(insText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1534 outStr
= XtMalloc(expReplLen
+ expInsLen
+
1535 nLines
* (column
+ insWidth
+ MAX_EXP_CHAR_LEN
) + 1);
1537 /* Loop over all lines in the buffer between start and end inserting
1538 text at column, splitting tabs and adding padding appropriately */
1543 lineEnd
= BufEndOfLine(buf
, lineStart
);
1544 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1545 insLine
= copyLine(insPtr
, &len
);
1547 insertColInLine(line
, insLine
, column
, insWidth
, buf
->tabDist
,
1548 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1551 #if 0 /* Earlier comments claimed that trailing whitespace could multiply on
1552 the ends of lines, but insertColInLine looks like it should never
1553 add space unnecessarily, and this trimming interfered with
1554 paragraph filling, so lets see if it works without it. MWE */
1557 for (c
=outPtr
+len
-1; c
>outPtr
&& (*c
== ' ' || *c
== '\t'); c
--)
1563 lineStart
= lineEnd
< buf
->length
? lineEnd
+ 1 : buf
->length
;
1564 if (*insPtr
== '\0')
1568 if (outPtr
!= outStr
)
1569 outPtr
--; /* trim back off extra newline */
1572 /* replace the text between start and end with the new stuff */
1573 delete(buf
, start
, end
);
1574 insert(buf
, start
, outStr
);
1575 *nInserted
= outPtr
- outStr
;
1576 *nDeleted
= end
- start
;
1577 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1582 ** Delete a rectangle of text without calling the modify callbacks. Returns
1583 ** the number of characters replacing those between start and end. Note that
1584 ** in some pathological cases, deleting can actually increase the size of
1585 ** the buffer because of tab expansions. "endPos" returns the buffer position
1586 ** of the point in the last line where the text was removed (as a hint for
1587 ** routines which need to position the cursor after a delete operation)
1589 static void deleteRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
1590 int rectEnd
, int *replaceLen
, int *endPos
)
1592 int nLines
, lineStart
, lineEnd
, len
, endOffset
;
1593 char *outStr
, *outPtr
, *line
, *text
, *expText
;
1595 /* allocate a buffer for the replacement string large enough to hold
1596 possibly expanded tabs as well as an additional MAX_EXP_CHAR_LEN * 2
1597 characters per line for padding where tabs and control characters cross
1598 the edges of the selection */
1599 start
= BufStartOfLine(buf
, start
);
1600 end
= BufEndOfLine(buf
, end
);
1601 nLines
= BufCountLines(buf
, start
, end
) + 1;
1602 text
= BufGetRange(buf
, start
, end
);
1603 expText
= expandTabs(text
, 0, buf
->tabDist
, buf
->nullSubsChar
, &len
);
1606 outStr
= XtMalloc(len
+ nLines
* MAX_EXP_CHAR_LEN
* 2 + 1);
1608 /* loop over all lines in the buffer between start and end removing
1609 the text between rectStart and rectEnd and padding appropriately */
1612 while (lineStart
<= buf
->length
&& lineStart
<= end
) {
1613 lineEnd
= BufEndOfLine(buf
, lineStart
);
1614 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1615 deleteRectFromLine(line
, rectStart
, rectEnd
, buf
->tabDist
,
1616 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1620 lineStart
= lineEnd
+ 1;
1622 if (outPtr
!= outStr
)
1623 outPtr
--; /* trim back off extra newline */
1626 /* replace the text between start and end with the newly created string */
1627 delete(buf
, start
, end
);
1628 insert(buf
, start
, outStr
);
1629 *replaceLen
= outPtr
- outStr
;
1630 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1635 ** Overlay a rectangular area of text without calling the modify callbacks.
1636 ** "nDeleted" and "nInserted" return the number of characters deleted and
1637 ** inserted beginning at the start of the line containing "startPos".
1638 ** "endPos" returns buffer position of the lower left edge of the inserted
1639 ** column (as a hint for routines which need to set a cursor position).
1641 static void overlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
1642 int rectEnd
, const char *insText
,
1643 int *nDeleted
, int *nInserted
, int *endPos
)
1645 int nLines
, start
, end
, lineStart
, lineEnd
;
1646 int expInsLen
, len
, endOffset
;
1647 char *c
, *outStr
, *outPtr
, *line
, *expText
, *insLine
;
1650 /* Allocate a buffer for the replacement string large enough to hold
1651 possibly expanded tabs in the inserted text, as well as per line: 1)
1652 an additional 2*MAX_EXP_CHAR_LEN characters for padding where tabs
1653 and control characters cross the column of the selection, 2) up to
1654 "column" additional spaces per line for padding out to the position
1655 of "column", 3) padding up to the width of the inserted text if that
1656 must be padded to align the text beyond the inserted column. (Space
1657 for additional newlines if the inserted text extends beyond the end
1658 of the buffer is counted with the length of insText) */
1659 start
= BufStartOfLine(buf
, startPos
);
1660 nLines
= countLines(insText
) + 1;
1661 end
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, start
, nLines
-1));
1662 expText
= expandTabs(insText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1665 outStr
= XtMalloc(end
-start
+ expInsLen
+
1666 nLines
* (rectEnd
+ MAX_EXP_CHAR_LEN
) + 1);
1668 /* Loop over all lines in the buffer between start and end overlaying the
1669 text between rectStart and rectEnd and padding appropriately. Trim
1670 trailing space from line (whitespace at the ends of lines otherwise
1671 tends to multiply, since additional padding is added to maintain it */
1676 lineEnd
= BufEndOfLine(buf
, lineStart
);
1677 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1678 insLine
= copyLine(insPtr
, &len
);
1680 overlayRectInLine(line
, insLine
, rectStart
, rectEnd
, buf
->tabDist
,
1681 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1684 for (c
=outPtr
+len
-1; c
>outPtr
&& (*c
== ' ' || *c
== '\t'); c
--)
1688 lineStart
= lineEnd
< buf
->length
? lineEnd
+ 1 : buf
->length
;
1689 if (*insPtr
== '\0')
1693 if (outPtr
!= outStr
)
1694 outPtr
--; /* trim back off extra newline */
1697 /* replace the text between start and end with the new stuff */
1698 delete(buf
, start
, end
);
1699 insert(buf
, start
, outStr
);
1700 *nInserted
= outPtr
- outStr
;
1701 *nDeleted
= end
- start
;
1702 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1707 ** Insert characters from single-line string "insLine" in single-line string
1708 ** "line" at "column", leaving "insWidth" space before continuing line.
1709 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1710 ** returns the number of characters from the beginning of the string to
1711 ** the right edge of the inserted text (as a hint for routines which need
1712 ** to position the cursor).
1714 static void insertColInLine(const char *line
, const char *insLine
,
1715 int column
, int insWidth
, int tabDist
, int useTabs
, char nullSubsChar
,
1716 char *outStr
, int *outLen
, int *endOffset
)
1718 char *c
, *outPtr
, *retabbedStr
;
1719 const char *linePtr
;
1720 int indent
, toIndent
, len
, postColIndent
;
1722 /* copy the line up to "column" */
1725 for (linePtr
=line
; *linePtr
!='\0'; linePtr
++) {
1726 len
= BufCharWidth(*linePtr
, indent
, tabDist
, nullSubsChar
);
1727 if (indent
+ len
> column
)
1730 *outPtr
++ = *linePtr
;
1733 /* If "column" falls in the middle of a character, and the character is a
1734 tab, leave it off and leave the indent short and it will get padded
1735 later. If it's a control character, insert it and adjust indent
1737 if (indent
< column
&& *linePtr
!= '\0') {
1738 postColIndent
= indent
+ len
;
1739 if (*linePtr
== '\t')
1742 *outPtr
++ = *linePtr
++;
1746 postColIndent
= indent
;
1748 /* If there's no text after the column and no text to insert, that's all */
1749 if (*insLine
== '\0' && *linePtr
== '\0') {
1750 *outLen
= *endOffset
= outPtr
- outStr
;
1754 /* pad out to column if text is too short */
1755 if (indent
< column
) {
1756 addPadding(outPtr
, indent
, column
, tabDist
, useTabs
, nullSubsChar
,&len
);
1761 /* Copy the text from "insLine" (if any), recalculating the tabs as if
1762 the inserted string began at column 0 to its new column destination */
1763 if (*insLine
!= '\0') {
1764 retabbedStr
= realignTabs(insLine
, 0, indent
, tabDist
, useTabs
,
1765 nullSubsChar
, &len
);
1766 for (c
=retabbedStr
; *c
!='\0'; c
++) {
1768 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1771 XtFree(retabbedStr
);
1774 /* If the original line did not extend past "column", that's all */
1775 if (*linePtr
== '\0') {
1776 *outLen
= *endOffset
= outPtr
- outStr
;
1780 /* Pad out to column + width of inserted text + (additional original
1781 offset due to non-breaking character at column) */
1782 toIndent
= column
+ insWidth
+ postColIndent
-column
;
1783 addPadding(outPtr
, indent
, toIndent
, tabDist
, useTabs
, nullSubsChar
, &len
);
1787 /* realign tabs for text beyond "column" and write it out */
1788 retabbedStr
= realignTabs(linePtr
, postColIndent
, indent
, tabDist
,
1789 useTabs
, nullSubsChar
, &len
);
1790 strcpy(outPtr
, retabbedStr
);
1791 XtFree(retabbedStr
);
1792 *endOffset
= outPtr
- outStr
;
1793 *outLen
= (outPtr
- outStr
) + len
;
1797 ** Remove characters in single-line string "line" between displayed positions
1798 ** "rectStart" and "rectEnd", and write the result to "outStr", which is
1799 ** assumed to be large enough to hold the returned string. Note that in
1800 ** certain cases, it is possible for the string to get longer due to
1801 ** expansion of tabs. "endOffset" returns the number of characters from
1802 ** the beginning of the string to the point where the characters were
1803 ** deleted (as a hint for routines which need to position the cursor).
1805 static void deleteRectFromLine(const char *line
, int rectStart
, int rectEnd
,
1806 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
1809 int indent
, preRectIndent
, postRectIndent
, len
;
1814 /* copy the line up to rectStart */
1817 for (c
=line
; *c
!='\0'; c
++) {
1818 if (indent
> rectStart
)
1820 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1821 if (indent
+ len
> rectStart
&& (indent
== rectStart
|| *c
== '\t'))
1826 preRectIndent
= indent
;
1828 /* skip the characters between rectStart and rectEnd */
1829 for(; *c
!='\0' && indent
<rectEnd
; c
++)
1830 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1831 postRectIndent
= indent
;
1833 /* If the line ended before rectEnd, there's nothing more to do */
1836 *outLen
= *endOffset
= outPtr
- outStr
;
1840 /* fill in any space left by removed tabs or control characters
1841 which straddled the boundaries */
1842 indent
= max(rectStart
+ postRectIndent
-rectEnd
, preRectIndent
);
1843 addPadding(outPtr
, preRectIndent
, indent
, tabDist
, useTabs
, nullSubsChar
,
1847 /* Copy the rest of the line. If the indentation has changed, preserve
1848 the position of non-whitespace characters by converting tabs to
1849 spaces, then back to tabs with the correct offset */
1850 retabbedStr
= realignTabs(c
, postRectIndent
, indent
, tabDist
, useTabs
,
1851 nullSubsChar
, &len
);
1852 strcpy(outPtr
, retabbedStr
);
1853 XtFree(retabbedStr
);
1854 *endOffset
= outPtr
- outStr
;
1855 *outLen
= (outPtr
- outStr
) + len
;
1859 ** Overlay characters from single-line string "insLine" on single-line string
1860 ** "line" between displayed character offsets "rectStart" and "rectEnd".
1861 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1862 ** returns the number of characters from the beginning of the string to
1863 ** the right edge of the inserted text (as a hint for routines which need
1864 ** to position the cursor).
1866 ** This code does not handle control characters very well, but oh well.
1868 static void overlayRectInLine(const char *line
, const char *insLine
,
1869 int rectStart
, int rectEnd
, int tabDist
, int useTabs
,
1870 char nullSubsChar
, char *outStr
, int *outLen
, int *endOffset
)
1872 char *c
, *outPtr
, *retabbedStr
;
1873 const char *linePtr
;
1874 int inIndent
, outIndent
, len
, postRectIndent
;
1876 /* copy the line up to "rectStart" or just before the char that
1879 inIndent
= outIndent
= 0;
1880 for (linePtr
=line
; *linePtr
!='\0'; linePtr
++) {
1881 len
= BufCharWidth(*linePtr
, inIndent
, tabDist
, nullSubsChar
);
1882 if (inIndent
+ len
> rectStart
)
1886 *outPtr
++ = *linePtr
;
1889 /* If "rectStart" falls in the middle of a character, and the character
1890 is a tab, leave it off and leave the outIndent short and it will get
1891 padded later. If it's a control character, insert it and adjust
1892 outIndent accordingly. */
1893 if (inIndent
< rectStart
&& *linePtr
!= '\0') {
1894 if (*linePtr
== '\t') {
1895 /* Skip past the tab */
1899 *outPtr
++ = *linePtr
++;
1905 /* skip the characters between rectStart and rectEnd */
1906 for(; *linePtr
!='\0' && inIndent
< rectEnd
; linePtr
++)
1907 inIndent
+= BufCharWidth(*linePtr
, inIndent
, tabDist
, nullSubsChar
);
1908 postRectIndent
= inIndent
;
1910 /* After this inIndent is dead and linePtr is supposed to point at the
1911 character just past the last character that will be altered by
1912 the overlay, whether that's a \t or otherwise. postRectIndent is
1913 the position at which that character is supposed to appear */
1915 /* If there's no text after rectStart and no text to insert, that's all */
1916 if (*insLine
== '\0' && *linePtr
== '\0') {
1917 *outLen
= *endOffset
= outPtr
- outStr
;
1921 /* pad out to rectStart if text is too short */
1922 if (outIndent
< rectStart
) {
1923 addPadding(outPtr
, outIndent
, rectStart
, tabDist
, useTabs
, nullSubsChar
,
1927 outIndent
= rectStart
;
1929 /* Copy the text from "insLine" (if any), recalculating the tabs as if
1930 the inserted string began at column 0 to its new column destination */
1931 if (*insLine
!= '\0') {
1932 retabbedStr
= realignTabs(insLine
, 0, rectStart
, tabDist
, useTabs
,
1933 nullSubsChar
, &len
);
1934 for (c
=retabbedStr
; *c
!='\0'; c
++) {
1936 len
= BufCharWidth(*c
, outIndent
, tabDist
, nullSubsChar
);
1939 XtFree(retabbedStr
);
1942 /* If the original line did not extend past "rectStart", that's all */
1943 if (*linePtr
== '\0') {
1944 *outLen
= *endOffset
= outPtr
- outStr
;
1948 /* Pad out to rectEnd + (additional original offset
1949 due to non-breaking character at right boundary) */
1950 addPadding(outPtr
, outIndent
, postRectIndent
, tabDist
, useTabs
,
1951 nullSubsChar
, &len
);
1953 outIndent
= postRectIndent
;
1955 /* copy the text beyond "rectEnd" */
1956 strcpy(outPtr
, linePtr
);
1957 *endOffset
= outPtr
- outStr
;
1958 *outLen
= (outPtr
- outStr
) + strlen(linePtr
);
1961 static void setSelection(selection
*sel
, int start
, int end
)
1963 sel
->selected
= start
!= end
;
1964 sel
->zeroWidth
= (start
== end
) ? 1 : 0;
1965 sel
->rectangular
= False
;
1966 sel
->start
= min(start
, end
);
1967 sel
->end
= max(start
, end
);
1970 static void setRectSelect(selection
*sel
, int start
, int end
,
1971 int rectStart
, int rectEnd
)
1973 sel
->selected
= rectStart
< rectEnd
;
1974 sel
->zeroWidth
= (rectStart
== rectEnd
) ? 1 : 0;
1975 sel
->rectangular
= True
;
1978 sel
->rectStart
= rectStart
;
1979 sel
->rectEnd
= rectEnd
;
1982 static int getSelectionPos(selection
*sel
, int *start
, int *end
,
1983 int *isRect
, int *rectStart
, int *rectEnd
)
1985 /* Always fill in the parameters (zero-width can be requested too). */
1986 *isRect
= sel
->rectangular
;
1987 *start
= sel
->start
;
1989 if (sel
->rectangular
) {
1990 *rectStart
= sel
->rectStart
;
1991 *rectEnd
= sel
->rectEnd
;
1993 return sel
->selected
;
1996 static char *getSelectionText(textBuffer
*buf
, selection
*sel
)
1998 int start
, end
, isRect
, rectStart
, rectEnd
;
2001 /* If there's no selection, return an allocated empty string */
2002 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
)) {
2008 /* If the selection is not rectangular, return the selected range */
2010 return BufGetTextInRect(buf
, start
, end
, rectStart
, rectEnd
);
2012 return BufGetRange(buf
, start
, end
);
2015 static void removeSelected(textBuffer
*buf
, selection
*sel
)
2018 int isRect
, rectStart
, rectEnd
;
2020 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
))
2023 BufRemoveRect(buf
, start
, end
, rectStart
, rectEnd
);
2025 BufRemove(buf
, start
, end
);
2028 static void replaceSelected(textBuffer
*buf
, selection
*sel
, const char *text
)
2030 int start
, end
, isRect
, rectStart
, rectEnd
;
2031 selection oldSelection
= *sel
;
2033 /* If there's no selection, return */
2034 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
))
2037 /* Do the appropriate type of replace */
2039 BufReplaceRect(buf
, start
, end
, rectStart
, rectEnd
, text
);
2041 BufReplace(buf
, start
, end
, text
);
2043 /* Unselect (happens automatically in BufReplace, but BufReplaceRect
2044 can't detect when the contents of a selection goes away) */
2045 sel
->selected
= False
;
2046 redisplaySelection(buf
, &oldSelection
, sel
);
2049 static void addPadding(char *string
, int startIndent
, int toIndent
,
2050 int tabDist
, int useTabs
, char nullSubsChar
, int *charsAdded
)
2055 indent
= startIndent
;
2058 while (indent
< toIndent
) {
2059 len
= BufCharWidth('\t', indent
, tabDist
, nullSubsChar
);
2060 if (len
> 1 && indent
+ len
<= toIndent
) {
2069 while (indent
< toIndent
) {
2074 *charsAdded
= outPtr
- string
;
2078 ** Call the stored modify callback procedure(s) for this buffer to update the
2079 ** changed area(s) on the screen and any other listeners.
2081 static void callModifyCBs(textBuffer
*buf
, int pos
, int nDeleted
,
2082 int nInserted
, int nRestyled
, char *deletedText
)
2086 for (i
=0; i
<buf
->nModifyProcs
; i
++)
2087 (*buf
->modifyProcs
[i
])(pos
, nInserted
, nDeleted
, nRestyled
,
2088 deletedText
, buf
->cbArgs
[i
]);
2092 ** Call the stored pre-delete callback procedure(s) for this buffer to update
2093 ** the changed area(s) on the screen and any other listeners.
2095 static void callPreDeleteCBs(textBuffer
*buf
, int pos
, int nDeleted
)
2099 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++)
2100 (*buf
->preDeleteProcs
[i
])(pos
, nDeleted
, buf
->preDeleteCbArgs
[i
]);
2104 ** Call the stored redisplay procedure(s) for this buffer to update the
2105 ** screen for a change in a selection.
2107 static void redisplaySelection(textBuffer
*buf
, selection
*oldSelection
,
2108 selection
*newSelection
)
2110 int oldStart
, oldEnd
, newStart
, newEnd
, ch1Start
, ch1End
, ch2Start
, ch2End
;
2112 /* If either selection is rectangular, add an additional character to
2113 the end of the selection to request the redraw routines to wipe out
2114 the parts of the selection beyond the end of the line */
2115 oldStart
= oldSelection
->start
;
2116 newStart
= newSelection
->start
;
2117 oldEnd
= oldSelection
->end
;
2118 newEnd
= newSelection
->end
;
2119 if (oldSelection
->rectangular
)
2121 if (newSelection
->rectangular
)
2124 /* If the old or new selection is unselected, just redisplay the
2125 single area that is (was) selected and return */
2126 if (!oldSelection
->selected
&& !newSelection
->selected
)
2128 if (!oldSelection
->selected
) {
2129 callModifyCBs(buf
, newStart
, 0, 0, newEnd
-newStart
, NULL
);
2132 if (!newSelection
->selected
) {
2133 callModifyCBs(buf
, oldStart
, 0, 0, oldEnd
-oldStart
, NULL
);
2137 /* If the selection changed from normal to rectangular or visa versa, or
2138 if a rectangular selection changed boundaries, redisplay everything */
2139 if ((oldSelection
->rectangular
&& !newSelection
->rectangular
) ||
2140 (!oldSelection
->rectangular
&& newSelection
->rectangular
) ||
2141 (oldSelection
->rectangular
&& (
2142 (oldSelection
->rectStart
!= newSelection
->rectStart
) ||
2143 (oldSelection
->rectEnd
!= newSelection
->rectEnd
)))) {
2144 callModifyCBs(buf
, min(oldStart
, newStart
), 0, 0,
2145 max(oldEnd
, newEnd
) - min(oldStart
, newStart
), NULL
);
2149 /* If the selections are non-contiguous, do two separate updates
2151 if (oldEnd
< newStart
|| newEnd
< oldStart
) {
2152 callModifyCBs(buf
, oldStart
, 0, 0, oldEnd
-oldStart
, NULL
);
2153 callModifyCBs(buf
, newStart
, 0, 0, newEnd
-newStart
, NULL
);
2157 /* Otherwise, separate into 3 separate regions: ch1, and ch2 (the two
2158 changed areas), and the unchanged area of their intersection,
2159 and update only the changed area(s) */
2160 ch1Start
= min(oldStart
, newStart
);
2161 ch2End
= max(oldEnd
, newEnd
);
2162 ch1End
= max(oldStart
, newStart
);
2163 ch2Start
= min(oldEnd
, newEnd
);
2164 if (ch1Start
!= ch1End
)
2165 callModifyCBs(buf
, ch1Start
, 0, 0, ch1End
-ch1Start
, NULL
);
2166 if (ch2Start
!= ch2End
)
2167 callModifyCBs(buf
, ch2Start
, 0, 0, ch2End
-ch2Start
, NULL
);
2170 static void moveGap(textBuffer
*buf
, int pos
)
2172 int gapLen
= buf
->gapEnd
- buf
->gapStart
;
2174 if (pos
> buf
->gapStart
)
2175 memmove(&buf
->buf
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
2176 pos
- buf
->gapStart
);
2178 memmove(&buf
->buf
[pos
+ gapLen
], &buf
->buf
[pos
], buf
->gapStart
- pos
);
2179 buf
->gapEnd
+= pos
- buf
->gapStart
;
2180 buf
->gapStart
+= pos
- buf
->gapStart
;
2184 ** reallocate the text storage in "buf" to have a gap starting at "newGapStart"
2185 ** and a gap size of "newGapLen", preserving the buffer's current contents.
2187 static void reallocateBuf(textBuffer
*buf
, int newGapStart
, int newGapLen
)
2192 newBuf
= XtMalloc(buf
->length
+ newGapLen
);
2193 newGapEnd
= newGapStart
+ newGapLen
;
2194 if (newGapStart
<= buf
->gapStart
) {
2195 memcpy(newBuf
, buf
->buf
, newGapStart
);
2196 memcpy(&newBuf
[newGapEnd
], &buf
->buf
[newGapStart
],
2197 buf
->gapStart
- newGapStart
);
2198 memcpy(&newBuf
[newGapEnd
+ buf
->gapStart
- newGapStart
],
2199 &buf
->buf
[buf
->gapEnd
], buf
->length
- buf
->gapStart
);
2200 } else { /* newGapStart > buf->gapStart */
2201 memcpy(newBuf
, buf
->buf
, buf
->gapStart
);
2202 memcpy(&newBuf
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
2203 newGapStart
- buf
->gapStart
);
2204 memcpy(&newBuf
[newGapEnd
],
2205 &buf
->buf
[buf
->gapEnd
+ newGapStart
- buf
->gapStart
],
2206 buf
->length
- newGapStart
);
2210 buf
->gapStart
= newGapStart
;
2211 buf
->gapEnd
= newGapEnd
;
2213 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
2218 ** Update all of the selections in "buf" for changes in the buffer's text
2220 static void updateSelections(textBuffer
*buf
, int pos
, int nDeleted
,
2223 updateSelection(&buf
->primary
, pos
, nDeleted
, nInserted
);
2224 updateSelection(&buf
->secondary
, pos
, nDeleted
, nInserted
);
2225 updateSelection(&buf
->highlight
, pos
, nDeleted
, nInserted
);
2229 ** Update an individual selection for changes in the corresponding text
2231 static void updateSelection(selection
*sel
, int pos
, int nDeleted
,
2234 if ((!sel
->selected
&& !sel
->zeroWidth
) || pos
> sel
->end
)
2236 if (pos
+nDeleted
<= sel
->start
) {
2237 sel
->start
+= nInserted
- nDeleted
;
2238 sel
->end
+= nInserted
- nDeleted
;
2239 } else if (pos
<= sel
->start
&& pos
+nDeleted
>= sel
->end
) {
2242 sel
->selected
= False
;
2243 sel
->zeroWidth
= False
;
2244 } else if (pos
<= sel
->start
&& pos
+nDeleted
< sel
->end
) {
2246 sel
->end
= nInserted
+ sel
->end
- nDeleted
;
2247 } else if (pos
< sel
->end
) {
2248 sel
->end
+= nInserted
- nDeleted
;
2249 if (sel
->end
<= sel
->start
)
2250 sel
->selected
= False
;
2255 ** Search forwards in buffer "buf" for character "searchChar", starting
2256 ** with the character "startPos", and returning the result in "foundPos"
2257 ** returns True if found, False if not. (The difference between this and
2258 ** BufSearchForward is that it's optimized for single characters. The
2259 ** overall performance of the text widget is dependent on its ability to
2260 ** count lines quickly, hence searching for a single character: newline)
2262 static int searchForward(textBuffer
*buf
, int startPos
, char searchChar
,
2265 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
2268 while (pos
< buf
->gapStart
) {
2269 if (buf
->buf
[pos
] == searchChar
) {
2275 while (pos
< buf
->length
) {
2276 if (buf
->buf
[pos
+ gapLen
] == searchChar
) {
2282 *foundPos
= buf
->length
;
2287 ** Search backwards in buffer "buf" for character "searchChar", starting
2288 ** with the character BEFORE "startPos", returning the result in "foundPos"
2289 ** returns True if found, False if not. (The difference between this and
2290 ** BufSearchBackward is that it's optimized for single characters. The
2291 ** overall performance of the text widget is dependent on its ability to
2292 ** count lines quickly, hence searching for a single character: newline)
2294 static int searchBackward(textBuffer
*buf
, int startPos
, char searchChar
,
2297 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
2299 if (startPos
== 0) {
2303 pos
= startPos
== 0 ? 0 : startPos
- 1;
2304 while (pos
>= buf
->gapStart
) {
2305 if (buf
->buf
[pos
+ gapLen
] == searchChar
) {
2312 if (buf
->buf
[pos
] == searchChar
) {
2323 ** Copy from "text" to end up to but not including newline (or end of "text")
2324 ** and return the copy as the function value, and the length of the line in
2327 static char *copyLine(const char *text
, int *lineLen
)
2333 for (c
=text
; *c
!='\0' && *c
!='\n'; c
++)
2335 outStr
= XtMalloc(len
+ 1);
2336 strncpy(outStr
, text
, len
);
2343 ** Count the number of newlines in a null-terminated text string;
2345 static int countLines(const char *string
)
2350 for (c
=string
; *c
!='\0'; c
++)
2351 if (*c
== '\n') lineCount
++;
2356 ** Measure the width in displayed characters of string "text"
2358 static int textWidth(const char *text
, int tabDist
, char nullSubsChar
)
2360 int width
= 0, maxWidth
= 0;
2363 for (c
=text
; *c
!='\0'; c
++) {
2365 if (width
> maxWidth
)
2369 width
+= BufCharWidth(*c
, width
, tabDist
, nullSubsChar
);
2371 if (width
> maxWidth
)
2377 ** Find the first and last character position in a line withing a rectangular
2378 ** selection (for copying). Includes tabs which cross rectStart, but not
2379 ** control characters which do so. Leaves off tabs which cross rectEnd.
2381 ** Technically, the calling routine should convert tab characters which
2382 ** cross the right boundary of the selection to spaces which line up with
2383 ** the edge of the selection. Unfortunately, the additional memory
2384 ** management required in the parent routine to allow for the changes
2385 ** in string size is not worth all the extra work just for a couple of
2386 ** shifted characters, so if a tab protrudes, just lop it off and hope
2387 ** that there are other characters in the selection to establish the right
2388 ** margin for subsequent columnar pastes of this data.
2390 static void findRectSelBoundariesForCopy(textBuffer
*buf
, int lineStartPos
,
2391 int rectStart
, int rectEnd
, int *selStart
, int *selEnd
)
2393 int pos
, width
, indent
= 0;
2396 /* find the start of the selection */
2397 for (pos
=lineStartPos
; pos
<buf
->length
; pos
++) {
2398 c
= BufGetCharacter(buf
, pos
);
2401 width
= BufCharWidth(c
, indent
, buf
->tabDist
, buf
->nullSubsChar
);
2402 if (indent
+ width
> rectStart
) {
2403 if (indent
!= rectStart
&& c
!= '\t') {
2414 for (; pos
<buf
->length
; pos
++) {
2415 c
= BufGetCharacter(buf
, pos
);
2418 width
= BufCharWidth(c
, indent
, buf
->tabDist
, buf
->nullSubsChar
);
2420 if (indent
> rectEnd
) {
2421 if (indent
-width
!= rectEnd
&& c
!= '\t')
2430 ** Adjust the space and tab characters from string "text" so that non-white
2431 ** characters remain stationary when the text is shifted from starting at
2432 ** "origIndent" to starting at "newIndent". Returns an allocated string
2433 ** which must be freed by the caller with XtFree.
2435 static char *realignTabs(const char *text
, int origIndent
, int newIndent
,
2436 int tabDist
, int useTabs
, char nullSubsChar
, int *newLength
)
2438 char *expStr
, *outStr
;
2441 /* If the tabs settings are the same, retain original tabs */
2442 if (origIndent
% tabDist
== newIndent
%tabDist
) {
2444 outStr
= XtMalloc(len
+ 1);
2445 strcpy(outStr
, text
);
2450 /* If the tab settings are not the same, brutally convert tabs to
2451 spaces, then back to tabs in the new position */
2452 expStr
= expandTabs(text
, origIndent
, tabDist
, nullSubsChar
, &len
);
2457 outStr
= unexpandTabs(expStr
, newIndent
, tabDist
, nullSubsChar
, newLength
);
2463 ** Expand tabs to spaces for a block of text. The additional parameter
2464 ** "startIndent" if nonzero, indicates that the text is a rectangular selection
2465 ** beginning at column "startIndent"
2467 static char *expandTabs(const char *text
, int startIndent
, int tabDist
,
2468 char nullSubsChar
, int *newLen
)
2470 char *outStr
, *outPtr
;
2472 int indent
, len
, outLen
= 0;
2474 /* rehearse the expansion to figure out length for output string */
2475 indent
= startIndent
;
2476 for (c
=text
; *c
!='\0'; c
++) {
2478 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2481 } else if (*c
== '\n') {
2482 indent
= startIndent
;
2485 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2490 /* do the expansion */
2491 outStr
= XtMalloc(outLen
+1);
2493 indent
= startIndent
;
2494 for (c
=text
; *c
!= '\0'; c
++) {
2496 len
= BufExpandCharacter(*c
, indent
, outPtr
, tabDist
, nullSubsChar
);
2499 } else if (*c
== '\n') {
2500 indent
= startIndent
;
2503 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2507 outStr
[outLen
] = '\0';
2513 ** Convert sequences of spaces into tabs. The threshold for conversion is
2514 ** when 3 or more spaces can be converted into a single tab, this avoids
2515 ** converting double spaces after a period withing a block of text.
2517 static char *unexpandTabs(const char *text
, int startIndent
, int tabDist
,
2518 char nullSubsChar
, int *newLen
)
2520 char *outStr
, *outPtr
, expandedChar
[MAX_EXP_CHAR_LEN
];
2524 outStr
= XtMalloc(strlen(text
)+1);
2526 indent
= startIndent
;
2527 for (c
=text
; *c
!='\0';) {
2529 len
= BufExpandCharacter('\t', indent
, expandedChar
, tabDist
,
2531 if (len
>= 3 && !strncmp(c
, expandedChar
, len
)) {
2539 } else if (*c
== '\n') {
2540 indent
= startIndent
;
2548 *newLen
= outPtr
- outStr
;
2552 static int max(int i1
, int i2
)
2554 return i1
>= i2
? i1
: i2
;
2557 static int min(int i1
, int i2
)
2559 return i1
<= i2
? i1
: i2
;