1 static const char CVSID
[] = "$Id: textBuf.c,v 1.32 2004/07/21 11:32:05 yooden 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 *
11 * version. In addition, you may distribute version of this program linked to *
12 * Motif or Open Motif. See README for details. *
14 * This software is distributed in the hope that it will be useful, but WITHOUT *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
19 * You should have received a copy of the GNU General Public License along with *
20 * software; if not, write to the Free Software Foundation, Inc., 59 Temple *
21 * Place, Suite 330, Boston, MA 02111-1307 USA *
23 * Nirvana Text Editor *
26 * Written by Mark Edel *
28 *******************************************************************************/
31 #include "../config.h"
46 #define PREFERRED_GAP_SIZE 80 /* Initial size for the buffer gap (empty space
47 in the buffer where text might be inserted
48 if the user is typing sequential chars) */
50 static void histogramCharacters(const char *string
, int length
, char hist
[256],
52 static void subsChars(char *string
, int length
, char fromChar
, char toChar
);
53 static char chooseNullSubsChar(char hist
[256]);
54 static int insert(textBuffer
*buf
, int pos
, const char *text
);
55 static void delete(textBuffer
*buf
, int start
, int end
);
56 static void deleteRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
57 int rectEnd
, int *replaceLen
, int *endPos
);
58 static void insertCol(textBuffer
*buf
, int column
, int startPos
, const char *insText
,
59 int *nDeleted
, int *nInserted
, int *endPos
);
60 static void overlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
61 int rectEnd
, const char *insText
, int *nDeleted
, int *nInserted
, int *endPos
);
62 static void insertColInLine(const char *line
, const char *insLine
, int column
, int insWidth
,
63 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
65 static void deleteRectFromLine(const char *line
, int rectStart
, int rectEnd
,
66 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
68 static void overlayRectInLine(const char *line
, const char *insLine
, int rectStart
,
69 int rectEnd
, int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
,
70 int *outLen
, int *endOffset
);
71 static void callPreDeleteCBs(textBuffer
*buf
, int pos
, int nDeleted
);
72 static void callModifyCBs(textBuffer
*buf
, int pos
, int nDeleted
,
73 int nInserted
, int nRestyled
, char *deletedText
);
74 static void redisplaySelection(textBuffer
*buf
, selection
*oldSelection
,
75 selection
*newSelection
);
76 static void moveGap(textBuffer
*buf
, int pos
);
77 static void reallocateBuf(textBuffer
*buf
, int newGapStart
, int newGapLen
);
78 static void setSelection(selection
*sel
, int start
, int end
);
79 static void setRectSelect(selection
*sel
, int start
, int end
,
80 int rectStart
, int rectEnd
);
81 static void updateSelections(textBuffer
*buf
, int pos
, int nDeleted
,
83 static void updateSelection(selection
*sel
, int pos
, int nDeleted
,
85 static int getSelectionPos(selection
*sel
, int *start
, int *end
,
86 int *isRect
, int *rectStart
, int *rectEnd
);
87 static char *getSelectionText(textBuffer
*buf
, selection
*sel
);
88 static void removeSelected(textBuffer
*buf
, selection
*sel
);
89 static void replaceSelected(textBuffer
*buf
, selection
*sel
, const char *text
);
90 static void addPadding(char *string
, int startIndent
, int toIndent
,
91 int tabDist
, int useTabs
, char nullSubsChar
, int *charsAdded
);
92 static int searchForward(textBuffer
*buf
, int startPos
, char searchChar
,
94 static int searchBackward(textBuffer
*buf
, int startPos
, char searchChar
,
96 static char *copyLine(const char *text
, int *lineLen
);
97 static int countLines(const char *string
);
98 static int textWidth(const char *text
, int tabDist
, char nullSubsChar
);
99 static void findRectSelBoundariesForCopy(textBuffer
*buf
, int lineStartPos
,
100 int rectStart
, int rectEnd
, int *selStart
, int *selEnd
);
101 static char *realignTabs(const char *text
, int origIndent
, int newIndent
,
102 int tabDist
, int useTabs
, char nullSubsChar
, int *newLength
);
103 static char *expandTabs(const char *text
, int startIndent
, int tabDist
,
104 char nullSubsChar
, int *newLen
);
105 static char *unexpandTabs(const char *text
, int startIndent
, int tabDist
,
106 char nullSubsChar
, int *newLen
);
107 static int max(int i1
, int i2
);
108 static int min(int i1
, int i2
);
111 static const char *ControlCodeTable
[64] = {
112 "nul", "soh", "stx", "etx", "sel", "ht", "rnl", "del",
113 "ge", "sps", "rpt", "vt", "ff", "cr", "so", "si",
114 "dle", "dc1", "dc2", "dc3", "res", "nl", "bs", "poc",
115 "can", "em", "ubs", "cu1", "ifs", "igs", "irs", "ius",
116 "ds", "sos", "fs", "wus", "byp", "lf", "etb", "esc",
117 "sa", "sfe", "sm", "csp", "mfa", "enq", "ack", "bel",
118 "x30", "x31", "syn", "ir", "pp", "trn", "nbs", "eot",
119 "sbs", "it", "rff", "cu3", "dc4", "nak", "x3e", "sub"};
121 static const char *ControlCodeTable
[32] = {
122 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
123 "bs", "ht", "nl", "vt", "np", "cr", "so", "si",
124 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
125 "can", "em", "sub", "esc", "fs", "gs", "rs", "us"};
129 ** Create an empty text buffer
131 textBuffer
*BufCreate(void)
133 textBuffer
*buf
= BufCreatePreallocated(0);
138 ** Create an empty text buffer of a pre-determined size (use this to
139 ** avoid unnecessary re-allocation if you know exactly how much the buffer
142 textBuffer
*BufCreatePreallocated(int requestedSize
)
146 buf
= (textBuffer
*)XtMalloc(sizeof(textBuffer
));
148 buf
->buf
= XtMalloc(requestedSize
+ PREFERRED_GAP_SIZE
);
150 buf
->gapEnd
= PREFERRED_GAP_SIZE
;
153 buf
->primary
.selected
= False
;
154 buf
->primary
.zeroWidth
= False
;
155 buf
->primary
.rectangular
= False
;
156 buf
->primary
.start
= buf
->primary
.end
= 0;
157 buf
->secondary
.selected
= False
;
158 buf
->secondary
.zeroWidth
= False
;
159 buf
->secondary
.start
= buf
->secondary
.end
= 0;
160 buf
->secondary
.rectangular
= False
;
161 buf
->highlight
.selected
= False
;
162 buf
->highlight
.zeroWidth
= False
;
163 buf
->highlight
.start
= buf
->highlight
.end
= 0;
164 buf
->highlight
.rectangular
= False
;
165 buf
->modifyProcs
= NULL
;
167 buf
->nModifyProcs
= 0;
168 buf
->preDeleteProcs
= NULL
;
169 buf
->preDeleteCbArgs
= NULL
;
170 buf
->nPreDeleteProcs
= 0;
171 buf
->nullSubsChar
= '\0';
173 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
175 buf
->rangesetTable
= NULL
;
180 ** Free a text buffer
182 void BufFree(textBuffer
*buf
)
185 if (buf
->nModifyProcs
!= 0) {
186 XtFree((char *)buf
->modifyProcs
);
187 XtFree((char *)buf
->cbArgs
);
189 if (buf
->rangesetTable
)
190 RangesetTableFree(buf
->rangesetTable
);
191 if (buf
->nPreDeleteProcs
!= 0) {
192 XtFree((char *)buf
->preDeleteProcs
);
193 XtFree((char *)buf
->preDeleteCbArgs
);
199 ** Get the entire contents of a text buffer. Memory is allocated to contain
200 ** the returned string, which the caller must free.
202 char *BufGetAll(textBuffer
*buf
)
206 text
= XtMalloc(buf
->length
+1);
207 memcpy(text
, buf
->buf
, buf
->gapStart
);
208 memcpy(&text
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
209 buf
->length
- buf
->gapStart
);
210 text
[buf
->length
] = '\0';
215 ** Replace the entire contents of the text buffer
217 void BufSetAll(textBuffer
*buf
, const char *text
)
219 int length
, deletedLength
;
221 length
= strlen(text
);
223 callPreDeleteCBs(buf
, 0, buf
->length
);
225 /* Save information for redisplay, and get rid of the old buffer */
226 deletedText
= BufGetAll(buf
);
227 deletedLength
= buf
->length
;
230 /* Start a new buffer with a gap of PREFERRED_GAP_SIZE in the center */
231 buf
->buf
= XtMalloc(length
+ PREFERRED_GAP_SIZE
);
232 buf
->length
= length
;
233 buf
->gapStart
= length
/2;
234 buf
->gapEnd
= buf
->gapStart
+ PREFERRED_GAP_SIZE
;
235 memcpy(buf
->buf
, text
, buf
->gapStart
);
236 memcpy(&buf
->buf
[buf
->gapEnd
], &text
[buf
->gapStart
], length
-buf
->gapStart
);
238 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
241 /* Zero all of the existing selections */
242 updateSelections(buf
, 0, deletedLength
, 0);
244 /* Call the saved display routine(s) to update the screen */
245 callModifyCBs(buf
, 0, deletedLength
, length
, 0, deletedText
);
250 ** Return a copy of the text between "start" and "end" character positions
251 ** from text buffer "buf". Positions start at 0, and the range does not
252 ** include the character pointed to by "end"
254 char *BufGetRange(textBuffer
*buf
, int start
, int end
)
257 int length
, part1Length
;
259 /* Make sure start and end are ok, and allocate memory for returned string.
260 If start is bad, return "", if end is bad, adjust it. */
261 if (start
< 0 || start
> buf
->length
) {
271 if (end
> buf
->length
)
273 length
= end
- start
;
274 text
= XtMalloc(length
+1);
276 /* Copy the text from the buffer to the returned string */
277 if (end
<= buf
->gapStart
) {
278 memcpy(text
, &buf
->buf
[start
], length
);
279 } else if (start
>= buf
->gapStart
) {
280 memcpy(text
, &buf
->buf
[start
+(buf
->gapEnd
-buf
->gapStart
)], length
);
282 part1Length
= buf
->gapStart
- start
;
283 memcpy(text
, &buf
->buf
[start
], part1Length
);
284 memcpy(&text
[part1Length
], &buf
->buf
[buf
->gapEnd
], length
-part1Length
);
291 ** Return the character at buffer position "pos". Positions start at 0.
293 char BufGetCharacter(textBuffer
*buf
, int pos
)
295 if (pos
< 0 || pos
>= buf
->length
)
297 if (pos
< buf
->gapStart
)
298 return buf
->buf
[pos
];
300 return buf
->buf
[pos
+ buf
->gapEnd
-buf
->gapStart
];
304 ** Insert null-terminated string "text" at position "pos" in "buf"
306 void BufInsert(textBuffer
*buf
, int pos
, const char *text
)
310 /* if pos is not contiguous to existing text, make it */
311 if (pos
> buf
->length
) pos
= buf
->length
;
312 if (pos
< 0 ) pos
= 0;
314 /* Even if nothing is deleted, we must call these callbacks */
315 callPreDeleteCBs(buf
, pos
, 0);
317 /* insert and redisplay */
318 nInserted
= insert(buf
, pos
, text
);
319 buf
->cursorPosHint
= pos
+ nInserted
;
320 callModifyCBs(buf
, pos
, 0, nInserted
, 0, NULL
);
324 ** Delete the characters between "start" and "end", and insert the
325 ** null-terminated string "text" in their place in in "buf"
327 void BufReplace(textBuffer
*buf
, int start
, int end
, const char *text
)
330 int nInserted
= strlen(text
);
332 callPreDeleteCBs(buf
, start
, end
-start
);
333 deletedText
= BufGetRange(buf
, start
, end
);
334 delete(buf
, start
, end
);
335 insert(buf
, start
, text
);
336 buf
->cursorPosHint
= start
+ nInserted
;
337 callModifyCBs(buf
, start
, end
-start
, nInserted
, 0, deletedText
);
341 void BufRemove(textBuffer
*buf
, int start
, int end
)
345 /* Make sure the arguments make sense */
351 if (start
> buf
->length
) start
= buf
->length
;
352 if (start
< 0) start
= 0;
353 if (end
> buf
->length
) end
= buf
->length
;
354 if (end
< 0) end
= 0;
356 callPreDeleteCBs(buf
, start
, end
-start
);
357 /* Remove and redisplay */
358 deletedText
= BufGetRange(buf
, start
, end
);
359 delete(buf
, start
, end
);
360 buf
->cursorPosHint
= start
;
361 callModifyCBs(buf
, start
, end
-start
, 0, 0, deletedText
);
365 void BufCopyFromBuf(textBuffer
*fromBuf
, textBuffer
*toBuf
, int fromStart
,
366 int fromEnd
, int toPos
)
368 int length
= fromEnd
- fromStart
;
371 /* Prepare the buffer to receive the new text. If the new text fits in
372 the current buffer, just move the gap (if necessary) to where
373 the text should be inserted. If the new text is too large, reallocate
374 the buffer with a gap large enough to accomodate the new text and a
375 gap of PREFERRED_GAP_SIZE */
376 if (length
> toBuf
->gapEnd
- toBuf
->gapStart
)
377 reallocateBuf(toBuf
, toPos
, length
+ PREFERRED_GAP_SIZE
);
378 else if (toPos
!= toBuf
->gapStart
)
379 moveGap(toBuf
, toPos
);
381 /* Insert the new text (toPos now corresponds to the start of the gap) */
382 if (fromEnd
<= fromBuf
->gapStart
) {
383 memcpy(&toBuf
->buf
[toPos
], &fromBuf
->buf
[fromStart
], length
);
384 } else if (fromStart
>= fromBuf
->gapStart
) {
385 memcpy(&toBuf
->buf
[toPos
],
386 &fromBuf
->buf
[fromStart
+(fromBuf
->gapEnd
-fromBuf
->gapStart
)],
389 part1Length
= fromBuf
->gapStart
- fromStart
;
390 memcpy(&toBuf
->buf
[toPos
], &fromBuf
->buf
[fromStart
], part1Length
);
391 memcpy(&toBuf
->buf
[toPos
+part1Length
], &fromBuf
->buf
[fromBuf
->gapEnd
],
394 toBuf
->gapStart
+= length
;
395 toBuf
->length
+= length
;
396 updateSelections(toBuf
, toPos
, 0, length
);
400 ** Insert "text" columnwise into buffer starting at displayed character
401 ** position "column" on the line beginning at "startPos". Opens a rectangular
402 ** space the width and height of "text", by moving all text to the right of
403 ** "column" right. If charsInserted and charsDeleted are not NULL, the
404 ** number of characters inserted and deleted in the operation (beginning
405 ** at startPos) are returned in these arguments
407 void BufInsertCol(textBuffer
*buf
, int column
, int startPos
, const char *text
,
408 int *charsInserted
, int *charsDeleted
)
410 int nLines
, lineStartPos
, nDeleted
, insertDeleted
, nInserted
;
413 nLines
= countLines(text
);
414 lineStartPos
= BufStartOfLine(buf
, startPos
);
415 nDeleted
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, startPos
, nLines
)) -
417 callPreDeleteCBs(buf
, lineStartPos
, nDeleted
);
418 deletedText
= BufGetRange(buf
, lineStartPos
, lineStartPos
+ nDeleted
);
419 insertCol(buf
, column
, lineStartPos
, text
, &insertDeleted
, &nInserted
,
420 &buf
->cursorPosHint
);
421 if (nDeleted
!= insertDeleted
)
422 fprintf(stderr
, "NEdit internal consistency check ins1 failed");
423 callModifyCBs(buf
, lineStartPos
, nDeleted
, nInserted
, 0, deletedText
);
425 if (charsInserted
!= NULL
)
426 *charsInserted
= nInserted
;
427 if (charsDeleted
!= NULL
)
428 *charsDeleted
= nDeleted
;
432 ** Overlay "text" between displayed character positions "rectStart" and
433 ** "rectEnd" on the line beginning at "startPos". If charsInserted and
434 ** charsDeleted are not NULL, the number of characters inserted and deleted
435 ** in the operation (beginning at startPos) are returned in these arguments.
436 ** If rectEnd equals -1, the width of the inserted text is measured first.
438 void BufOverlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
439 int rectEnd
, const char *text
, int *charsInserted
, int *charsDeleted
)
441 int nLines
, lineStartPos
, nDeleted
, insertDeleted
, nInserted
;
444 nLines
= countLines(text
);
445 lineStartPos
= BufStartOfLine(buf
, startPos
);
447 rectEnd
= rectStart
+ textWidth(text
, buf
->tabDist
, buf
->nullSubsChar
);
448 lineStartPos
= BufStartOfLine(buf
, startPos
);
449 nDeleted
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, startPos
, nLines
)) -
451 callPreDeleteCBs(buf
, lineStartPos
, nDeleted
);
452 deletedText
= BufGetRange(buf
, lineStartPos
, lineStartPos
+ nDeleted
);
453 overlayRect(buf
, lineStartPos
, rectStart
, rectEnd
, text
, &insertDeleted
,
454 &nInserted
, &buf
->cursorPosHint
);
455 if (nDeleted
!= insertDeleted
)
456 fprintf(stderr
, "NEdit internal consistency check ovly1 failed");
457 callModifyCBs(buf
, lineStartPos
, nDeleted
, nInserted
, 0, deletedText
);
459 if (charsInserted
!= NULL
)
460 *charsInserted
= nInserted
;
461 if (charsDeleted
!= NULL
)
462 *charsDeleted
= nDeleted
;
466 ** Replace a rectangular area in buf, given by "start", "end", "rectStart",
467 ** and "rectEnd", with "text". If "text" is vertically longer than the
468 ** rectangle, add extra lines to make room for it.
470 void BufReplaceRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
471 int rectEnd
, const char *text
)
475 int i
, nInsertedLines
, nDeletedLines
, insLen
, hint
;
476 int insertDeleted
, insertInserted
, deleteInserted
;
479 /* Make sure start and end refer to complete lines, since the
480 columnar delete and insert operations will replace whole lines */
481 start
= BufStartOfLine(buf
, start
);
482 end
= BufEndOfLine(buf
, end
);
484 callPreDeleteCBs(buf
, start
, end
-start
);
486 /* If more lines will be deleted than inserted, pad the inserted text
487 with newlines to make it as long as the number of deleted lines. This
488 will indent all of the text to the right of the rectangle to the same
489 column. If more lines will be inserted than deleted, insert extra
490 lines in the buffer at the end of the rectangle to make room for the
491 additional lines in "text" */
492 nInsertedLines
= countLines(text
);
493 nDeletedLines
= BufCountLines(buf
, start
, end
);
494 if (nInsertedLines
< nDeletedLines
) {
497 insLen
= strlen(text
);
498 insText
= XtMalloc(insLen
+ nDeletedLines
- nInsertedLines
+ 1);
499 strcpy(insText
, text
);
500 insPtr
= insText
+ insLen
;
501 for (i
=0; i
<nDeletedLines
-nInsertedLines
; i
++)
504 } else if (nDeletedLines
< nInsertedLines
) {
505 linesPadded
= nInsertedLines
-nDeletedLines
;
506 for (i
=0; i
<linesPadded
; i
++)
507 insert(buf
, end
, "\n");
508 } else /* nDeletedLines == nInsertedLines */ {
511 /* Save a copy of the text which will be modified for the modify CBs */
512 deletedText
= BufGetRange(buf
, start
, end
);
514 /* Delete then insert */
515 deleteRect(buf
, start
, end
, rectStart
, rectEnd
, &deleteInserted
, &hint
);
517 insertCol(buf
, rectStart
, start
, insText
, &insertDeleted
, &insertInserted
,
518 &buf
->cursorPosHint
);
522 insertCol(buf
, rectStart
, start
, text
, &insertDeleted
, &insertInserted
,
523 &buf
->cursorPosHint
);
525 /* Figure out how many chars were inserted and call modify callbacks */
526 if (insertDeleted
!= deleteInserted
+ linesPadded
)
527 fprintf(stderr
, "NEdit: internal consistency check repl1 failed\n");
528 callModifyCBs(buf
, start
, end
-start
, insertInserted
, 0, deletedText
);
533 ** Remove a rectangular swath of characters between character positions start
534 ** and end and horizontal displayed-character offsets rectStart and rectEnd.
536 void BufRemoveRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
542 start
= BufStartOfLine(buf
, start
);
543 end
= BufEndOfLine(buf
, end
);
544 callPreDeleteCBs(buf
, start
, end
-start
);
545 deletedText
= BufGetRange(buf
, start
, end
);
546 deleteRect(buf
, start
, end
, rectStart
, rectEnd
, &nInserted
,
547 &buf
->cursorPosHint
);
548 callModifyCBs(buf
, start
, end
-start
, nInserted
, 0, deletedText
);
553 ** Clear a rectangular "hole" out of the buffer between character positions
554 ** start and end and horizontal displayed-character offsets rectStart and
557 void BufClearRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
563 nLines
= BufCountLines(buf
, start
, end
);
564 newlineString
= XtMalloc(nLines
+1);
565 for (i
=0; i
<nLines
; i
++)
566 newlineString
[i
] = '\n';
567 newlineString
[i
] = '\0';
568 BufOverlayRect(buf
, start
, rectStart
, rectEnd
, newlineString
,
570 XtFree(newlineString
);
573 char *BufGetTextInRect(textBuffer
*buf
, int start
, int end
,
574 int rectStart
, int rectEnd
)
576 int lineStart
, selLeft
, selRight
, len
;
577 char *textOut
, *textIn
, *outPtr
, *retabbedStr
;
579 start
= BufStartOfLine(buf
, start
);
580 end
= BufEndOfLine(buf
, end
);
581 textOut
= XtMalloc((end
- start
) + 1);
584 while (lineStart
<= end
) {
585 findRectSelBoundariesForCopy(buf
, lineStart
, rectStart
, rectEnd
,
586 &selLeft
, &selRight
);
587 textIn
= BufGetRange(buf
, selLeft
, selRight
);
588 len
= selRight
- selLeft
;
589 memcpy(outPtr
, textIn
, len
);
592 lineStart
= BufEndOfLine(buf
, selRight
) + 1;
595 if (outPtr
!= textOut
)
596 outPtr
--; /* don't leave trailing newline */
599 /* If necessary, realign the tabs in the selection as if the text were
600 positioned at the left margin */
601 retabbedStr
= realignTabs(textOut
, rectStart
, 0, buf
->tabDist
,
602 buf
->useTabs
, buf
->nullSubsChar
, &len
);
608 ** Get the hardware tab distance used by all displays for this buffer,
609 ** and used in computing offsets for rectangular selection operations.
611 int BufGetTabDistance(textBuffer
*buf
)
617 ** Set the hardware tab distance used by all displays for this buffer,
618 ** and used in computing offsets for rectangular selection operations.
620 void BufSetTabDistance(textBuffer
*buf
, int tabDist
)
624 /* First call the pre-delete callbacks with the previous tab setting
626 callPreDeleteCBs(buf
, 0, buf
->length
);
628 /* Change the tab setting */
629 buf
->tabDist
= tabDist
;
631 /* Force any display routines to redisplay everything (unfortunately,
632 this means copying the whole buffer contents to provide "deletedText" */
633 deletedText
= BufGetAll(buf
);
634 callModifyCBs(buf
, 0, buf
->length
, buf
->length
, 0, deletedText
);
638 void BufCheckDisplay(textBuffer
*buf
, int start
, int end
)
640 /* just to make sure colors in the selected region are up to date */
641 callModifyCBs(buf
, start
, 0, 0, end
-start
, NULL
);
644 void BufSelect(textBuffer
*buf
, int start
, int end
)
646 selection oldSelection
= buf
->primary
;
648 setSelection(&buf
->primary
, start
, end
);
649 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
652 void BufUnselect(textBuffer
*buf
)
654 selection oldSelection
= buf
->primary
;
656 buf
->primary
.selected
= False
;
657 buf
->primary
.zeroWidth
= False
;
658 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
661 void BufRectSelect(textBuffer
*buf
, int start
, int end
, int rectStart
,
664 selection oldSelection
= buf
->primary
;
666 setRectSelect(&buf
->primary
, start
, end
, rectStart
, rectEnd
);
667 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
670 int BufGetSelectionPos(textBuffer
*buf
, int *start
, int *end
,
671 int *isRect
, int *rectStart
, int *rectEnd
)
673 return getSelectionPos(&buf
->primary
, start
, end
, isRect
, rectStart
,
677 /* Same as above, but also returns TRUE for empty selections */
678 int BufGetEmptySelectionPos(textBuffer
*buf
, int *start
, int *end
,
679 int *isRect
, int *rectStart
, int *rectEnd
)
681 return getSelectionPos(&buf
->primary
, start
, end
, isRect
, rectStart
,
682 rectEnd
) || buf
->primary
.zeroWidth
;
685 char *BufGetSelectionText(textBuffer
*buf
)
687 return getSelectionText(buf
, &buf
->primary
);
690 void BufRemoveSelected(textBuffer
*buf
)
692 removeSelected(buf
, &buf
->primary
);
695 void BufReplaceSelected(textBuffer
*buf
, const char *text
)
697 replaceSelected(buf
, &buf
->primary
, text
);
700 void BufSecondarySelect(textBuffer
*buf
, int start
, int end
)
702 selection oldSelection
= buf
->secondary
;
704 setSelection(&buf
->secondary
, start
, end
);
705 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
708 void BufSecondaryUnselect(textBuffer
*buf
)
710 selection oldSelection
= buf
->secondary
;
712 buf
->secondary
.selected
= False
;
713 buf
->secondary
.zeroWidth
= False
;
714 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
717 void BufSecRectSelect(textBuffer
*buf
, int start
, int end
,
718 int rectStart
, int rectEnd
)
720 selection oldSelection
= buf
->secondary
;
722 setRectSelect(&buf
->secondary
, start
, end
, rectStart
, rectEnd
);
723 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
726 int BufGetSecSelectPos(textBuffer
*buf
, int *start
, int *end
,
727 int *isRect
, int *rectStart
, int *rectEnd
)
729 return getSelectionPos(&buf
->secondary
, start
, end
, isRect
, rectStart
,
733 char *BufGetSecSelectText(textBuffer
*buf
)
735 return getSelectionText(buf
, &buf
->secondary
);
738 void BufRemoveSecSelect(textBuffer
*buf
)
740 removeSelected(buf
, &buf
->secondary
);
743 void BufReplaceSecSelect(textBuffer
*buf
, const char *text
)
745 replaceSelected(buf
, &buf
->secondary
, text
);
748 void BufHighlight(textBuffer
*buf
, int start
, int end
)
750 selection oldSelection
= buf
->highlight
;
752 setSelection(&buf
->highlight
, start
, end
);
753 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
756 void BufUnhighlight(textBuffer
*buf
)
758 selection oldSelection
= buf
->highlight
;
760 buf
->highlight
.selected
= False
;
761 buf
->highlight
.zeroWidth
= False
;
762 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
765 void BufRectHighlight(textBuffer
*buf
, int start
, int end
,
766 int rectStart
, int rectEnd
)
768 selection oldSelection
= buf
->highlight
;
770 setRectSelect(&buf
->highlight
, start
, end
, rectStart
, rectEnd
);
771 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
774 int BufGetHighlightPos(textBuffer
*buf
, int *start
, int *end
,
775 int *isRect
, int *rectStart
, int *rectEnd
)
777 return getSelectionPos(&buf
->highlight
, start
, end
, isRect
, rectStart
,
781 char *BufGetHighlightText(textBuffer
*buf
)
783 return getSelectionText(buf
, &buf
->highlight
);
787 ** Add a callback routine to be called when the buffer is modified
789 void BufAddModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
792 bufModifyCallbackProc
*newModifyProcs
;
796 newModifyProcs
= (bufModifyCallbackProc
*)
797 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
+1));
798 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
+1));
799 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
800 newModifyProcs
[i
] = buf
->modifyProcs
[i
];
801 newCBArgs
[i
] = buf
->cbArgs
[i
];
803 if (buf
->nModifyProcs
!= 0) {
804 XtFree((char *)buf
->modifyProcs
);
805 XtFree((char *)buf
->cbArgs
);
807 newModifyProcs
[buf
->nModifyProcs
] = bufModifiedCB
;
808 newCBArgs
[buf
->nModifyProcs
] = cbArg
;
810 buf
->modifyProcs
= newModifyProcs
;
811 buf
->cbArgs
= newCBArgs
;
815 ** Similar to the above, but makes sure that the callback is called before
816 ** normal priority callbacks.
818 void BufAddHighPriorityModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
821 bufModifyCallbackProc
*newModifyProcs
;
825 newModifyProcs
= (bufModifyCallbackProc
*)
826 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
+1));
827 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
+1));
828 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
829 newModifyProcs
[i
+1] = buf
->modifyProcs
[i
];
830 newCBArgs
[i
+1] = buf
->cbArgs
[i
];
832 if (buf
->nModifyProcs
!= 0) {
833 XtFree((char *)buf
->modifyProcs
);
834 XtFree((char *)buf
->cbArgs
);
836 newModifyProcs
[0] = bufModifiedCB
;
837 newCBArgs
[0] = cbArg
;
839 buf
->modifyProcs
= newModifyProcs
;
840 buf
->cbArgs
= newCBArgs
;
843 void BufRemoveModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
846 int i
, toRemove
= -1;
847 bufModifyCallbackProc
*newModifyProcs
;
850 /* find the matching callback to remove */
851 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
852 if (buf
->modifyProcs
[i
] == bufModifiedCB
&& buf
->cbArgs
[i
] == cbArg
) {
857 if (toRemove
== -1) {
858 fprintf(stderr
, "NEdit Internal Error: Can't find modify CB to remove\n");
862 /* Allocate new lists for remaining callback procs and args (if
865 if (buf
->nModifyProcs
== 0) {
866 buf
->nModifyProcs
= 0;
867 XtFree((char *)buf
->modifyProcs
);
868 buf
->modifyProcs
= NULL
;
869 XtFree((char *)buf
->cbArgs
);
873 newModifyProcs
= (bufModifyCallbackProc
*)
874 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
));
875 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
));
877 /* copy out the remaining members and free the old lists */
878 for (i
=0; i
<toRemove
; i
++) {
879 newModifyProcs
[i
] = buf
->modifyProcs
[i
];
880 newCBArgs
[i
] = buf
->cbArgs
[i
];
882 for (; i
<buf
->nModifyProcs
; i
++) {
883 newModifyProcs
[i
] = buf
->modifyProcs
[i
+1];
884 newCBArgs
[i
] = buf
->cbArgs
[i
+1];
886 XtFree((char *)buf
->modifyProcs
);
887 XtFree((char *)buf
->cbArgs
);
888 buf
->modifyProcs
= newModifyProcs
;
889 buf
->cbArgs
= newCBArgs
;
893 ** Add a callback routine to be called before text is deleted from the buffer.
895 void BufAddPreDeleteCB(textBuffer
*buf
, bufPreDeleteCallbackProc bufPreDeleteCB
,
898 bufPreDeleteCallbackProc
*newPreDeleteProcs
;
902 newPreDeleteProcs
= (bufPreDeleteCallbackProc
*)
903 XtMalloc(sizeof(bufPreDeleteCallbackProc
*) * (buf
->nPreDeleteProcs
+1));
904 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nPreDeleteProcs
+1));
905 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++) {
906 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
];
907 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
];
909 if (buf
->nPreDeleteProcs
!= 0) {
910 XtFree((char *)buf
->preDeleteProcs
);
911 XtFree((char *)buf
->preDeleteCbArgs
);
913 newPreDeleteProcs
[buf
->nPreDeleteProcs
] = bufPreDeleteCB
;
914 newCBArgs
[buf
->nPreDeleteProcs
] = cbArg
;
915 buf
->nPreDeleteProcs
++;
916 buf
->preDeleteProcs
= newPreDeleteProcs
;
917 buf
->preDeleteCbArgs
= newCBArgs
;
920 void BufRemovePreDeleteCB(textBuffer
*buf
, bufPreDeleteCallbackProc bufPreDeleteCB
,
923 int i
, toRemove
= -1;
924 bufPreDeleteCallbackProc
*newPreDeleteProcs
;
927 /* find the matching callback to remove */
928 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++) {
929 if (buf
->preDeleteProcs
[i
] == bufPreDeleteCB
&&
930 buf
->preDeleteCbArgs
[i
] == cbArg
) {
935 if (toRemove
== -1) {
936 fprintf(stderr
, "NEdit Internal Error: Can't find pre-delete CB to remove\n");
940 /* Allocate new lists for remaining callback procs and args (if
942 buf
->nPreDeleteProcs
--;
943 if (buf
->nPreDeleteProcs
== 0) {
944 buf
->nPreDeleteProcs
= 0;
945 XtFree((char *)buf
->preDeleteProcs
);
946 buf
->preDeleteProcs
= NULL
;
947 XtFree((char *)buf
->preDeleteCbArgs
);
948 buf
->preDeleteCbArgs
= NULL
;
951 newPreDeleteProcs
= (bufPreDeleteCallbackProc
*)
952 XtMalloc(sizeof(bufPreDeleteCallbackProc
*) * (buf
->nPreDeleteProcs
));
953 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nPreDeleteProcs
));
955 /* copy out the remaining members and free the old lists */
956 for (i
=0; i
<toRemove
; i
++) {
957 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
];
958 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
];
960 for (; i
<buf
->nPreDeleteProcs
; i
++) {
961 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
+1];
962 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
+1];
964 XtFree((char *)buf
->preDeleteProcs
);
965 XtFree((char *)buf
->preDeleteCbArgs
);
966 buf
->preDeleteProcs
= newPreDeleteProcs
;
967 buf
->preDeleteCbArgs
= newCBArgs
;
971 ** Return the text from the entire line containing position "pos"
973 char *BufGetLineText(textBuffer
*buf
, int pos
)
975 return BufGetRange(buf
, BufStartOfLine(buf
, pos
), BufEndOfLine(buf
, pos
));
979 ** Find the position of the start of the line containing position "pos"
981 int BufStartOfLine(textBuffer
*buf
, int pos
)
985 if (!searchBackward(buf
, pos
, '\n', &startPos
))
992 ** Find the position of the end of the line containing position "pos"
993 ** (which is either a pointer to the newline character ending the line,
994 ** or a pointer to one character beyond the end of the buffer)
996 int BufEndOfLine(textBuffer
*buf
, int pos
)
1000 if (!searchForward(buf
, pos
, '\n', &endPos
))
1001 endPos
= buf
->length
;
1006 ** Get a character from the text buffer expanded into it's screen
1007 ** representation (which may be several characters for a tab or a
1008 ** control code). Returns the number of characters written to "outStr".
1009 ** "indent" is the number of characters from the start of the line
1010 ** for figuring tabs. Output string is guranteed to be shorter or
1011 ** equal in length to MAX_EXP_CHAR_LEN
1013 int BufGetExpandedChar(textBuffer
*buf
, int pos
, int indent
, char *outStr
)
1015 return BufExpandCharacter(BufGetCharacter(buf
, pos
), indent
, outStr
,
1016 buf
->tabDist
, buf
->nullSubsChar
);
1020 ** Expand a single character from the text buffer into it's screen
1021 ** representation (which may be several characters for a tab or a
1022 ** control code). Returns the number of characters added to "outStr".
1023 ** "indent" is the number of characters from the start of the line
1024 ** for figuring tabs. Output string is guranteed to be shorter or
1025 ** equal in length to MAX_EXP_CHAR_LEN
1027 int BufExpandCharacter(char c
, int indent
, char *outStr
, int tabDist
,
1032 /* Convert tabs to spaces */
1034 nSpaces
= tabDist
- (indent
% tabDist
);
1035 for (i
=0; i
<nSpaces
; i
++)
1040 /* Convert ASCII (and EBCDIC in the __MVS__ (OS/390) case) control
1041 codes to readable character sequences */
1042 if (c
== nullSubsChar
) {
1043 sprintf(outStr
, "<nul>");
1047 if (((unsigned char)c
) <= 63) {
1048 sprintf(outStr
, "<%s>", ControlCodeTable
[(unsigned char)c
]);
1049 return strlen(outStr
);
1052 if (((unsigned char)c
) <= 31) {
1053 sprintf(outStr
, "<%s>", ControlCodeTable
[(unsigned char)c
]);
1054 return strlen(outStr
);
1055 } else if (c
== 127) {
1056 sprintf(outStr
, "<del>");
1061 /* Otherwise, just return the character */
1067 ** Return the length in displayed characters of character "c" expanded
1068 ** for display (as discussed above in BufGetExpandedChar). If the
1069 ** buffer for which the character width is being measured is doing null
1070 ** substitution, nullSubsChar should be passed as that character (or nul
1073 int BufCharWidth(char c
, int indent
, int tabDist
, char nullSubsChar
)
1075 /* Note, this code must parallel that in BufExpandCharacter */
1076 if (c
== nullSubsChar
)
1079 return tabDist
- (indent
% tabDist
);
1080 else if (((unsigned char)c
) <= 31)
1081 return strlen(ControlCodeTable
[(unsigned char)c
]) + 2;
1088 ** Count the number of displayed characters between buffer position
1089 ** "lineStartPos" and "targetPos". (displayed characters are the characters
1090 ** shown on the screen to represent characters in the buffer, where tabs and
1091 ** control characters are expanded)
1093 int BufCountDispChars(textBuffer
*buf
, int lineStartPos
, int targetPos
)
1095 int pos
, charCount
= 0;
1096 char expandedChar
[MAX_EXP_CHAR_LEN
];
1099 while (pos
< targetPos
&& pos
< buf
->length
)
1100 charCount
+= BufGetExpandedChar(buf
, pos
++, charCount
, expandedChar
);
1105 ** Count forward from buffer position "startPos" in displayed characters
1106 ** (displayed characters are the characters shown on the screen to represent
1107 ** characters in the buffer, where tabs and control characters are expanded)
1109 int BufCountForwardDispChars(textBuffer
*buf
, int lineStartPos
, int nChars
)
1111 int pos
, charCount
= 0;
1115 while (charCount
< nChars
&& pos
< buf
->length
) {
1116 c
= BufGetCharacter(buf
, pos
);
1119 charCount
+= BufCharWidth(c
, charCount
, buf
->tabDist
,buf
->nullSubsChar
);
1126 ** Count the number of newlines between startPos and endPos in buffer "buf".
1127 ** The character at position "endPos" is not counted.
1129 int BufCountLines(textBuffer
*buf
, int startPos
, int endPos
)
1131 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1135 while (pos
< buf
->gapStart
) {
1138 if (buf
->buf
[pos
++] == '\n')
1141 while (pos
< buf
->length
) {
1144 if (buf
->buf
[pos
++ + gapLen
] == '\n')
1151 ** Find the first character of the line "nLines" forward from "startPos"
1152 ** in "buf" and return its position
1154 int BufCountForwardNLines(textBuffer
*buf
, int startPos
, int nLines
)
1156 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1163 while (pos
< buf
->gapStart
) {
1164 if (buf
->buf
[pos
++] == '\n') {
1166 if (lineCount
== nLines
)
1170 while (pos
< buf
->length
) {
1171 if (buf
->buf
[pos
++ + gapLen
] == '\n') {
1173 if (lineCount
>= nLines
)
1181 ** Find the position of the first character of the line "nLines" backwards
1182 ** from "startPos" (not counting the character pointed to by "startpos" if
1183 ** that is a newline) in "buf". nLines == 0 means find the beginning of
1186 int BufCountBackwardNLines(textBuffer
*buf
, int startPos
, int nLines
)
1188 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1195 while (pos
>= buf
->gapStart
) {
1196 if (buf
->buf
[pos
+ gapLen
] == '\n') {
1197 if (++lineCount
>= nLines
)
1203 if (buf
->buf
[pos
] == '\n') {
1204 if (++lineCount
>= nLines
)
1213 ** Search forwards in buffer "buf" for characters in "searchChars", starting
1214 ** with the character "startPos", and returning the result in "foundPos"
1215 ** returns True if found, False if not.
1217 int BufSearchForward(textBuffer
*buf
, int startPos
, const char *searchChars
,
1220 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1224 while (pos
< buf
->gapStart
) {
1225 for (c
=searchChars
; *c
!='\0'; c
++) {
1226 if (buf
->buf
[pos
] == *c
) {
1233 while (pos
< buf
->length
) {
1234 for (c
=searchChars
; *c
!='\0'; c
++) {
1235 if (buf
->buf
[pos
+ gapLen
] == *c
) {
1242 *foundPos
= buf
->length
;
1247 ** Search backwards in buffer "buf" for characters in "searchChars", starting
1248 ** with the character BEFORE "startPos", returning the result in "foundPos"
1249 ** returns True if found, False if not.
1251 int BufSearchBackward(textBuffer
*buf
, int startPos
, const char *searchChars
,
1254 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1257 if (startPos
== 0) {
1261 pos
= startPos
== 0 ? 0 : startPos
- 1;
1262 while (pos
>= buf
->gapStart
) {
1263 for (c
=searchChars
; *c
!='\0'; c
++) {
1264 if (buf
->buf
[pos
+ gapLen
] == *c
) {
1272 for (c
=searchChars
; *c
!='\0'; c
++) {
1273 if (buf
->buf
[pos
] == *c
) {
1285 ** A horrible design flaw in NEdit (from the very start, before we knew that
1286 ** NEdit would become so popular), is that it uses C NULL terminated strings
1287 ** to hold text. This means editing text containing NUL characters is not
1288 ** possible without special consideration. Here is the special consideration.
1289 ** The routines below maintain a special substitution-character which stands
1290 ** in for a null, and translates strings an buffers back and forth from/to
1291 ** the substituted form, figure out what to substitute, and figure out
1292 ** when we're in over our heads and no translation is possible.
1296 ** The primary routine for integrating new text into a text buffer with
1297 ** substitution of another character for ascii nuls. This substitutes null
1298 ** characters in the string in preparation for being copied or replaced
1299 ** into the buffer, and if neccessary, adjusts the buffer as well, in the
1300 ** event that the string contains the character it is currently using for
1301 ** substitution. Returns False, if substitution is no longer possible
1302 ** because all non-printable characters are already in use.
1304 int BufSubstituteNullChars(char *string
, int length
, textBuffer
*buf
)
1306 char histogram
[256];
1308 /* Find out what characters the string contains */
1309 histogramCharacters(string
, length
, histogram
, True
);
1311 /* Does the string contain the null-substitute character? If so, re-
1312 histogram the buffer text to find a character which is ok in both the
1313 string and the buffer, and change the buffer's null-substitution
1314 character. If none can be found, give up and return False */
1315 if (histogram
[(unsigned char)buf
->nullSubsChar
] != 0) {
1316 char *bufString
, newSubsChar
;
1317 bufString
= BufGetAll(buf
);
1318 histogramCharacters(bufString
, buf
->length
, histogram
, False
);
1319 newSubsChar
= chooseNullSubsChar(histogram
);
1320 if (newSubsChar
== '\0') {
1324 subsChars(bufString
, buf
->length
, buf
->nullSubsChar
, newSubsChar
);
1325 delete(buf
, 0, buf
->length
);
1326 insert(buf
, 0, bufString
);
1328 buf
->nullSubsChar
= newSubsChar
;
1331 /* If the string contains null characters, substitute them with the
1332 buffer's null substitution character */
1333 if (histogram
[0] != 0)
1334 subsChars(string
, length
, '\0', buf
->nullSubsChar
);
1339 ** Convert strings obtained from buffers which contain null characters, which
1340 ** have been substituted for by a special substitution character, back to
1341 ** a null-containing string. There is no time penalty for calling this
1342 ** routine if no substitution has been done.
1344 void BufUnsubstituteNullChars(char *string
, textBuffer
*buf
)
1346 register char *c
, subsChar
= buf
->nullSubsChar
;
1348 if (subsChar
== '\0')
1350 for (c
=string
; *c
!= '\0'; c
++)
1356 ** Compares len Bytes contained in buf starting at Position pos with
1357 ** the contens of cmpText. Returns 0 if there are no differences,
1361 int BufCmp(textBuffer
* buf
, int pos
, int len
, const char *cmpText
)
1368 if (posEnd
> buf
->length
) {
1375 if (posEnd
<= buf
->gapStart
) {
1376 return (strncmp(&(buf
->buf
[pos
]), cmpText
, len
));
1377 } else if (pos
>= buf
->gapStart
) {
1378 return (strncmp (&buf
->buf
[pos
+ (buf
->gapEnd
- buf
->gapStart
)],
1381 part1Length
= buf
->gapStart
- pos
;
1382 result
= strncmp(&buf
->buf
[pos
], cmpText
, part1Length
);
1386 return (strncmp(&buf
->buf
[buf
->gapEnd
], &cmpText
[part1Length
],
1387 len
- part1Length
));
1392 ** Create a pseudo-histogram of the characters in a string (don't actually
1393 ** count, because we don't want overflow, just mark the character's presence
1394 ** with a 1). If init is true, initialize the histogram before acumulating.
1395 ** if not, add the new data to an existing histogram.
1397 static void histogramCharacters(const char *string
, int length
, char hist
[256],
1404 for (i
=0; i
<256; i
++)
1406 for (c
=string
; c
< &string
[length
]; c
++)
1407 hist
[*((unsigned char *)c
)] |= 1;
1411 ** Substitute fromChar with toChar in string.
1413 static void subsChars(char *string
, int length
, char fromChar
, char toChar
)
1417 for (c
=string
; c
< &string
[length
]; c
++)
1418 if (*c
== fromChar
) *c
= toChar
;
1422 ** Search through ascii control characters in histogram in order of least
1423 ** likelihood of use, find an unused character to use as a stand-in for a
1424 ** null. If the character set is full (no available characters outside of
1425 ** the printable set, return the null character.
1427 static char chooseNullSubsChar(char hist
[256])
1429 #define N_REPLACEMENTS 25
1430 static char replacements
[N_REPLACEMENTS
] = {1,2,3,4,5,6,14,15,16,17,18,19,
1431 20,21,22,23,24,25,26,28,29,30,31,11,7};
1433 for (i
= 0; i
< N_REPLACEMENTS
; i
++)
1434 if (hist
[(unsigned char)replacements
[i
]] == 0)
1435 return replacements
[i
];
1440 ** Internal (non-redisplaying) version of BufInsert. Returns the length of
1441 ** text inserted (this is just strlen(text), however this calculation can be
1442 ** expensive and the length will be required by any caller who will continue
1443 ** on to call redisplay). pos must be contiguous with the existing text in
1444 ** the buffer (i.e. not past the end).
1446 static int insert(textBuffer
*buf
, int pos
, const char *text
)
1448 int length
= strlen(text
);
1450 /* Prepare the buffer to receive the new text. If the new text fits in
1451 the current buffer, just move the gap (if necessary) to where
1452 the text should be inserted. If the new text is too large, reallocate
1453 the buffer with a gap large enough to accomodate the new text and a
1454 gap of PREFERRED_GAP_SIZE */
1455 if (length
> buf
->gapEnd
- buf
->gapStart
)
1456 reallocateBuf(buf
, pos
, length
+ PREFERRED_GAP_SIZE
);
1457 else if (pos
!= buf
->gapStart
)
1460 /* Insert the new text (pos now corresponds to the start of the gap) */
1461 memcpy(&buf
->buf
[pos
], text
, length
);
1462 buf
->gapStart
+= length
;
1463 buf
->length
+= length
;
1464 updateSelections(buf
, pos
, 0, length
);
1470 ** Internal (non-redisplaying) version of BufRemove. Removes the contents
1471 ** of the buffer between start and end (and moves the gap to the site of
1474 static void delete(textBuffer
*buf
, int start
, int end
)
1476 /* if the gap is not contiguous to the area to remove, move it there */
1477 if (start
> buf
->gapStart
)
1478 moveGap(buf
, start
);
1479 else if (end
< buf
->gapStart
)
1482 /* expand the gap to encompass the deleted characters */
1483 buf
->gapEnd
+= end
- buf
->gapStart
;
1484 buf
->gapStart
-= buf
->gapStart
- start
;
1486 /* update the length */
1487 buf
->length
-= end
- start
;
1489 /* fix up any selections which might be affected by the change */
1490 updateSelections(buf
, start
, end
-start
, 0);
1494 ** Insert a column of text without calling the modify callbacks. Note that
1495 ** in some pathological cases, inserting can actually decrease the size of
1496 ** the buffer because of spaces being coalesced into tabs. "nDeleted" and
1497 ** "nInserted" return the number of characters deleted and inserted beginning
1498 ** at the start of the line containing "startPos". "endPos" returns buffer
1499 ** position of the lower left edge of the inserted column (as a hint for
1500 ** routines which need to set a cursor position).
1502 static void insertCol(textBuffer
*buf
, int column
, int startPos
,
1503 const char *insText
, int *nDeleted
, int *nInserted
, int *endPos
)
1505 int nLines
, start
, end
, insWidth
, lineStart
, lineEnd
;
1506 int expReplLen
, expInsLen
, len
, endOffset
;
1507 char *outStr
, *outPtr
, *line
, *replText
, *expText
, *insLine
;
1513 /* Allocate a buffer for the replacement string large enough to hold
1514 possibly expanded tabs in both the inserted text and the replaced
1515 area, as well as per line: 1) an additional 2*MAX_EXP_CHAR_LEN
1516 characters for padding where tabs and control characters cross the
1517 column of the selection, 2) up to "column" additional spaces per
1518 line for padding out to the position of "column", 3) padding up
1519 to the width of the inserted text if that must be padded to align
1520 the text beyond the inserted column. (Space for additional
1521 newlines if the inserted text extends beyond the end of the buffer
1522 is counted with the length of insText) */
1523 start
= BufStartOfLine(buf
, startPos
);
1524 nLines
= countLines(insText
) + 1;
1525 insWidth
= textWidth(insText
, buf
->tabDist
, buf
->nullSubsChar
);
1526 end
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, start
, nLines
-1));
1527 replText
= BufGetRange(buf
, start
, end
);
1528 expText
= expandTabs(replText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1532 expText
= expandTabs(insText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1535 outStr
= XtMalloc(expReplLen
+ expInsLen
+
1536 nLines
* (column
+ insWidth
+ MAX_EXP_CHAR_LEN
) + 1);
1538 /* Loop over all lines in the buffer between start and end inserting
1539 text at column, splitting tabs and adding padding appropriately */
1544 lineEnd
= BufEndOfLine(buf
, lineStart
);
1545 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1546 insLine
= copyLine(insPtr
, &len
);
1548 insertColInLine(line
, insLine
, column
, insWidth
, buf
->tabDist
,
1549 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1552 #if 0 /* Earlier comments claimed that trailing whitespace could multiply on
1553 the ends of lines, but insertColInLine looks like it should never
1554 add space unnecessarily, and this trimming interfered with
1555 paragraph filling, so lets see if it works without it. MWE */
1558 for (c
=outPtr
+len
-1; c
>outPtr
&& (*c
== ' ' || *c
== '\t'); c
--)
1564 lineStart
= lineEnd
< buf
->length
? lineEnd
+ 1 : buf
->length
;
1565 if (*insPtr
== '\0')
1569 if (outPtr
!= outStr
)
1570 outPtr
--; /* trim back off extra newline */
1573 /* replace the text between start and end with the new stuff */
1574 delete(buf
, start
, end
);
1575 insert(buf
, start
, outStr
);
1576 *nInserted
= outPtr
- outStr
;
1577 *nDeleted
= end
- start
;
1578 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1583 ** Delete a rectangle of text without calling the modify callbacks. Returns
1584 ** the number of characters replacing those between start and end. Note that
1585 ** in some pathological cases, deleting can actually increase the size of
1586 ** the buffer because of tab expansions. "endPos" returns the buffer position
1587 ** of the point in the last line where the text was removed (as a hint for
1588 ** routines which need to position the cursor after a delete operation)
1590 static void deleteRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
1591 int rectEnd
, int *replaceLen
, int *endPos
)
1593 int nLines
, lineStart
, lineEnd
, len
, endOffset
;
1594 char *outStr
, *outPtr
, *line
, *text
, *expText
;
1596 /* allocate a buffer for the replacement string large enough to hold
1597 possibly expanded tabs as well as an additional MAX_EXP_CHAR_LEN * 2
1598 characters per line for padding where tabs and control characters cross
1599 the edges of the selection */
1600 start
= BufStartOfLine(buf
, start
);
1601 end
= BufEndOfLine(buf
, end
);
1602 nLines
= BufCountLines(buf
, start
, end
) + 1;
1603 text
= BufGetRange(buf
, start
, end
);
1604 expText
= expandTabs(text
, 0, buf
->tabDist
, buf
->nullSubsChar
, &len
);
1607 outStr
= XtMalloc(len
+ nLines
* MAX_EXP_CHAR_LEN
* 2 + 1);
1609 /* loop over all lines in the buffer between start and end removing
1610 the text between rectStart and rectEnd and padding appropriately */
1613 while (lineStart
<= buf
->length
&& lineStart
<= end
) {
1614 lineEnd
= BufEndOfLine(buf
, lineStart
);
1615 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1616 deleteRectFromLine(line
, rectStart
, rectEnd
, buf
->tabDist
,
1617 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1621 lineStart
= lineEnd
+ 1;
1623 if (outPtr
!= outStr
)
1624 outPtr
--; /* trim back off extra newline */
1627 /* replace the text between start and end with the newly created string */
1628 delete(buf
, start
, end
);
1629 insert(buf
, start
, outStr
);
1630 *replaceLen
= outPtr
- outStr
;
1631 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1636 ** Overlay a rectangular area of text without calling the modify callbacks.
1637 ** "nDeleted" and "nInserted" return the number of characters deleted and
1638 ** inserted beginning at the start of the line containing "startPos".
1639 ** "endPos" returns buffer position of the lower left edge of the inserted
1640 ** column (as a hint for routines which need to set a cursor position).
1642 static void overlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
1643 int rectEnd
, const char *insText
,
1644 int *nDeleted
, int *nInserted
, int *endPos
)
1646 int nLines
, start
, end
, lineStart
, lineEnd
;
1647 int expInsLen
, len
, endOffset
;
1648 char *c
, *outStr
, *outPtr
, *line
, *expText
, *insLine
;
1651 /* Allocate a buffer for the replacement string large enough to hold
1652 possibly expanded tabs in the inserted text, as well as per line: 1)
1653 an additional 2*MAX_EXP_CHAR_LEN characters for padding where tabs
1654 and control characters cross the column of the selection, 2) up to
1655 "column" additional spaces per line for padding out to the position
1656 of "column", 3) padding up to the width of the inserted text if that
1657 must be padded to align the text beyond the inserted column. (Space
1658 for additional newlines if the inserted text extends beyond the end
1659 of the buffer is counted with the length of insText) */
1660 start
= BufStartOfLine(buf
, startPos
);
1661 nLines
= countLines(insText
) + 1;
1662 end
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, start
, nLines
-1));
1663 expText
= expandTabs(insText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1666 outStr
= XtMalloc(end
-start
+ expInsLen
+
1667 nLines
* (rectEnd
+ MAX_EXP_CHAR_LEN
) + 1);
1669 /* Loop over all lines in the buffer between start and end overlaying the
1670 text between rectStart and rectEnd and padding appropriately. Trim
1671 trailing space from line (whitespace at the ends of lines otherwise
1672 tends to multiply, since additional padding is added to maintain it */
1677 lineEnd
= BufEndOfLine(buf
, lineStart
);
1678 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1679 insLine
= copyLine(insPtr
, &len
);
1681 overlayRectInLine(line
, insLine
, rectStart
, rectEnd
, buf
->tabDist
,
1682 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1685 for (c
=outPtr
+len
-1; c
>outPtr
&& (*c
== ' ' || *c
== '\t'); c
--)
1689 lineStart
= lineEnd
< buf
->length
? lineEnd
+ 1 : buf
->length
;
1690 if (*insPtr
== '\0')
1694 if (outPtr
!= outStr
)
1695 outPtr
--; /* trim back off extra newline */
1698 /* replace the text between start and end with the new stuff */
1699 delete(buf
, start
, end
);
1700 insert(buf
, start
, outStr
);
1701 *nInserted
= outPtr
- outStr
;
1702 *nDeleted
= end
- start
;
1703 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1708 ** Insert characters from single-line string "insLine" in single-line string
1709 ** "line" at "column", leaving "insWidth" space before continuing line.
1710 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1711 ** returns the number of characters from the beginning of the string to
1712 ** the right edge of the inserted text (as a hint for routines which need
1713 ** to position the cursor).
1715 static void insertColInLine(const char *line
, const char *insLine
,
1716 int column
, int insWidth
, int tabDist
, int useTabs
, char nullSubsChar
,
1717 char *outStr
, int *outLen
, int *endOffset
)
1719 char *c
, *outPtr
, *retabbedStr
;
1720 const char *linePtr
;
1721 int indent
, toIndent
, len
, postColIndent
;
1723 /* copy the line up to "column" */
1726 for (linePtr
=line
; *linePtr
!='\0'; linePtr
++) {
1727 len
= BufCharWidth(*linePtr
, indent
, tabDist
, nullSubsChar
);
1728 if (indent
+ len
> column
)
1731 *outPtr
++ = *linePtr
;
1734 /* If "column" falls in the middle of a character, and the character is a
1735 tab, leave it off and leave the indent short and it will get padded
1736 later. If it's a control character, insert it and adjust indent
1738 if (indent
< column
&& *linePtr
!= '\0') {
1739 postColIndent
= indent
+ len
;
1740 if (*linePtr
== '\t')
1743 *outPtr
++ = *linePtr
++;
1747 postColIndent
= indent
;
1749 /* If there's no text after the column and no text to insert, that's all */
1750 if (*insLine
== '\0' && *linePtr
== '\0') {
1751 *outLen
= *endOffset
= outPtr
- outStr
;
1755 /* pad out to column if text is too short */
1756 if (indent
< column
) {
1757 addPadding(outPtr
, indent
, column
, tabDist
, useTabs
, nullSubsChar
,&len
);
1762 /* Copy the text from "insLine" (if any), recalculating the tabs as if
1763 the inserted string began at column 0 to its new column destination */
1764 if (*insLine
!= '\0') {
1765 retabbedStr
= realignTabs(insLine
, 0, indent
, tabDist
, useTabs
,
1766 nullSubsChar
, &len
);
1767 for (c
=retabbedStr
; *c
!='\0'; c
++) {
1769 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1772 XtFree(retabbedStr
);
1775 /* If the original line did not extend past "column", that's all */
1776 if (*linePtr
== '\0') {
1777 *outLen
= *endOffset
= outPtr
- outStr
;
1781 /* Pad out to column + width of inserted text + (additional original
1782 offset due to non-breaking character at column) */
1783 toIndent
= column
+ insWidth
+ postColIndent
-column
;
1784 addPadding(outPtr
, indent
, toIndent
, tabDist
, useTabs
, nullSubsChar
, &len
);
1788 /* realign tabs for text beyond "column" and write it out */
1789 retabbedStr
= realignTabs(linePtr
, postColIndent
, indent
, tabDist
,
1790 useTabs
, nullSubsChar
, &len
);
1791 strcpy(outPtr
, retabbedStr
);
1792 XtFree(retabbedStr
);
1793 *endOffset
= outPtr
- outStr
;
1794 *outLen
= (outPtr
- outStr
) + len
;
1798 ** Remove characters in single-line string "line" between displayed positions
1799 ** "rectStart" and "rectEnd", and write the result to "outStr", which is
1800 ** assumed to be large enough to hold the returned string. Note that in
1801 ** certain cases, it is possible for the string to get longer due to
1802 ** expansion of tabs. "endOffset" returns the number of characters from
1803 ** the beginning of the string to the point where the characters were
1804 ** deleted (as a hint for routines which need to position the cursor).
1806 static void deleteRectFromLine(const char *line
, int rectStart
, int rectEnd
,
1807 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
1810 int indent
, preRectIndent
, postRectIndent
, len
;
1815 /* copy the line up to rectStart */
1818 for (c
=line
; *c
!='\0'; c
++) {
1819 if (indent
> rectStart
)
1821 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1822 if (indent
+ len
> rectStart
&& (indent
== rectStart
|| *c
== '\t'))
1827 preRectIndent
= indent
;
1829 /* skip the characters between rectStart and rectEnd */
1830 for(; *c
!='\0' && indent
<rectEnd
; c
++)
1831 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1832 postRectIndent
= indent
;
1834 /* If the line ended before rectEnd, there's nothing more to do */
1837 *outLen
= *endOffset
= outPtr
- outStr
;
1841 /* fill in any space left by removed tabs or control characters
1842 which straddled the boundaries */
1843 indent
= max(rectStart
+ postRectIndent
-rectEnd
, preRectIndent
);
1844 addPadding(outPtr
, preRectIndent
, indent
, tabDist
, useTabs
, nullSubsChar
,
1848 /* Copy the rest of the line. If the indentation has changed, preserve
1849 the position of non-whitespace characters by converting tabs to
1850 spaces, then back to tabs with the correct offset */
1851 retabbedStr
= realignTabs(c
, postRectIndent
, indent
, tabDist
, useTabs
,
1852 nullSubsChar
, &len
);
1853 strcpy(outPtr
, retabbedStr
);
1854 XtFree(retabbedStr
);
1855 *endOffset
= outPtr
- outStr
;
1856 *outLen
= (outPtr
- outStr
) + len
;
1860 ** Overlay characters from single-line string "insLine" on single-line string
1861 ** "line" between displayed character offsets "rectStart" and "rectEnd".
1862 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1863 ** returns the number of characters from the beginning of the string to
1864 ** the right edge of the inserted text (as a hint for routines which need
1865 ** to position the cursor).
1867 ** This code does not handle control characters very well, but oh well.
1869 static void overlayRectInLine(const char *line
, const char *insLine
,
1870 int rectStart
, int rectEnd
, int tabDist
, int useTabs
,
1871 char nullSubsChar
, char *outStr
, int *outLen
, int *endOffset
)
1873 char *c
, *outPtr
, *retabbedStr
;
1874 const char *linePtr
;
1875 int inIndent
, outIndent
, len
, postRectIndent
;
1877 /* copy the line up to "rectStart" or just before the char that
1880 inIndent
= outIndent
= 0;
1881 for (linePtr
=line
; *linePtr
!='\0'; linePtr
++) {
1882 len
= BufCharWidth(*linePtr
, inIndent
, tabDist
, nullSubsChar
);
1883 if (inIndent
+ len
> rectStart
)
1887 *outPtr
++ = *linePtr
;
1890 /* If "rectStart" falls in the middle of a character, and the character
1891 is a tab, leave it off and leave the outIndent short and it will get
1892 padded later. If it's a control character, insert it and adjust
1893 outIndent accordingly. */
1894 if (inIndent
< rectStart
&& *linePtr
!= '\0') {
1895 if (*linePtr
== '\t') {
1896 /* Skip past the tab */
1900 *outPtr
++ = *linePtr
++;
1906 /* skip the characters between rectStart and rectEnd */
1907 for(; *linePtr
!='\0' && inIndent
< rectEnd
; linePtr
++)
1908 inIndent
+= BufCharWidth(*linePtr
, inIndent
, tabDist
, nullSubsChar
);
1909 postRectIndent
= inIndent
;
1911 /* After this inIndent is dead and linePtr is supposed to point at the
1912 character just past the last character that will be altered by
1913 the overlay, whether that's a \t or otherwise. postRectIndent is
1914 the position at which that character is supposed to appear */
1916 /* If there's no text after rectStart and no text to insert, that's all */
1917 if (*insLine
== '\0' && *linePtr
== '\0') {
1918 *outLen
= *endOffset
= outPtr
- outStr
;
1922 /* pad out to rectStart if text is too short */
1923 if (outIndent
< rectStart
) {
1924 addPadding(outPtr
, outIndent
, rectStart
, tabDist
, useTabs
, nullSubsChar
,
1928 outIndent
= rectStart
;
1930 /* Copy the text from "insLine" (if any), recalculating the tabs as if
1931 the inserted string began at column 0 to its new column destination */
1932 if (*insLine
!= '\0') {
1933 retabbedStr
= realignTabs(insLine
, 0, rectStart
, tabDist
, useTabs
,
1934 nullSubsChar
, &len
);
1935 for (c
=retabbedStr
; *c
!='\0'; c
++) {
1937 len
= BufCharWidth(*c
, outIndent
, tabDist
, nullSubsChar
);
1940 XtFree(retabbedStr
);
1943 /* If the original line did not extend past "rectStart", that's all */
1944 if (*linePtr
== '\0') {
1945 *outLen
= *endOffset
= outPtr
- outStr
;
1949 /* Pad out to rectEnd + (additional original offset
1950 due to non-breaking character at right boundary) */
1951 addPadding(outPtr
, outIndent
, postRectIndent
, tabDist
, useTabs
,
1952 nullSubsChar
, &len
);
1954 outIndent
= postRectIndent
;
1956 /* copy the text beyond "rectEnd" */
1957 strcpy(outPtr
, linePtr
);
1958 *endOffset
= outPtr
- outStr
;
1959 *outLen
= (outPtr
- outStr
) + strlen(linePtr
);
1962 static void setSelection(selection
*sel
, int start
, int end
)
1964 sel
->selected
= start
!= end
;
1965 sel
->zeroWidth
= (start
== end
) ? 1 : 0;
1966 sel
->rectangular
= False
;
1967 sel
->start
= min(start
, end
);
1968 sel
->end
= max(start
, end
);
1971 static void setRectSelect(selection
*sel
, int start
, int end
,
1972 int rectStart
, int rectEnd
)
1974 sel
->selected
= rectStart
< rectEnd
;
1975 sel
->zeroWidth
= (rectStart
== rectEnd
) ? 1 : 0;
1976 sel
->rectangular
= True
;
1979 sel
->rectStart
= rectStart
;
1980 sel
->rectEnd
= rectEnd
;
1983 static int getSelectionPos(selection
*sel
, int *start
, int *end
,
1984 int *isRect
, int *rectStart
, int *rectEnd
)
1986 /* Always fill in the parameters (zero-width can be requested too). */
1987 *isRect
= sel
->rectangular
;
1988 *start
= sel
->start
;
1990 if (sel
->rectangular
) {
1991 *rectStart
= sel
->rectStart
;
1992 *rectEnd
= sel
->rectEnd
;
1994 return sel
->selected
;
1997 static char *getSelectionText(textBuffer
*buf
, selection
*sel
)
1999 int start
, end
, isRect
, rectStart
, rectEnd
;
2002 /* If there's no selection, return an allocated empty string */
2003 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
)) {
2009 /* If the selection is not rectangular, return the selected range */
2011 return BufGetTextInRect(buf
, start
, end
, rectStart
, rectEnd
);
2013 return BufGetRange(buf
, start
, end
);
2016 static void removeSelected(textBuffer
*buf
, selection
*sel
)
2019 int isRect
, rectStart
, rectEnd
;
2021 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
))
2024 BufRemoveRect(buf
, start
, end
, rectStart
, rectEnd
);
2026 BufRemove(buf
, start
, end
);
2029 static void replaceSelected(textBuffer
*buf
, selection
*sel
, const char *text
)
2031 int start
, end
, isRect
, rectStart
, rectEnd
;
2032 selection oldSelection
= *sel
;
2034 /* If there's no selection, return */
2035 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
))
2038 /* Do the appropriate type of replace */
2040 BufReplaceRect(buf
, start
, end
, rectStart
, rectEnd
, text
);
2042 BufReplace(buf
, start
, end
, text
);
2044 /* Unselect (happens automatically in BufReplace, but BufReplaceRect
2045 can't detect when the contents of a selection goes away) */
2046 sel
->selected
= False
;
2047 redisplaySelection(buf
, &oldSelection
, sel
);
2050 static void addPadding(char *string
, int startIndent
, int toIndent
,
2051 int tabDist
, int useTabs
, char nullSubsChar
, int *charsAdded
)
2056 indent
= startIndent
;
2059 while (indent
< toIndent
) {
2060 len
= BufCharWidth('\t', indent
, tabDist
, nullSubsChar
);
2061 if (len
> 1 && indent
+ len
<= toIndent
) {
2070 while (indent
< toIndent
) {
2075 *charsAdded
= outPtr
- string
;
2079 ** Call the stored modify callback procedure(s) for this buffer to update the
2080 ** changed area(s) on the screen and any other listeners.
2082 static void callModifyCBs(textBuffer
*buf
, int pos
, int nDeleted
,
2083 int nInserted
, int nRestyled
, char *deletedText
)
2087 for (i
=0; i
<buf
->nModifyProcs
; i
++)
2088 (*buf
->modifyProcs
[i
])(pos
, nInserted
, nDeleted
, nRestyled
,
2089 deletedText
, buf
->cbArgs
[i
]);
2093 ** Call the stored pre-delete callback procedure(s) for this buffer to update
2094 ** the changed area(s) on the screen and any other listeners.
2096 static void callPreDeleteCBs(textBuffer
*buf
, int pos
, int nDeleted
)
2100 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++)
2101 (*buf
->preDeleteProcs
[i
])(pos
, nDeleted
, buf
->preDeleteCbArgs
[i
]);
2105 ** Call the stored redisplay procedure(s) for this buffer to update the
2106 ** screen for a change in a selection.
2108 static void redisplaySelection(textBuffer
*buf
, selection
*oldSelection
,
2109 selection
*newSelection
)
2111 int oldStart
, oldEnd
, newStart
, newEnd
, ch1Start
, ch1End
, ch2Start
, ch2End
;
2113 /* If either selection is rectangular, add an additional character to
2114 the end of the selection to request the redraw routines to wipe out
2115 the parts of the selection beyond the end of the line */
2116 oldStart
= oldSelection
->start
;
2117 newStart
= newSelection
->start
;
2118 oldEnd
= oldSelection
->end
;
2119 newEnd
= newSelection
->end
;
2120 if (oldSelection
->rectangular
)
2122 if (newSelection
->rectangular
)
2125 /* If the old or new selection is unselected, just redisplay the
2126 single area that is (was) selected and return */
2127 if (!oldSelection
->selected
&& !newSelection
->selected
)
2129 if (!oldSelection
->selected
) {
2130 callModifyCBs(buf
, newStart
, 0, 0, newEnd
-newStart
, NULL
);
2133 if (!newSelection
->selected
) {
2134 callModifyCBs(buf
, oldStart
, 0, 0, oldEnd
-oldStart
, NULL
);
2138 /* If the selection changed from normal to rectangular or visa versa, or
2139 if a rectangular selection changed boundaries, redisplay everything */
2140 if ((oldSelection
->rectangular
&& !newSelection
->rectangular
) ||
2141 (!oldSelection
->rectangular
&& newSelection
->rectangular
) ||
2142 (oldSelection
->rectangular
&& (
2143 (oldSelection
->rectStart
!= newSelection
->rectStart
) ||
2144 (oldSelection
->rectEnd
!= newSelection
->rectEnd
)))) {
2145 callModifyCBs(buf
, min(oldStart
, newStart
), 0, 0,
2146 max(oldEnd
, newEnd
) - min(oldStart
, newStart
), NULL
);
2150 /* If the selections are non-contiguous, do two separate updates
2152 if (oldEnd
< newStart
|| newEnd
< oldStart
) {
2153 callModifyCBs(buf
, oldStart
, 0, 0, oldEnd
-oldStart
, NULL
);
2154 callModifyCBs(buf
, newStart
, 0, 0, newEnd
-newStart
, NULL
);
2158 /* Otherwise, separate into 3 separate regions: ch1, and ch2 (the two
2159 changed areas), and the unchanged area of their intersection,
2160 and update only the changed area(s) */
2161 ch1Start
= min(oldStart
, newStart
);
2162 ch2End
= max(oldEnd
, newEnd
);
2163 ch1End
= max(oldStart
, newStart
);
2164 ch2Start
= min(oldEnd
, newEnd
);
2165 if (ch1Start
!= ch1End
)
2166 callModifyCBs(buf
, ch1Start
, 0, 0, ch1End
-ch1Start
, NULL
);
2167 if (ch2Start
!= ch2End
)
2168 callModifyCBs(buf
, ch2Start
, 0, 0, ch2End
-ch2Start
, NULL
);
2171 static void moveGap(textBuffer
*buf
, int pos
)
2173 int gapLen
= buf
->gapEnd
- buf
->gapStart
;
2175 if (pos
> buf
->gapStart
)
2176 memmove(&buf
->buf
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
2177 pos
- buf
->gapStart
);
2179 memmove(&buf
->buf
[pos
+ gapLen
], &buf
->buf
[pos
], buf
->gapStart
- pos
);
2180 buf
->gapEnd
+= pos
- buf
->gapStart
;
2181 buf
->gapStart
+= pos
- buf
->gapStart
;
2185 ** reallocate the text storage in "buf" to have a gap starting at "newGapStart"
2186 ** and a gap size of "newGapLen", preserving the buffer's current contents.
2188 static void reallocateBuf(textBuffer
*buf
, int newGapStart
, int newGapLen
)
2193 newBuf
= XtMalloc(buf
->length
+ newGapLen
);
2194 newGapEnd
= newGapStart
+ newGapLen
;
2195 if (newGapStart
<= buf
->gapStart
) {
2196 memcpy(newBuf
, buf
->buf
, newGapStart
);
2197 memcpy(&newBuf
[newGapEnd
], &buf
->buf
[newGapStart
],
2198 buf
->gapStart
- newGapStart
);
2199 memcpy(&newBuf
[newGapEnd
+ buf
->gapStart
- newGapStart
],
2200 &buf
->buf
[buf
->gapEnd
], buf
->length
- buf
->gapStart
);
2201 } else { /* newGapStart > buf->gapStart */
2202 memcpy(newBuf
, buf
->buf
, buf
->gapStart
);
2203 memcpy(&newBuf
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
2204 newGapStart
- buf
->gapStart
);
2205 memcpy(&newBuf
[newGapEnd
],
2206 &buf
->buf
[buf
->gapEnd
+ newGapStart
- buf
->gapStart
],
2207 buf
->length
- newGapStart
);
2211 buf
->gapStart
= newGapStart
;
2212 buf
->gapEnd
= newGapEnd
;
2214 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
2219 ** Update all of the selections in "buf" for changes in the buffer's text
2221 static void updateSelections(textBuffer
*buf
, int pos
, int nDeleted
,
2224 updateSelection(&buf
->primary
, pos
, nDeleted
, nInserted
);
2225 updateSelection(&buf
->secondary
, pos
, nDeleted
, nInserted
);
2226 updateSelection(&buf
->highlight
, pos
, nDeleted
, nInserted
);
2230 ** Update an individual selection for changes in the corresponding text
2232 static void updateSelection(selection
*sel
, int pos
, int nDeleted
,
2235 if ((!sel
->selected
&& !sel
->zeroWidth
) || pos
> sel
->end
)
2237 if (pos
+nDeleted
<= sel
->start
) {
2238 sel
->start
+= nInserted
- nDeleted
;
2239 sel
->end
+= nInserted
- nDeleted
;
2240 } else if (pos
<= sel
->start
&& pos
+nDeleted
>= sel
->end
) {
2243 sel
->selected
= False
;
2244 sel
->zeroWidth
= False
;
2245 } else if (pos
<= sel
->start
&& pos
+nDeleted
< sel
->end
) {
2247 sel
->end
= nInserted
+ sel
->end
- nDeleted
;
2248 } else if (pos
< sel
->end
) {
2249 sel
->end
+= nInserted
- nDeleted
;
2250 if (sel
->end
<= sel
->start
)
2251 sel
->selected
= False
;
2256 ** Search forwards in buffer "buf" for character "searchChar", starting
2257 ** with the character "startPos", and returning the result in "foundPos"
2258 ** returns True if found, False if not. (The difference between this and
2259 ** BufSearchForward is that it's optimized for single characters. The
2260 ** overall performance of the text widget is dependent on its ability to
2261 ** count lines quickly, hence searching for a single character: newline)
2263 static int searchForward(textBuffer
*buf
, int startPos
, char searchChar
,
2266 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
2269 while (pos
< buf
->gapStart
) {
2270 if (buf
->buf
[pos
] == searchChar
) {
2276 while (pos
< buf
->length
) {
2277 if (buf
->buf
[pos
+ gapLen
] == searchChar
) {
2283 *foundPos
= buf
->length
;
2288 ** Search backwards in buffer "buf" for character "searchChar", starting
2289 ** with the character BEFORE "startPos", returning the result in "foundPos"
2290 ** returns True if found, False if not. (The difference between this and
2291 ** BufSearchBackward is that it's optimized for single characters. The
2292 ** overall performance of the text widget is dependent on its ability to
2293 ** count lines quickly, hence searching for a single character: newline)
2295 static int searchBackward(textBuffer
*buf
, int startPos
, char searchChar
,
2298 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
2300 if (startPos
== 0) {
2304 pos
= startPos
== 0 ? 0 : startPos
- 1;
2305 while (pos
>= buf
->gapStart
) {
2306 if (buf
->buf
[pos
+ gapLen
] == searchChar
) {
2313 if (buf
->buf
[pos
] == searchChar
) {
2324 ** Copy from "text" to end up to but not including newline (or end of "text")
2325 ** and return the copy as the function value, and the length of the line in
2328 static char *copyLine(const char *text
, int *lineLen
)
2334 for (c
=text
; *c
!='\0' && *c
!='\n'; c
++)
2336 outStr
= XtMalloc(len
+ 1);
2337 strncpy(outStr
, text
, len
);
2344 ** Count the number of newlines in a null-terminated text string;
2346 static int countLines(const char *string
)
2351 for (c
=string
; *c
!='\0'; c
++)
2352 if (*c
== '\n') lineCount
++;
2357 ** Measure the width in displayed characters of string "text"
2359 static int textWidth(const char *text
, int tabDist
, char nullSubsChar
)
2361 int width
= 0, maxWidth
= 0;
2364 for (c
=text
; *c
!='\0'; c
++) {
2366 if (width
> maxWidth
)
2370 width
+= BufCharWidth(*c
, width
, tabDist
, nullSubsChar
);
2372 if (width
> maxWidth
)
2378 ** Find the first and last character position in a line withing a rectangular
2379 ** selection (for copying). Includes tabs which cross rectStart, but not
2380 ** control characters which do so. Leaves off tabs which cross rectEnd.
2382 ** Technically, the calling routine should convert tab characters which
2383 ** cross the right boundary of the selection to spaces which line up with
2384 ** the edge of the selection. Unfortunately, the additional memory
2385 ** management required in the parent routine to allow for the changes
2386 ** in string size is not worth all the extra work just for a couple of
2387 ** shifted characters, so if a tab protrudes, just lop it off and hope
2388 ** that there are other characters in the selection to establish the right
2389 ** margin for subsequent columnar pastes of this data.
2391 static void findRectSelBoundariesForCopy(textBuffer
*buf
, int lineStartPos
,
2392 int rectStart
, int rectEnd
, int *selStart
, int *selEnd
)
2394 int pos
, width
, indent
= 0;
2397 /* find the start of the selection */
2398 for (pos
=lineStartPos
; pos
<buf
->length
; pos
++) {
2399 c
= BufGetCharacter(buf
, pos
);
2402 width
= BufCharWidth(c
, indent
, buf
->tabDist
, buf
->nullSubsChar
);
2403 if (indent
+ width
> rectStart
) {
2404 if (indent
!= rectStart
&& c
!= '\t') {
2415 for (; pos
<buf
->length
; pos
++) {
2416 c
= BufGetCharacter(buf
, pos
);
2419 width
= BufCharWidth(c
, indent
, buf
->tabDist
, buf
->nullSubsChar
);
2421 if (indent
> rectEnd
) {
2422 if (indent
-width
!= rectEnd
&& c
!= '\t')
2431 ** Adjust the space and tab characters from string "text" so that non-white
2432 ** characters remain stationary when the text is shifted from starting at
2433 ** "origIndent" to starting at "newIndent". Returns an allocated string
2434 ** which must be freed by the caller with XtFree.
2436 static char *realignTabs(const char *text
, int origIndent
, int newIndent
,
2437 int tabDist
, int useTabs
, char nullSubsChar
, int *newLength
)
2439 char *expStr
, *outStr
;
2442 /* If the tabs settings are the same, retain original tabs */
2443 if (origIndent
% tabDist
== newIndent
%tabDist
) {
2445 outStr
= XtMalloc(len
+ 1);
2446 strcpy(outStr
, text
);
2451 /* If the tab settings are not the same, brutally convert tabs to
2452 spaces, then back to tabs in the new position */
2453 expStr
= expandTabs(text
, origIndent
, tabDist
, nullSubsChar
, &len
);
2458 outStr
= unexpandTabs(expStr
, newIndent
, tabDist
, nullSubsChar
, newLength
);
2464 ** Expand tabs to spaces for a block of text. The additional parameter
2465 ** "startIndent" if nonzero, indicates that the text is a rectangular selection
2466 ** beginning at column "startIndent"
2468 static char *expandTabs(const char *text
, int startIndent
, int tabDist
,
2469 char nullSubsChar
, int *newLen
)
2471 char *outStr
, *outPtr
;
2473 int indent
, len
, outLen
= 0;
2475 /* rehearse the expansion to figure out length for output string */
2476 indent
= startIndent
;
2477 for (c
=text
; *c
!='\0'; c
++) {
2479 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2482 } else if (*c
== '\n') {
2483 indent
= startIndent
;
2486 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2491 /* do the expansion */
2492 outStr
= XtMalloc(outLen
+1);
2494 indent
= startIndent
;
2495 for (c
=text
; *c
!= '\0'; c
++) {
2497 len
= BufExpandCharacter(*c
, indent
, outPtr
, tabDist
, nullSubsChar
);
2500 } else if (*c
== '\n') {
2501 indent
= startIndent
;
2504 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2508 outStr
[outLen
] = '\0';
2514 ** Convert sequences of spaces into tabs. The threshold for conversion is
2515 ** when 3 or more spaces can be converted into a single tab, this avoids
2516 ** converting double spaces after a period withing a block of text.
2518 static char *unexpandTabs(const char *text
, int startIndent
, int tabDist
,
2519 char nullSubsChar
, int *newLen
)
2521 char *outStr
, *outPtr
, expandedChar
[MAX_EXP_CHAR_LEN
];
2525 outStr
= XtMalloc(strlen(text
)+1);
2527 indent
= startIndent
;
2528 for (c
=text
; *c
!='\0';) {
2530 len
= BufExpandCharacter('\t', indent
, expandedChar
, tabDist
,
2532 if (len
>= 3 && !strncmp(c
, expandedChar
, len
)) {
2540 } else if (*c
== '\n') {
2541 indent
= startIndent
;
2549 *newLen
= outPtr
- outStr
;
2553 static int max(int i1
, int i2
)
2555 return i1
>= i2
? i1
: i2
;
2558 static int min(int i1
, int i2
)
2560 return i1
<= i2
? i1
: i2
;