1 static const char CVSID
[] = "$Id: textBuf.c,v 1.27 2003/05/02 18:18:45 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"
47 #define PREFERRED_GAP_SIZE 80 /* Initial size for the buffer gap (empty space
48 in the buffer where text might be inserted
49 if the user is typing sequential chars) */
51 static void histogramCharacters(const char *string
, int length
, char hist
[256],
53 static void subsChars(char *string
, int length
, char fromChar
, char toChar
);
54 static char chooseNullSubsChar(char hist
[256]);
55 static int insert(textBuffer
*buf
, int pos
, const char *text
);
56 static void delete(textBuffer
*buf
, int start
, int end
);
57 static void deleteRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
58 int rectEnd
, int *replaceLen
, int *endPos
);
59 static void insertCol(textBuffer
*buf
, int column
, int startPos
, const char *insText
,
60 int *nDeleted
, int *nInserted
, int *endPos
);
61 static void overlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
62 int rectEnd
, const char *insText
, int *nDeleted
, int *nInserted
, int *endPos
);
63 static void insertColInLine(const char *line
, const char *insLine
, int column
, int insWidth
,
64 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
66 static void deleteRectFromLine(const char *line
, int rectStart
, int rectEnd
,
67 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
69 static void overlayRectInLine(const char *line
, const char *insLine
, int rectStart
,
70 int rectEnd
, int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
,
71 int *outLen
, int *endOffset
);
72 static void callPreDeleteCBs(textBuffer
*buf
, int pos
, int nDeleted
);
73 static void callModifyCBs(textBuffer
*buf
, int pos
, int nDeleted
,
74 int nInserted
, int nRestyled
, char *deletedText
);
75 static void redisplaySelection(textBuffer
*buf
, selection
*oldSelection
,
76 selection
*newSelection
);
77 static void moveGap(textBuffer
*buf
, int pos
);
78 static void reallocateBuf(textBuffer
*buf
, int newGapStart
, int newGapLen
);
79 static void setSelection(selection
*sel
, int start
, int end
);
80 static void setRectSelect(selection
*sel
, int start
, int end
,
81 int rectStart
, int rectEnd
);
82 static void updateSelections(textBuffer
*buf
, int pos
, int nDeleted
,
84 static void updateSelection(selection
*sel
, int pos
, int nDeleted
,
86 static int getSelectionPos(selection
*sel
, int *start
, int *end
,
87 int *isRect
, int *rectStart
, int *rectEnd
);
88 static char *getSelectionText(textBuffer
*buf
, selection
*sel
);
89 static void removeSelected(textBuffer
*buf
, selection
*sel
);
90 static void replaceSelected(textBuffer
*buf
, selection
*sel
, const char *text
);
91 static void addPadding(char *string
, int startIndent
, int toIndent
,
92 int tabDist
, int useTabs
, char nullSubsChar
, int *charsAdded
);
93 static int searchForward(textBuffer
*buf
, int startPos
, char searchChar
,
95 static int searchBackward(textBuffer
*buf
, int startPos
, char searchChar
,
97 static char *copyLine(const char *text
, int *lineLen
);
98 static int countLines(const char *string
);
99 static int textWidth(const char *text
, int tabDist
, char nullSubsChar
);
100 static void findRectSelBoundariesForCopy(textBuffer
*buf
, int lineStartPos
,
101 int rectStart
, int rectEnd
, int *selStart
, int *selEnd
);
102 static char *realignTabs(const char *text
, int origIndent
, int newIndent
,
103 int tabDist
, int useTabs
, char nullSubsChar
, int *newLength
);
104 static char *expandTabs(const char *text
, int startIndent
, int tabDist
,
105 char nullSubsChar
, int *newLen
);
106 static char *unexpandTabs(const char *text
, int startIndent
, int tabDist
,
107 char nullSubsChar
, int *newLen
);
108 static int max(int i1
, int i2
);
109 static int min(int i1
, int i2
);
112 static const char *ControlCodeTable
[64] = {
113 "nul", "soh", "stx", "etx", "sel", "ht", "rnl", "del",
114 "ge", "sps", "rpt", "vt", "ff", "cr", "so", "si",
115 "dle", "dc1", "dc2", "dc3", "res", "nl", "bs", "poc",
116 "can", "em", "ubs", "cu1", "ifs", "igs", "irs", "ius",
117 "ds", "sos", "fs", "wus", "byp", "lf", "etb", "esc",
118 "sa", "sfe", "sm", "csp", "mfa", "enq", "ack", "bel",
119 "x30", "x31", "syn", "ir", "pp", "trn", "nbs", "eot",
120 "sbs", "it", "rff", "cu3", "dc4", "nak", "x3e", "sub"};
122 static const char *ControlCodeTable
[32] = {
123 "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
124 "bs", "ht", "nl", "vt", "np", "cr", "so", "si",
125 "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
126 "can", "em", "sub", "esc", "fs", "gs", "rs", "us"};
130 ** Create an empty text buffer
132 textBuffer
*BufCreate(void)
134 textBuffer
*buf
= BufCreatePreallocated(0);
135 BufAddModifyCB(buf
, RangesetBufModifiedCB
, buf
);
140 ** Create an empty text buffer of a pre-determined size (use this to
141 ** avoid unnecessary re-allocation if you know exactly how much the buffer
144 textBuffer
*BufCreatePreallocated(int requestedSize
)
148 buf
= (textBuffer
*)XtMalloc(sizeof(textBuffer
));
150 buf
->buf
= XtMalloc(requestedSize
+ PREFERRED_GAP_SIZE
);
152 buf
->gapEnd
= PREFERRED_GAP_SIZE
;
155 buf
->primary
.selected
= False
;
156 buf
->primary
.zeroWidth
= False
;
157 buf
->primary
.rectangular
= False
;
158 buf
->primary
.start
= buf
->primary
.end
= 0;
159 buf
->secondary
.selected
= False
;
160 buf
->secondary
.zeroWidth
= False
;
161 buf
->secondary
.start
= buf
->secondary
.end
= 0;
162 buf
->secondary
.rectangular
= False
;
163 buf
->highlight
.selected
= False
;
164 buf
->highlight
.zeroWidth
= False
;
165 buf
->highlight
.start
= buf
->highlight
.end
= 0;
166 buf
->highlight
.rectangular
= False
;
167 buf
->modifyProcs
= NULL
;
169 buf
->nModifyProcs
= 0;
170 buf
->preDeleteProcs
= NULL
;
171 buf
->preDeleteCbArgs
= NULL
;
172 buf
->nPreDeleteProcs
= 0;
173 buf
->nullSubsChar
= '\0';
175 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
177 buf
->rangesetTable
= NULL
;
182 ** Free a text buffer
184 void BufFree(textBuffer
*buf
)
187 if (buf
->nModifyProcs
!= 0) {
188 XtFree((char *)buf
->modifyProcs
);
189 XtFree((char *)buf
->cbArgs
);
191 if (buf
->rangesetTable
)
192 RangesetTableFree(buf
->rangesetTable
);
193 if (buf
->nPreDeleteProcs
!= 0) {
194 XtFree((char *)buf
->preDeleteProcs
);
195 XtFree((char *)buf
->preDeleteCbArgs
);
201 ** Get the entire contents of a text buffer. Memory is allocated to contain
202 ** the returned string, which the caller must free.
204 char *BufGetAll(textBuffer
*buf
)
208 text
= XtMalloc(buf
->length
+1);
209 memcpy(text
, buf
->buf
, buf
->gapStart
);
210 memcpy(&text
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
211 buf
->length
- buf
->gapStart
);
212 text
[buf
->length
] = '\0';
217 ** Replace the entire contents of the text buffer
219 void BufSetAll(textBuffer
*buf
, const char *text
)
221 int length
, deletedLength
;
223 length
= strlen(text
);
225 callPreDeleteCBs(buf
, 0, buf
->length
);
227 /* Save information for redisplay, and get rid of the old buffer */
228 deletedText
= BufGetAll(buf
);
229 deletedLength
= buf
->length
;
232 /* Start a new buffer with a gap of PREFERRED_GAP_SIZE in the center */
233 buf
->buf
= XtMalloc(length
+ PREFERRED_GAP_SIZE
);
234 buf
->length
= length
;
235 buf
->gapStart
= length
/2;
236 buf
->gapEnd
= buf
->gapStart
+ PREFERRED_GAP_SIZE
;
237 memcpy(buf
->buf
, text
, buf
->gapStart
);
238 memcpy(&buf
->buf
[buf
->gapEnd
], &text
[buf
->gapStart
], length
-buf
->gapStart
);
240 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
243 /* Zero all of the existing selections */
244 updateSelections(buf
, 0, deletedLength
, 0);
246 /* Call the saved display routine(s) to update the screen */
247 callModifyCBs(buf
, 0, deletedLength
, length
, 0, deletedText
);
252 ** Return a copy of the text between "start" and "end" character positions
253 ** from text buffer "buf". Positions start at 0, and the range does not
254 ** include the character pointed to by "end"
256 char *BufGetRange(textBuffer
*buf
, int start
, int end
)
259 int length
, part1Length
;
261 /* Make sure start and end are ok, and allocate memory for returned string.
262 If start is bad, return "", if end is bad, adjust it. */
263 if (start
< 0 || start
> buf
->length
) {
273 if (end
> buf
->length
)
275 length
= end
- start
;
276 text
= XtMalloc(length
+1);
278 /* Copy the text from the buffer to the returned string */
279 if (end
<= buf
->gapStart
) {
280 memcpy(text
, &buf
->buf
[start
], length
);
281 } else if (start
>= buf
->gapStart
) {
282 memcpy(text
, &buf
->buf
[start
+(buf
->gapEnd
-buf
->gapStart
)], length
);
284 part1Length
= buf
->gapStart
- start
;
285 memcpy(text
, &buf
->buf
[start
], part1Length
);
286 memcpy(&text
[part1Length
], &buf
->buf
[buf
->gapEnd
], length
-part1Length
);
293 ** Return the character at buffer position "pos". Positions start at 0.
295 char BufGetCharacter(textBuffer
*buf
, int pos
)
297 if (pos
< 0 || pos
>= buf
->length
)
299 if (pos
< buf
->gapStart
)
300 return buf
->buf
[pos
];
302 return buf
->buf
[pos
+ buf
->gapEnd
-buf
->gapStart
];
306 ** Insert null-terminated string "text" at position "pos" in "buf"
308 void BufInsert(textBuffer
*buf
, int pos
, const char *text
)
312 /* if pos is not contiguous to existing text, make it */
313 if (pos
> buf
->length
) pos
= buf
->length
;
314 if (pos
< 0 ) pos
= 0;
316 /* Even if nothing is deleted, we must call these callbacks */
317 callPreDeleteCBs(buf
, pos
, 0);
319 /* insert and redisplay */
320 nInserted
= insert(buf
, pos
, text
);
321 buf
->cursorPosHint
= pos
+ nInserted
;
322 callModifyCBs(buf
, pos
, 0, nInserted
, 0, NULL
);
326 ** Delete the characters between "start" and "end", and insert the
327 ** null-terminated string "text" in their place in in "buf"
329 void BufReplace(textBuffer
*buf
, int start
, int end
, const char *text
)
332 int nInserted
= strlen(text
);
334 callPreDeleteCBs(buf
, start
, end
-start
);
335 deletedText
= BufGetRange(buf
, start
, end
);
336 delete(buf
, start
, end
);
337 insert(buf
, start
, text
);
338 buf
->cursorPosHint
= start
+ nInserted
;
339 callModifyCBs(buf
, start
, end
-start
, nInserted
, 0, deletedText
);
343 void BufRemove(textBuffer
*buf
, int start
, int end
)
347 /* Make sure the arguments make sense */
353 if (start
> buf
->length
) start
= buf
->length
;
354 if (start
< 0) start
= 0;
355 if (end
> buf
->length
) end
= buf
->length
;
356 if (end
< 0) end
= 0;
358 callPreDeleteCBs(buf
, start
, end
-start
);
359 /* Remove and redisplay */
360 deletedText
= BufGetRange(buf
, start
, end
);
361 delete(buf
, start
, end
);
362 buf
->cursorPosHint
= start
;
363 callModifyCBs(buf
, start
, end
-start
, 0, 0, deletedText
);
367 void BufCopyFromBuf(textBuffer
*fromBuf
, textBuffer
*toBuf
, int fromStart
,
368 int fromEnd
, int toPos
)
370 int length
= fromEnd
- fromStart
;
373 /* Prepare the buffer to receive the new text. If the new text fits in
374 the current buffer, just move the gap (if necessary) to where
375 the text should be inserted. If the new text is too large, reallocate
376 the buffer with a gap large enough to accomodate the new text and a
377 gap of PREFERRED_GAP_SIZE */
378 if (length
> toBuf
->gapEnd
- toBuf
->gapStart
)
379 reallocateBuf(toBuf
, toPos
, length
+ PREFERRED_GAP_SIZE
);
380 else if (toPos
!= toBuf
->gapStart
)
381 moveGap(toBuf
, toPos
);
383 /* Insert the new text (toPos now corresponds to the start of the gap) */
384 if (fromEnd
<= fromBuf
->gapStart
) {
385 memcpy(&toBuf
->buf
[toPos
], &fromBuf
->buf
[fromStart
], length
);
386 } else if (fromStart
>= fromBuf
->gapStart
) {
387 memcpy(&toBuf
->buf
[toPos
],
388 &fromBuf
->buf
[fromStart
+(fromBuf
->gapEnd
-fromBuf
->gapStart
)],
391 part1Length
= fromBuf
->gapStart
- fromStart
;
392 memcpy(&toBuf
->buf
[toPos
], &fromBuf
->buf
[fromStart
], part1Length
);
393 memcpy(&toBuf
->buf
[toPos
+part1Length
], &fromBuf
->buf
[fromBuf
->gapEnd
],
396 toBuf
->gapStart
+= length
;
397 toBuf
->length
+= length
;
398 updateSelections(toBuf
, toPos
, 0, length
);
402 ** Insert "text" columnwise into buffer starting at displayed character
403 ** position "column" on the line beginning at "startPos". Opens a rectangular
404 ** space the width and height of "text", by moving all text to the right of
405 ** "column" right. If charsInserted and charsDeleted are not NULL, the
406 ** number of characters inserted and deleted in the operation (beginning
407 ** at startPos) are returned in these arguments
409 void BufInsertCol(textBuffer
*buf
, int column
, int startPos
, const char *text
,
410 int *charsInserted
, int *charsDeleted
)
412 int nLines
, lineStartPos
, nDeleted
, insertDeleted
, nInserted
;
415 nLines
= countLines(text
);
416 lineStartPos
= BufStartOfLine(buf
, startPos
);
417 nDeleted
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, startPos
, nLines
)) -
419 callPreDeleteCBs(buf
, lineStartPos
, nDeleted
);
420 deletedText
= BufGetRange(buf
, lineStartPos
, lineStartPos
+ nDeleted
);
421 insertCol(buf
, column
, lineStartPos
, text
, &insertDeleted
, &nInserted
,
422 &buf
->cursorPosHint
);
423 if (nDeleted
!= insertDeleted
)
424 fprintf(stderr
, "NEdit internal consistency check ins1 failed");
425 callModifyCBs(buf
, lineStartPos
, nDeleted
, nInserted
, 0, deletedText
);
427 if (charsInserted
!= NULL
)
428 *charsInserted
= nInserted
;
429 if (charsDeleted
!= NULL
)
430 *charsDeleted
= nDeleted
;
434 ** Overlay "text" between displayed character positions "rectStart" and
435 ** "rectEnd" on the line beginning at "startPos". If charsInserted and
436 ** charsDeleted are not NULL, the number of characters inserted and deleted
437 ** in the operation (beginning at startPos) are returned in these arguments.
438 ** If rectEnd equals -1, the width of the inserted text is measured first.
440 void BufOverlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
441 int rectEnd
, const char *text
, int *charsInserted
, int *charsDeleted
)
443 int nLines
, lineStartPos
, nDeleted
, insertDeleted
, nInserted
;
446 nLines
= countLines(text
);
447 lineStartPos
= BufStartOfLine(buf
, startPos
);
449 rectEnd
= rectStart
+ textWidth(text
, buf
->tabDist
, buf
->nullSubsChar
);
450 lineStartPos
= BufStartOfLine(buf
, startPos
);
451 nDeleted
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, startPos
, nLines
)) -
453 callPreDeleteCBs(buf
, lineStartPos
, nDeleted
);
454 deletedText
= BufGetRange(buf
, lineStartPos
, lineStartPos
+ nDeleted
);
455 overlayRect(buf
, lineStartPos
, rectStart
, rectEnd
, text
, &insertDeleted
,
456 &nInserted
, &buf
->cursorPosHint
);
457 if (nDeleted
!= insertDeleted
)
458 fprintf(stderr
, "NEdit internal consistency check ovly1 failed");
459 callModifyCBs(buf
, lineStartPos
, nDeleted
, nInserted
, 0, deletedText
);
461 if (charsInserted
!= NULL
)
462 *charsInserted
= nInserted
;
463 if (charsDeleted
!= NULL
)
464 *charsDeleted
= nDeleted
;
468 ** Replace a rectangular area in buf, given by "start", "end", "rectStart",
469 ** and "rectEnd", with "text". If "text" is vertically longer than the
470 ** rectangle, add extra lines to make room for it.
472 void BufReplaceRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
473 int rectEnd
, const char *text
)
477 int i
, nInsertedLines
, nDeletedLines
, insLen
, hint
;
478 int insertDeleted
, insertInserted
, deleteInserted
;
481 /* Make sure start and end refer to complete lines, since the
482 columnar delete and insert operations will replace whole lines */
483 start
= BufStartOfLine(buf
, start
);
484 end
= BufEndOfLine(buf
, end
);
486 callPreDeleteCBs(buf
, start
, end
-start
);
488 /* If more lines will be deleted than inserted, pad the inserted text
489 with newlines to make it as long as the number of deleted lines. This
490 will indent all of the text to the right of the rectangle to the same
491 column. If more lines will be inserted than deleted, insert extra
492 lines in the buffer at the end of the rectangle to make room for the
493 additional lines in "text" */
494 nInsertedLines
= countLines(text
);
495 nDeletedLines
= BufCountLines(buf
, start
, end
);
496 if (nInsertedLines
< nDeletedLines
) {
499 insLen
= strlen(text
);
500 insText
= XtMalloc(insLen
+ nDeletedLines
- nInsertedLines
+ 1);
501 strcpy(insText
, text
);
502 insPtr
= insText
+ insLen
;
503 for (i
=0; i
<nDeletedLines
-nInsertedLines
; i
++)
506 } else if (nDeletedLines
< nInsertedLines
) {
507 linesPadded
= nInsertedLines
-nDeletedLines
;
508 for (i
=0; i
<linesPadded
; i
++)
509 insert(buf
, end
, "\n");
510 } else /* nDeletedLines == nInsertedLines */ {
513 /* Save a copy of the text which will be modified for the modify CBs */
514 deletedText
= BufGetRange(buf
, start
, end
);
516 /* Delete then insert */
517 deleteRect(buf
, start
, end
, rectStart
, rectEnd
, &deleteInserted
, &hint
);
519 insertCol(buf
, rectStart
, start
, insText
, &insertDeleted
, &insertInserted
,
520 &buf
->cursorPosHint
);
524 insertCol(buf
, rectStart
, start
, text
, &insertDeleted
, &insertInserted
,
525 &buf
->cursorPosHint
);
527 /* Figure out how many chars were inserted and call modify callbacks */
528 if (insertDeleted
!= deleteInserted
+ linesPadded
)
529 fprintf(stderr
, "NEdit: internal consistency check repl1 failed\n");
530 callModifyCBs(buf
, start
, end
-start
, insertInserted
, 0, deletedText
);
535 ** Remove a rectangular swath of characters between character positions start
536 ** and end and horizontal displayed-character offsets rectStart and rectEnd.
538 void BufRemoveRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
544 start
= BufStartOfLine(buf
, start
);
545 end
= BufEndOfLine(buf
, end
);
546 callPreDeleteCBs(buf
, start
, end
-start
);
547 deletedText
= BufGetRange(buf
, start
, end
);
548 deleteRect(buf
, start
, end
, rectStart
, rectEnd
, &nInserted
,
549 &buf
->cursorPosHint
);
550 callModifyCBs(buf
, start
, end
-start
, nInserted
, 0, deletedText
);
555 ** Clear a rectangular "hole" out of the buffer between character positions
556 ** start and end and horizontal displayed-character offsets rectStart and
559 void BufClearRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
565 nLines
= BufCountLines(buf
, start
, end
);
566 newlineString
= XtMalloc(nLines
+1);
567 for (i
=0; i
<nLines
; i
++)
568 newlineString
[i
] = '\n';
569 newlineString
[i
] = '\0';
570 BufOverlayRect(buf
, start
, rectStart
, rectEnd
, newlineString
,
572 XtFree(newlineString
);
575 char *BufGetTextInRect(textBuffer
*buf
, int start
, int end
,
576 int rectStart
, int rectEnd
)
578 int lineStart
, selLeft
, selRight
, len
;
579 char *textOut
, *textIn
, *outPtr
, *retabbedStr
;
581 start
= BufStartOfLine(buf
, start
);
582 end
= BufEndOfLine(buf
, end
);
583 textOut
= XtMalloc((end
- start
) + 1);
586 while (lineStart
<= end
) {
587 findRectSelBoundariesForCopy(buf
, lineStart
, rectStart
, rectEnd
,
588 &selLeft
, &selRight
);
589 textIn
= BufGetRange(buf
, selLeft
, selRight
);
590 len
= selRight
- selLeft
;
591 memcpy(outPtr
, textIn
, len
);
594 lineStart
= BufEndOfLine(buf
, selRight
) + 1;
597 if (outPtr
!= textOut
)
598 outPtr
--; /* don't leave trailing newline */
601 /* If necessary, realign the tabs in the selection as if the text were
602 positioned at the left margin */
603 retabbedStr
= realignTabs(textOut
, rectStart
, 0, buf
->tabDist
,
604 buf
->useTabs
, buf
->nullSubsChar
, &len
);
610 ** Get the hardware tab distance used by all displays for this buffer,
611 ** and used in computing offsets for rectangular selection operations.
613 int BufGetTabDistance(textBuffer
*buf
)
619 ** Set the hardware tab distance used by all displays for this buffer,
620 ** and used in computing offsets for rectangular selection operations.
622 void BufSetTabDistance(textBuffer
*buf
, int tabDist
)
626 /* First call the pre-delete callbacks with the previous tab setting
628 callPreDeleteCBs(buf
, 0, buf
->length
);
630 /* Change the tab setting */
631 buf
->tabDist
= tabDist
;
633 /* Force any display routines to redisplay everything (unfortunately,
634 this means copying the whole buffer contents to provide "deletedText" */
635 deletedText
= BufGetAll(buf
);
636 callModifyCBs(buf
, 0, buf
->length
, buf
->length
, 0, deletedText
);
640 void BufCheckDisplay(textBuffer
*buf
, int start
, int end
)
642 /* just to make sure colors in the selected region are up to date */
643 callModifyCBs(buf
, start
, 0, 0, end
-start
, NULL
);
646 void BufSelect(textBuffer
*buf
, int start
, int end
)
648 selection oldSelection
= buf
->primary
;
650 setSelection(&buf
->primary
, start
, end
);
651 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
654 void BufUnselect(textBuffer
*buf
)
656 selection oldSelection
= buf
->primary
;
658 buf
->primary
.selected
= False
;
659 buf
->primary
.zeroWidth
= False
;
660 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
663 void BufRectSelect(textBuffer
*buf
, int start
, int end
, int rectStart
,
666 selection oldSelection
= buf
->primary
;
668 setRectSelect(&buf
->primary
, start
, end
, rectStart
, rectEnd
);
669 redisplaySelection(buf
, &oldSelection
, &buf
->primary
);
672 int BufGetSelectionPos(textBuffer
*buf
, int *start
, int *end
,
673 int *isRect
, int *rectStart
, int *rectEnd
)
675 return getSelectionPos(&buf
->primary
, start
, end
, isRect
, rectStart
,
679 /* Same as above, but also returns TRUE for empty selections */
680 int BufGetEmptySelectionPos(textBuffer
*buf
, int *start
, int *end
,
681 int *isRect
, int *rectStart
, int *rectEnd
)
683 return getSelectionPos(&buf
->primary
, start
, end
, isRect
, rectStart
,
684 rectEnd
) || buf
->primary
.zeroWidth
;
687 char *BufGetSelectionText(textBuffer
*buf
)
689 return getSelectionText(buf
, &buf
->primary
);
692 void BufRemoveSelected(textBuffer
*buf
)
694 removeSelected(buf
, &buf
->primary
);
697 void BufReplaceSelected(textBuffer
*buf
, const char *text
)
699 replaceSelected(buf
, &buf
->primary
, text
);
702 void BufSecondarySelect(textBuffer
*buf
, int start
, int end
)
704 selection oldSelection
= buf
->secondary
;
706 setSelection(&buf
->secondary
, start
, end
);
707 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
710 void BufSecondaryUnselect(textBuffer
*buf
)
712 selection oldSelection
= buf
->secondary
;
714 buf
->secondary
.selected
= False
;
715 buf
->secondary
.zeroWidth
= False
;
716 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
719 void BufSecRectSelect(textBuffer
*buf
, int start
, int end
,
720 int rectStart
, int rectEnd
)
722 selection oldSelection
= buf
->secondary
;
724 setRectSelect(&buf
->secondary
, start
, end
, rectStart
, rectEnd
);
725 redisplaySelection(buf
, &oldSelection
, &buf
->secondary
);
728 int BufGetSecSelectPos(textBuffer
*buf
, int *start
, int *end
,
729 int *isRect
, int *rectStart
, int *rectEnd
)
731 return getSelectionPos(&buf
->secondary
, start
, end
, isRect
, rectStart
,
735 char *BufGetSecSelectText(textBuffer
*buf
)
737 return getSelectionText(buf
, &buf
->secondary
);
740 void BufRemoveSecSelect(textBuffer
*buf
)
742 removeSelected(buf
, &buf
->secondary
);
745 void BufReplaceSecSelect(textBuffer
*buf
, const char *text
)
747 replaceSelected(buf
, &buf
->secondary
, text
);
750 void BufHighlight(textBuffer
*buf
, int start
, int end
)
752 selection oldSelection
= buf
->highlight
;
754 setSelection(&buf
->highlight
, start
, end
);
755 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
758 void BufUnhighlight(textBuffer
*buf
)
760 selection oldSelection
= buf
->highlight
;
762 buf
->highlight
.selected
= False
;
763 buf
->highlight
.zeroWidth
= False
;
764 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
767 void BufRectHighlight(textBuffer
*buf
, int start
, int end
,
768 int rectStart
, int rectEnd
)
770 selection oldSelection
= buf
->highlight
;
772 setRectSelect(&buf
->highlight
, start
, end
, rectStart
, rectEnd
);
773 redisplaySelection(buf
, &oldSelection
, &buf
->highlight
);
776 int BufGetHighlightPos(textBuffer
*buf
, int *start
, int *end
,
777 int *isRect
, int *rectStart
, int *rectEnd
)
779 return getSelectionPos(&buf
->highlight
, start
, end
, isRect
, rectStart
,
783 char *BufGetHighlightText(textBuffer
*buf
)
785 return getSelectionText(buf
, &buf
->highlight
);
789 ** Add a callback routine to be called when the buffer is modified
791 void BufAddModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
794 bufModifyCallbackProc
*newModifyProcs
;
798 newModifyProcs
= (bufModifyCallbackProc
*)
799 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
+1));
800 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
+1));
801 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
802 newModifyProcs
[i
] = buf
->modifyProcs
[i
];
803 newCBArgs
[i
] = buf
->cbArgs
[i
];
805 if (buf
->nModifyProcs
!= 0) {
806 XtFree((char *)buf
->modifyProcs
);
807 XtFree((char *)buf
->cbArgs
);
809 newModifyProcs
[buf
->nModifyProcs
] = bufModifiedCB
;
810 newCBArgs
[buf
->nModifyProcs
] = cbArg
;
812 buf
->modifyProcs
= newModifyProcs
;
813 buf
->cbArgs
= newCBArgs
;
816 void BufRemoveModifyCB(textBuffer
*buf
, bufModifyCallbackProc bufModifiedCB
,
819 int i
, toRemove
= -1;
820 bufModifyCallbackProc
*newModifyProcs
;
823 /* find the matching callback to remove */
824 for (i
=0; i
<buf
->nModifyProcs
; i
++) {
825 if (buf
->modifyProcs
[i
] == bufModifiedCB
&& buf
->cbArgs
[i
] == cbArg
) {
830 if (toRemove
== -1) {
831 fprintf(stderr
, "NEdit Internal Error: Can't find modify CB to remove\n");
835 /* Allocate new lists for remaining callback procs and args (if
838 if (buf
->nModifyProcs
== 0) {
839 buf
->nModifyProcs
= 0;
840 XtFree((char *)buf
->modifyProcs
);
841 buf
->modifyProcs
= NULL
;
842 XtFree((char *)buf
->cbArgs
);
846 newModifyProcs
= (bufModifyCallbackProc
*)
847 XtMalloc(sizeof(bufModifyCallbackProc
*) * (buf
->nModifyProcs
));
848 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nModifyProcs
));
850 /* copy out the remaining members and free the old lists */
851 for (i
=0; i
<toRemove
; i
++) {
852 newModifyProcs
[i
] = buf
->modifyProcs
[i
];
853 newCBArgs
[i
] = buf
->cbArgs
[i
];
855 for (; i
<buf
->nModifyProcs
; i
++) {
856 newModifyProcs
[i
] = buf
->modifyProcs
[i
+1];
857 newCBArgs
[i
] = buf
->cbArgs
[i
+1];
859 XtFree((char *)buf
->modifyProcs
);
860 XtFree((char *)buf
->cbArgs
);
861 buf
->modifyProcs
= newModifyProcs
;
862 buf
->cbArgs
= newCBArgs
;
866 ** Add a callback routine to be called before text is deleted from the buffer.
868 void BufAddPreDeleteCB(textBuffer
*buf
, bufPreDeleteCallbackProc bufPreDeleteCB
,
871 bufPreDeleteCallbackProc
*newPreDeleteProcs
;
875 newPreDeleteProcs
= (bufPreDeleteCallbackProc
*)
876 XtMalloc(sizeof(bufPreDeleteCallbackProc
*) * (buf
->nPreDeleteProcs
+1));
877 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nPreDeleteProcs
+1));
878 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++) {
879 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
];
880 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
];
882 if (buf
->nPreDeleteProcs
!= 0) {
883 XtFree((char *)buf
->preDeleteProcs
);
884 XtFree((char *)buf
->preDeleteCbArgs
);
886 newPreDeleteProcs
[buf
->nPreDeleteProcs
] = bufPreDeleteCB
;
887 newCBArgs
[buf
->nPreDeleteProcs
] = cbArg
;
888 buf
->nPreDeleteProcs
++;
889 buf
->preDeleteProcs
= newPreDeleteProcs
;
890 buf
->preDeleteCbArgs
= newCBArgs
;
893 void BufRemovePreDeleteCB(textBuffer
*buf
, bufPreDeleteCallbackProc bufPreDeleteCB
,
896 int i
, toRemove
= -1;
897 bufPreDeleteCallbackProc
*newPreDeleteProcs
;
900 /* find the matching callback to remove */
901 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++) {
902 if (buf
->preDeleteProcs
[i
] == bufPreDeleteCB
&&
903 buf
->preDeleteCbArgs
[i
] == cbArg
) {
908 if (toRemove
== -1) {
909 fprintf(stderr
, "NEdit Internal Error: Can't find pre-delete CB to remove\n");
913 /* Allocate new lists for remaining callback procs and args (if
915 buf
->nPreDeleteProcs
--;
916 if (buf
->nPreDeleteProcs
== 0) {
917 buf
->nPreDeleteProcs
= 0;
918 XtFree((char *)buf
->preDeleteProcs
);
919 buf
->preDeleteProcs
= NULL
;
920 XtFree((char *)buf
->preDeleteCbArgs
);
921 buf
->preDeleteCbArgs
= NULL
;
924 newPreDeleteProcs
= (bufPreDeleteCallbackProc
*)
925 XtMalloc(sizeof(bufPreDeleteCallbackProc
*) * (buf
->nPreDeleteProcs
));
926 newCBArgs
= (void *)XtMalloc(sizeof(void *) * (buf
->nPreDeleteProcs
));
928 /* copy out the remaining members and free the old lists */
929 for (i
=0; i
<toRemove
; i
++) {
930 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
];
931 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
];
933 for (; i
<buf
->nPreDeleteProcs
; i
++) {
934 newPreDeleteProcs
[i
] = buf
->preDeleteProcs
[i
+1];
935 newCBArgs
[i
] = buf
->preDeleteCbArgs
[i
+1];
937 XtFree((char *)buf
->preDeleteProcs
);
938 XtFree((char *)buf
->preDeleteCbArgs
);
939 buf
->preDeleteProcs
= newPreDeleteProcs
;
940 buf
->preDeleteCbArgs
= newCBArgs
;
944 ** Return the text from the entire line containing position "pos"
946 char *BufGetLineText(textBuffer
*buf
, int pos
)
948 return BufGetRange(buf
, BufStartOfLine(buf
, pos
), BufEndOfLine(buf
, pos
));
952 ** Find the position of the start of the line containing position "pos"
954 int BufStartOfLine(textBuffer
*buf
, int pos
)
958 if (!searchBackward(buf
, pos
, '\n', &startPos
))
965 ** Find the position of the end of the line containing position "pos"
966 ** (which is either a pointer to the newline character ending the line,
967 ** or a pointer to one character beyond the end of the buffer)
969 int BufEndOfLine(textBuffer
*buf
, int pos
)
973 if (!searchForward(buf
, pos
, '\n', &endPos
))
974 endPos
= buf
->length
;
979 ** Get a character from the text buffer expanded into it's screen
980 ** representation (which may be several characters for a tab or a
981 ** control code). Returns the number of characters written to "outStr".
982 ** "indent" is the number of characters from the start of the line
983 ** for figuring tabs. Output string is guranteed to be shorter or
984 ** equal in length to MAX_EXP_CHAR_LEN
986 int BufGetExpandedChar(textBuffer
*buf
, int pos
, int indent
, char *outStr
)
988 return BufExpandCharacter(BufGetCharacter(buf
, pos
), indent
, outStr
,
989 buf
->tabDist
, buf
->nullSubsChar
);
993 ** Expand a single character from the text buffer into it's screen
994 ** representation (which may be several characters for a tab or a
995 ** control code). Returns the number of characters added to "outStr".
996 ** "indent" is the number of characters from the start of the line
997 ** for figuring tabs. Output string is guranteed to be shorter or
998 ** equal in length to MAX_EXP_CHAR_LEN
1000 int BufExpandCharacter(char c
, int indent
, char *outStr
, int tabDist
,
1005 /* Convert tabs to spaces */
1007 nSpaces
= tabDist
- (indent
% tabDist
);
1008 for (i
=0; i
<nSpaces
; i
++)
1013 /* Convert ASCII (and EBCDIC in the __MVS__ (OS/390) case) control
1014 codes to readable character sequences */
1015 if (c
== nullSubsChar
) {
1016 sprintf(outStr
, "<nul>");
1020 if (((unsigned char)c
) <= 63) {
1021 sprintf(outStr
, "<%s>", ControlCodeTable
[(unsigned char)c
]);
1022 return strlen(outStr
);
1025 if (((unsigned char)c
) <= 31) {
1026 sprintf(outStr
, "<%s>", ControlCodeTable
[(unsigned char)c
]);
1027 return strlen(outStr
);
1028 } else if (c
== 127) {
1029 sprintf(outStr
, "<del>");
1034 /* Otherwise, just return the character */
1040 ** Return the length in displayed characters of character "c" expanded
1041 ** for display (as discussed above in BufGetExpandedChar). If the
1042 ** buffer for which the character width is being measured is doing null
1043 ** substitution, nullSubsChar should be passed as that character (or nul
1046 int BufCharWidth(char c
, int indent
, int tabDist
, char nullSubsChar
)
1048 /* Note, this code must parallel that in BufExpandCharacter */
1049 if (c
== nullSubsChar
)
1052 return tabDist
- (indent
% tabDist
);
1053 else if (((unsigned char)c
) <= 31)
1054 return strlen(ControlCodeTable
[(unsigned char)c
]) + 2;
1061 ** Count the number of displayed characters between buffer position
1062 ** "lineStartPos" and "targetPos". (displayed characters are the characters
1063 ** shown on the screen to represent characters in the buffer, where tabs and
1064 ** control characters are expanded)
1066 int BufCountDispChars(textBuffer
*buf
, int lineStartPos
, int targetPos
)
1068 int pos
, charCount
= 0;
1069 char expandedChar
[MAX_EXP_CHAR_LEN
];
1072 while (pos
< targetPos
)
1073 charCount
+= BufGetExpandedChar(buf
, pos
++, charCount
, expandedChar
);
1078 ** Count forward from buffer position "startPos" in displayed characters
1079 ** (displayed characters are the characters shown on the screen to represent
1080 ** characters in the buffer, where tabs and control characters are expanded)
1082 int BufCountForwardDispChars(textBuffer
*buf
, int lineStartPos
, int nChars
)
1084 int pos
, charCount
= 0;
1088 while (charCount
< nChars
&& pos
< buf
->length
) {
1089 c
= BufGetCharacter(buf
, pos
);
1092 charCount
+= BufCharWidth(c
, charCount
, buf
->tabDist
,buf
->nullSubsChar
);
1099 ** Count the number of newlines between startPos and endPos in buffer "buf".
1100 ** The character at position "endPos" is not counted.
1102 int BufCountLines(textBuffer
*buf
, int startPos
, int endPos
)
1104 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1108 while (pos
< buf
->gapStart
) {
1111 if (buf
->buf
[pos
++] == '\n')
1114 while (pos
< buf
->length
) {
1117 if (buf
->buf
[pos
++ + gapLen
] == '\n')
1124 ** Find the first character of the line "nLines" forward from "startPos"
1125 ** in "buf" and return its position
1127 int BufCountForwardNLines(textBuffer
*buf
, int startPos
, int nLines
)
1129 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1136 while (pos
< buf
->gapStart
) {
1137 if (buf
->buf
[pos
++] == '\n') {
1139 if (lineCount
== nLines
)
1143 while (pos
< buf
->length
) {
1144 if (buf
->buf
[pos
++ + gapLen
] == '\n') {
1146 if (lineCount
>= nLines
)
1154 ** Find the position of the first character of the line "nLines" backwards
1155 ** from "startPos" (not counting the character pointed to by "startpos" if
1156 ** that is a newline) in "buf". nLines == 0 means find the beginning of
1159 int BufCountBackwardNLines(textBuffer
*buf
, int startPos
, int nLines
)
1161 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1168 while (pos
>= buf
->gapStart
) {
1169 if (buf
->buf
[pos
+ gapLen
] == '\n') {
1170 if (++lineCount
>= nLines
)
1176 if (buf
->buf
[pos
] == '\n') {
1177 if (++lineCount
>= nLines
)
1186 ** Search forwards in buffer "buf" for characters in "searchChars", starting
1187 ** with the character "startPos", and returning the result in "foundPos"
1188 ** returns True if found, False if not.
1190 int BufSearchForward(textBuffer
*buf
, int startPos
, const char *searchChars
,
1193 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1197 while (pos
< buf
->gapStart
) {
1198 for (c
=searchChars
; *c
!='\0'; c
++) {
1199 if (buf
->buf
[pos
] == *c
) {
1206 while (pos
< buf
->length
) {
1207 for (c
=searchChars
; *c
!='\0'; c
++) {
1208 if (buf
->buf
[pos
+ gapLen
] == *c
) {
1215 *foundPos
= buf
->length
;
1220 ** Search backwards in buffer "buf" for characters in "searchChars", starting
1221 ** with the character BEFORE "startPos", returning the result in "foundPos"
1222 ** returns True if found, False if not.
1224 int BufSearchBackward(textBuffer
*buf
, int startPos
, const char *searchChars
,
1227 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
1230 if (startPos
== 0) {
1234 pos
= startPos
== 0 ? 0 : startPos
- 1;
1235 while (pos
>= buf
->gapStart
) {
1236 for (c
=searchChars
; *c
!='\0'; c
++) {
1237 if (buf
->buf
[pos
+ gapLen
] == *c
) {
1245 for (c
=searchChars
; *c
!='\0'; c
++) {
1246 if (buf
->buf
[pos
] == *c
) {
1258 ** A horrible design flaw in NEdit (from the very start, before we knew that
1259 ** NEdit would become so popular), is that it uses C NULL terminated strings
1260 ** to hold text. This means editing text containing NUL characters is not
1261 ** possible without special consideration. Here is the special consideration.
1262 ** The routines below maintain a special substitution-character which stands
1263 ** in for a null, and translates strings an buffers back and forth from/to
1264 ** the substituted form, figure out what to substitute, and figure out
1265 ** when we're in over our heads and no translation is possible.
1269 ** The primary routine for integrating new text into a text buffer with
1270 ** substitution of another character for ascii nuls. This substitutes null
1271 ** characters in the string in preparation for being copied or replaced
1272 ** into the buffer, and if neccessary, adjusts the buffer as well, in the
1273 ** event that the string contains the character it is currently using for
1274 ** substitution. Returns False, if substitution is no longer possible
1275 ** because all non-printable characters are already in use.
1277 int BufSubstituteNullChars(char *string
, int length
, textBuffer
*buf
)
1279 char histogram
[256];
1281 /* Find out what characters the string contains */
1282 histogramCharacters(string
, length
, histogram
, True
);
1284 /* Does the string contain the null-substitute character? If so, re-
1285 histogram the buffer text to find a character which is ok in both the
1286 string and the buffer, and change the buffer's null-substitution
1287 character. If none can be found, give up and return False */
1288 if (histogram
[(unsigned char)buf
->nullSubsChar
] != 0) {
1289 char *bufString
, newSubsChar
;
1290 bufString
= BufGetAll(buf
);
1291 histogramCharacters(bufString
, buf
->length
, histogram
, False
);
1292 newSubsChar
= chooseNullSubsChar(histogram
);
1293 if (newSubsChar
== '\0') {
1297 subsChars(bufString
, buf
->length
, buf
->nullSubsChar
, newSubsChar
);
1298 delete(buf
, 0, buf
->length
);
1299 insert(buf
, 0, bufString
);
1301 buf
->nullSubsChar
= newSubsChar
;
1304 /* If the string contains null characters, substitute them with the
1305 buffer's null substitution character */
1306 if (histogram
[0] != 0)
1307 subsChars(string
, length
, '\0', buf
->nullSubsChar
);
1312 ** Convert strings obtained from buffers which contain null characters, which
1313 ** have been substituted for by a special substitution character, back to
1314 ** a null-containing string. There is no time penalty for calling this
1315 ** routine if no substitution has been done.
1317 void BufUnsubstituteNullChars(char *string
, textBuffer
*buf
)
1319 register char *c
, subsChar
= buf
->nullSubsChar
;
1321 if (subsChar
== '\0')
1323 for (c
=string
; *c
!= '\0'; c
++)
1329 ** Compares len Bytes contained in buf starting at Position pos with
1330 ** the contens of cmpText. Returns 0 if there are no differences,
1334 int BufCmp(textBuffer
* buf
, int pos
, int len
, const char *cmpText
)
1341 if (posEnd
> buf
->length
) {
1348 if (posEnd
<= buf
->gapStart
) {
1349 return (strncmp(&(buf
->buf
[pos
]), cmpText
, len
));
1350 } else if (pos
>= buf
->gapStart
) {
1351 return (strncmp (&buf
->buf
[pos
+ (buf
->gapEnd
- buf
->gapStart
)],
1354 part1Length
= buf
->gapStart
- pos
;
1355 result
= strncmp(&buf
->buf
[pos
], cmpText
, part1Length
);
1359 return (strncmp(&buf
->buf
[buf
->gapEnd
], &cmpText
[part1Length
],
1360 len
- part1Length
));
1365 ** Create a pseudo-histogram of the characters in a string (don't actually
1366 ** count, because we don't want overflow, just mark the character's presence
1367 ** with a 1). If init is true, initialize the histogram before acumulating.
1368 ** if not, add the new data to an existing histogram.
1370 static void histogramCharacters(const char *string
, int length
, char hist
[256],
1377 for (i
=0; i
<256; i
++)
1379 for (c
=string
; c
< &string
[length
]; c
++)
1380 hist
[*((unsigned char *)c
)] |= 1;
1384 ** Substitute fromChar with toChar in string.
1386 static void subsChars(char *string
, int length
, char fromChar
, char toChar
)
1390 for (c
=string
; c
< &string
[length
]; c
++)
1391 if (*c
== fromChar
) *c
= toChar
;
1395 ** Search through ascii control characters in histogram in order of least
1396 ** likelihood of use, find an unused character to use as a stand-in for a
1397 ** null. If the character set is full (no available characters outside of
1398 ** the printable set, return the null character.
1400 static char chooseNullSubsChar(char hist
[256])
1402 #define N_REPLACEMENTS 25
1403 static char replacements
[N_REPLACEMENTS
] = {1,2,3,4,5,6,14,15,16,17,18,19,
1404 20,21,22,23,24,25,26,28,29,30,31,11,7};
1406 for (i
= 0; i
< N_REPLACEMENTS
; i
++)
1407 if (hist
[(unsigned char)replacements
[i
]] == 0)
1408 return replacements
[i
];
1413 ** Internal (non-redisplaying) version of BufInsert. Returns the length of
1414 ** text inserted (this is just strlen(text), however this calculation can be
1415 ** expensive and the length will be required by any caller who will continue
1416 ** on to call redisplay). pos must be contiguous with the existing text in
1417 ** the buffer (i.e. not past the end).
1419 static int insert(textBuffer
*buf
, int pos
, const char *text
)
1421 int length
= strlen(text
);
1423 /* Prepare the buffer to receive the new text. If the new text fits in
1424 the current buffer, just move the gap (if necessary) to where
1425 the text should be inserted. If the new text is too large, reallocate
1426 the buffer with a gap large enough to accomodate the new text and a
1427 gap of PREFERRED_GAP_SIZE */
1428 if (length
> buf
->gapEnd
- buf
->gapStart
)
1429 reallocateBuf(buf
, pos
, length
+ PREFERRED_GAP_SIZE
);
1430 else if (pos
!= buf
->gapStart
)
1433 /* Insert the new text (pos now corresponds to the start of the gap) */
1434 memcpy(&buf
->buf
[pos
], text
, length
);
1435 buf
->gapStart
+= length
;
1436 buf
->length
+= length
;
1437 updateSelections(buf
, pos
, 0, length
);
1443 ** Internal (non-redisplaying) version of BufRemove. Removes the contents
1444 ** of the buffer between start and end (and moves the gap to the site of
1447 static void delete(textBuffer
*buf
, int start
, int end
)
1449 /* if the gap is not contiguous to the area to remove, move it there */
1450 if (start
> buf
->gapStart
)
1451 moveGap(buf
, start
);
1452 else if (end
< buf
->gapStart
)
1455 /* expand the gap to encompass the deleted characters */
1456 buf
->gapEnd
+= end
- buf
->gapStart
;
1457 buf
->gapStart
-= buf
->gapStart
- start
;
1459 /* update the length */
1460 buf
->length
-= end
- start
;
1462 /* fix up any selections which might be affected by the change */
1463 updateSelections(buf
, start
, end
-start
, 0);
1467 ** Insert a column of text without calling the modify callbacks. Note that
1468 ** in some pathological cases, inserting can actually decrease the size of
1469 ** the buffer because of spaces being coalesced into tabs. "nDeleted" and
1470 ** "nInserted" return the number of characters deleted and inserted beginning
1471 ** at the start of the line containing "startPos". "endPos" returns buffer
1472 ** position of the lower left edge of the inserted column (as a hint for
1473 ** routines which need to set a cursor position).
1475 static void insertCol(textBuffer
*buf
, int column
, int startPos
,
1476 const char *insText
, int *nDeleted
, int *nInserted
, int *endPos
)
1478 int nLines
, start
, end
, insWidth
, lineStart
, lineEnd
;
1479 int expReplLen
, expInsLen
, len
, endOffset
;
1480 char *outStr
, *outPtr
, *line
, *replText
, *expText
, *insLine
;
1486 /* Allocate a buffer for the replacement string large enough to hold
1487 possibly expanded tabs in both the inserted text and the replaced
1488 area, as well as per line: 1) an additional 2*MAX_EXP_CHAR_LEN
1489 characters for padding where tabs and control characters cross the
1490 column of the selection, 2) up to "column" additional spaces per
1491 line for padding out to the position of "column", 3) padding up
1492 to the width of the inserted text if that must be padded to align
1493 the text beyond the inserted column. (Space for additional
1494 newlines if the inserted text extends beyond the end of the buffer
1495 is counted with the length of insText) */
1496 start
= BufStartOfLine(buf
, startPos
);
1497 nLines
= countLines(insText
) + 1;
1498 insWidth
= textWidth(insText
, buf
->tabDist
, buf
->nullSubsChar
);
1499 end
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, start
, nLines
-1));
1500 replText
= BufGetRange(buf
, start
, end
);
1501 expText
= expandTabs(replText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1505 expText
= expandTabs(insText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1508 outStr
= XtMalloc(expReplLen
+ expInsLen
+
1509 nLines
* (column
+ insWidth
+ MAX_EXP_CHAR_LEN
) + 1);
1511 /* Loop over all lines in the buffer between start and end inserting
1512 text at column, splitting tabs and adding padding appropriately */
1517 lineEnd
= BufEndOfLine(buf
, lineStart
);
1518 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1519 insLine
= copyLine(insPtr
, &len
);
1521 insertColInLine(line
, insLine
, column
, insWidth
, buf
->tabDist
,
1522 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1525 #if 0 /* Earlier comments claimed that trailing whitespace could multiply on
1526 the ends of lines, but insertColInLine looks like it should never
1527 add space unnecessarily, and this trimming interfered with
1528 paragraph filling, so lets see if it works without it. MWE */
1531 for (c
=outPtr
+len
-1; c
>outPtr
&& (*c
== ' ' || *c
== '\t'); c
--)
1537 lineStart
= lineEnd
< buf
->length
? lineEnd
+ 1 : buf
->length
;
1538 if (*insPtr
== '\0')
1542 if (outPtr
!= outStr
)
1543 outPtr
--; /* trim back off extra newline */
1546 /* replace the text between start and end with the new stuff */
1547 delete(buf
, start
, end
);
1548 insert(buf
, start
, outStr
);
1549 *nInserted
= outPtr
- outStr
;
1550 *nDeleted
= end
- start
;
1551 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1556 ** Delete a rectangle of text without calling the modify callbacks. Returns
1557 ** the number of characters replacing those between start and end. Note that
1558 ** in some pathological cases, deleting can actually increase the size of
1559 ** the buffer because of tab expansions. "endPos" returns the buffer position
1560 ** of the point in the last line where the text was removed (as a hint for
1561 ** routines which need to position the cursor after a delete operation)
1563 static void deleteRect(textBuffer
*buf
, int start
, int end
, int rectStart
,
1564 int rectEnd
, int *replaceLen
, int *endPos
)
1566 int nLines
, lineStart
, lineEnd
, len
, endOffset
;
1567 char *outStr
, *outPtr
, *line
, *text
, *expText
;
1569 /* allocate a buffer for the replacement string large enough to hold
1570 possibly expanded tabs as well as an additional MAX_EXP_CHAR_LEN * 2
1571 characters per line for padding where tabs and control characters cross
1572 the edges of the selection */
1573 start
= BufStartOfLine(buf
, start
);
1574 end
= BufEndOfLine(buf
, end
);
1575 nLines
= BufCountLines(buf
, start
, end
) + 1;
1576 text
= BufGetRange(buf
, start
, end
);
1577 expText
= expandTabs(text
, 0, buf
->tabDist
, buf
->nullSubsChar
, &len
);
1580 outStr
= XtMalloc(len
+ nLines
* MAX_EXP_CHAR_LEN
* 2 + 1);
1582 /* loop over all lines in the buffer between start and end removing
1583 the text between rectStart and rectEnd and padding appropriately */
1586 while (lineStart
<= buf
->length
&& lineStart
<= end
) {
1587 lineEnd
= BufEndOfLine(buf
, lineStart
);
1588 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1589 deleteRectFromLine(line
, rectStart
, rectEnd
, buf
->tabDist
,
1590 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1594 lineStart
= lineEnd
+ 1;
1596 if (outPtr
!= outStr
)
1597 outPtr
--; /* trim back off extra newline */
1600 /* replace the text between start and end with the newly created string */
1601 delete(buf
, start
, end
);
1602 insert(buf
, start
, outStr
);
1603 *replaceLen
= outPtr
- outStr
;
1604 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1609 ** Overlay a rectangular area of text without calling the modify callbacks.
1610 ** "nDeleted" and "nInserted" return the number of characters deleted and
1611 ** inserted beginning at the start of the line containing "startPos".
1612 ** "endPos" returns buffer position of the lower left edge of the inserted
1613 ** column (as a hint for routines which need to set a cursor position).
1615 static void overlayRect(textBuffer
*buf
, int startPos
, int rectStart
,
1616 int rectEnd
, const char *insText
,
1617 int *nDeleted
, int *nInserted
, int *endPos
)
1619 int nLines
, start
, end
, lineStart
, lineEnd
;
1620 int expInsLen
, len
, endOffset
;
1621 char *c
, *outStr
, *outPtr
, *line
, *expText
, *insLine
;
1624 /* Allocate a buffer for the replacement string large enough to hold
1625 possibly expanded tabs in the inserted text, as well as per line: 1)
1626 an additional 2*MAX_EXP_CHAR_LEN characters for padding where tabs
1627 and control characters cross the column of the selection, 2) up to
1628 "column" additional spaces per line for padding out to the position
1629 of "column", 3) padding up to the width of the inserted text if that
1630 must be padded to align the text beyond the inserted column. (Space
1631 for additional newlines if the inserted text extends beyond the end
1632 of the buffer is counted with the length of insText) */
1633 start
= BufStartOfLine(buf
, startPos
);
1634 nLines
= countLines(insText
) + 1;
1635 end
= BufEndOfLine(buf
, BufCountForwardNLines(buf
, start
, nLines
-1));
1636 expText
= expandTabs(insText
, 0, buf
->tabDist
, buf
->nullSubsChar
,
1639 outStr
= XtMalloc(end
-start
+ expInsLen
+
1640 nLines
* (rectEnd
+ MAX_EXP_CHAR_LEN
) + 1);
1642 /* Loop over all lines in the buffer between start and end overlaying the
1643 text between rectStart and rectEnd and padding appropriately. Trim
1644 trailing space from line (whitespace at the ends of lines otherwise
1645 tends to multiply, since additional padding is added to maintain it */
1650 lineEnd
= BufEndOfLine(buf
, lineStart
);
1651 line
= BufGetRange(buf
, lineStart
, lineEnd
);
1652 insLine
= copyLine(insPtr
, &len
);
1654 overlayRectInLine(line
, insLine
, rectStart
, rectEnd
, buf
->tabDist
,
1655 buf
->useTabs
, buf
->nullSubsChar
, outPtr
, &len
, &endOffset
);
1658 for (c
=outPtr
+len
-1; c
>outPtr
&& (*c
== ' ' || *c
== '\t'); c
--)
1662 lineStart
= lineEnd
< buf
->length
? lineEnd
+ 1 : buf
->length
;
1663 if (*insPtr
== '\0')
1667 if (outPtr
!= outStr
)
1668 outPtr
--; /* trim back off extra newline */
1671 /* replace the text between start and end with the new stuff */
1672 delete(buf
, start
, end
);
1673 insert(buf
, start
, outStr
);
1674 *nInserted
= outPtr
- outStr
;
1675 *nDeleted
= end
- start
;
1676 *endPos
= start
+ (outPtr
- outStr
) - len
+ endOffset
;
1681 ** Insert characters from single-line string "insLine" in single-line string
1682 ** "line" at "column", leaving "insWidth" space before continuing line.
1683 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1684 ** returns the number of characters from the beginning of the string to
1685 ** the right edge of the inserted text (as a hint for routines which need
1686 ** to position the cursor).
1688 static void insertColInLine(const char *line
, const char *insLine
,
1689 int column
, int insWidth
, int tabDist
, int useTabs
, char nullSubsChar
,
1690 char *outStr
, int *outLen
, int *endOffset
)
1692 char *c
, *outPtr
, *retabbedStr
;
1693 const char *linePtr
;
1694 int indent
, toIndent
, len
, postColIndent
;
1696 /* copy the line up to "column" */
1699 for (linePtr
=line
; *linePtr
!='\0'; linePtr
++) {
1700 len
= BufCharWidth(*linePtr
, indent
, tabDist
, nullSubsChar
);
1701 if (indent
+ len
> column
)
1704 *outPtr
++ = *linePtr
;
1707 /* If "column" falls in the middle of a character, and the character is a
1708 tab, leave it off and leave the indent short and it will get padded
1709 later. If it's a control character, insert it and adjust indent
1711 if (indent
< column
&& *linePtr
!= '\0') {
1712 postColIndent
= indent
+ len
;
1713 if (*linePtr
== '\t')
1716 *outPtr
++ = *linePtr
++;
1720 postColIndent
= indent
;
1722 /* If there's no text after the column and no text to insert, that's all */
1723 if (*insLine
== '\0' && *linePtr
== '\0') {
1724 *outLen
= *endOffset
= outPtr
- outStr
;
1728 /* pad out to column if text is too short */
1729 if (indent
< column
) {
1730 addPadding(outPtr
, indent
, column
, tabDist
, useTabs
, nullSubsChar
,&len
);
1735 /* Copy the text from "insLine" (if any), recalculating the tabs as if
1736 the inserted string began at column 0 to its new column destination */
1737 if (*insLine
!= '\0') {
1738 retabbedStr
= realignTabs(insLine
, 0, indent
, tabDist
, useTabs
,
1739 nullSubsChar
, &len
);
1740 for (c
=retabbedStr
; *c
!='\0'; c
++) {
1742 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1745 XtFree(retabbedStr
);
1748 /* If the original line did not extend past "column", that's all */
1749 if (*linePtr
== '\0') {
1750 *outLen
= *endOffset
= outPtr
- outStr
;
1754 /* Pad out to column + width of inserted text + (additional original
1755 offset due to non-breaking character at column) */
1756 toIndent
= column
+ insWidth
+ postColIndent
-column
;
1757 addPadding(outPtr
, indent
, toIndent
, tabDist
, useTabs
, nullSubsChar
, &len
);
1761 /* realign tabs for text beyond "column" and write it out */
1762 retabbedStr
= realignTabs(linePtr
, postColIndent
, indent
, tabDist
,
1763 useTabs
, nullSubsChar
, &len
);
1764 strcpy(outPtr
, retabbedStr
);
1765 XtFree(retabbedStr
);
1766 *endOffset
= outPtr
- outStr
;
1767 *outLen
= (outPtr
- outStr
) + len
;
1771 ** Remove characters in single-line string "line" between displayed positions
1772 ** "rectStart" and "rectEnd", and write the result to "outStr", which is
1773 ** assumed to be large enough to hold the returned string. Note that in
1774 ** certain cases, it is possible for the string to get longer due to
1775 ** expansion of tabs. "endOffset" returns the number of characters from
1776 ** the beginning of the string to the point where the characters were
1777 ** deleted (as a hint for routines which need to position the cursor).
1779 static void deleteRectFromLine(const char *line
, int rectStart
, int rectEnd
,
1780 int tabDist
, int useTabs
, char nullSubsChar
, char *outStr
, int *outLen
,
1783 int indent
, preRectIndent
, postRectIndent
, len
;
1788 /* copy the line up to rectStart */
1791 for (c
=line
; *c
!='\0'; c
++) {
1792 if (indent
> rectStart
)
1794 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1795 if (indent
+ len
> rectStart
&& (indent
== rectStart
|| *c
== '\t'))
1800 preRectIndent
= indent
;
1802 /* skip the characters between rectStart and rectEnd */
1803 for(; *c
!='\0' && indent
<rectEnd
; c
++)
1804 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
1805 postRectIndent
= indent
;
1807 /* If the line ended before rectEnd, there's nothing more to do */
1810 *outLen
= *endOffset
= outPtr
- outStr
;
1814 /* fill in any space left by removed tabs or control characters
1815 which straddled the boundaries */
1816 indent
= max(rectStart
+ postRectIndent
-rectEnd
, preRectIndent
);
1817 addPadding(outPtr
, preRectIndent
, indent
, tabDist
, useTabs
, nullSubsChar
,
1821 /* Copy the rest of the line. If the indentation has changed, preserve
1822 the position of non-whitespace characters by converting tabs to
1823 spaces, then back to tabs with the correct offset */
1824 retabbedStr
= realignTabs(c
, postRectIndent
, indent
, tabDist
, useTabs
,
1825 nullSubsChar
, &len
);
1826 strcpy(outPtr
, retabbedStr
);
1827 XtFree(retabbedStr
);
1828 *endOffset
= outPtr
- outStr
;
1829 *outLen
= (outPtr
- outStr
) + len
;
1833 ** Overlay characters from single-line string "insLine" on single-line string
1834 ** "line" between displayed character offsets "rectStart" and "rectEnd".
1835 ** "outLen" returns the number of characters written to "outStr", "endOffset"
1836 ** returns the number of characters from the beginning of the string to
1837 ** the right edge of the inserted text (as a hint for routines which need
1838 ** to position the cursor).
1840 ** This code does not handle control characters very well, but oh well.
1842 static void overlayRectInLine(const char *line
, const char *insLine
,
1843 int rectStart
, int rectEnd
, int tabDist
, int useTabs
,
1844 char nullSubsChar
, char *outStr
, int *outLen
, int *endOffset
)
1846 char *c
, *outPtr
, *retabbedStr
;
1847 const char *linePtr
;
1848 int inIndent
, outIndent
, len
, postRectIndent
;
1850 /* copy the line up to "rectStart" or just before the char that
1853 inIndent
= outIndent
= 0;
1854 for (linePtr
=line
; *linePtr
!='\0'; linePtr
++) {
1855 len
= BufCharWidth(*linePtr
, inIndent
, tabDist
, nullSubsChar
);
1856 if (inIndent
+ len
> rectStart
)
1860 *outPtr
++ = *linePtr
;
1863 /* If "rectStart" falls in the middle of a character, and the character
1864 is a tab, leave it off and leave the outIndent short and it will get
1865 padded later. If it's a control character, insert it and adjust
1866 outIndent accordingly. */
1867 if (inIndent
< rectStart
&& *linePtr
!= '\0') {
1868 if (*linePtr
== '\t') {
1869 /* Skip past the tab */
1873 *outPtr
++ = *linePtr
++;
1879 /* skip the characters between rectStart and rectEnd */
1880 for(; *linePtr
!='\0' && inIndent
< rectEnd
; linePtr
++)
1881 inIndent
+= BufCharWidth(*linePtr
, inIndent
, tabDist
, nullSubsChar
);
1882 postRectIndent
= inIndent
;
1884 /* After this inIndent is dead and linePtr is supposed to point at the
1885 character just past the last character that will be altered by
1886 the overlay, whether that's a \t or otherwise. postRectIndent is
1887 the position at which that character is supposed to appear */
1889 /* If there's no text after rectStart and no text to insert, that's all */
1890 if (*insLine
== '\0' && *linePtr
== '\0') {
1891 *outLen
= *endOffset
= outPtr
- outStr
;
1895 /* pad out to rectStart if text is too short */
1896 if (outIndent
< rectStart
) {
1897 addPadding(outPtr
, outIndent
, rectStart
, tabDist
, useTabs
, nullSubsChar
,
1901 outIndent
= rectStart
;
1903 /* Copy the text from "insLine" (if any), recalculating the tabs as if
1904 the inserted string began at column 0 to its new column destination */
1905 if (*insLine
!= '\0') {
1906 retabbedStr
= realignTabs(insLine
, 0, rectStart
, tabDist
, useTabs
,
1907 nullSubsChar
, &len
);
1908 for (c
=retabbedStr
; *c
!='\0'; c
++) {
1910 len
= BufCharWidth(*c
, outIndent
, tabDist
, nullSubsChar
);
1913 XtFree(retabbedStr
);
1916 /* If the original line did not extend past "rectStart", that's all */
1917 if (*linePtr
== '\0') {
1918 *outLen
= *endOffset
= outPtr
- outStr
;
1922 /* Pad out to rectEnd + (additional original offset
1923 due to non-breaking character at right boundary) */
1924 addPadding(outPtr
, outIndent
, postRectIndent
, tabDist
, useTabs
,
1925 nullSubsChar
, &len
);
1927 outIndent
= postRectIndent
;
1929 /* copy the text beyond "rectEnd" */
1930 strcpy(outPtr
, linePtr
);
1931 *endOffset
= outPtr
- outStr
;
1932 *outLen
= (outPtr
- outStr
) + strlen(linePtr
);
1935 static void setSelection(selection
*sel
, int start
, int end
)
1937 sel
->selected
= start
!= end
;
1938 sel
->zeroWidth
= (start
== end
) ? 1 : 0;
1939 sel
->rectangular
= False
;
1940 sel
->start
= min(start
, end
);
1941 sel
->end
= max(start
, end
);
1944 static void setRectSelect(selection
*sel
, int start
, int end
,
1945 int rectStart
, int rectEnd
)
1947 sel
->selected
= rectStart
< rectEnd
;
1948 sel
->zeroWidth
= (rectStart
== rectEnd
) ? 1 : 0;
1949 sel
->rectangular
= True
;
1952 sel
->rectStart
= rectStart
;
1953 sel
->rectEnd
= rectEnd
;
1956 static int getSelectionPos(selection
*sel
, int *start
, int *end
,
1957 int *isRect
, int *rectStart
, int *rectEnd
)
1959 /* Always fill in the parameters (zero-width can be requested too). */
1960 *isRect
= sel
->rectangular
;
1961 *start
= sel
->start
;
1963 if (sel
->rectangular
) {
1964 *rectStart
= sel
->rectStart
;
1965 *rectEnd
= sel
->rectEnd
;
1967 return sel
->selected
;
1970 static char *getSelectionText(textBuffer
*buf
, selection
*sel
)
1972 int start
, end
, isRect
, rectStart
, rectEnd
;
1975 /* If there's no selection, return an allocated empty string */
1976 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
)) {
1982 /* If the selection is not rectangular, return the selected range */
1984 return BufGetTextInRect(buf
, start
, end
, rectStart
, rectEnd
);
1986 return BufGetRange(buf
, start
, end
);
1989 static void removeSelected(textBuffer
*buf
, selection
*sel
)
1992 int isRect
, rectStart
, rectEnd
;
1994 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
))
1997 BufRemoveRect(buf
, start
, end
, rectStart
, rectEnd
);
1999 BufRemove(buf
, start
, end
);
2002 static void replaceSelected(textBuffer
*buf
, selection
*sel
, const char *text
)
2004 int start
, end
, isRect
, rectStart
, rectEnd
;
2005 selection oldSelection
= *sel
;
2007 /* If there's no selection, return */
2008 if (!getSelectionPos(sel
, &start
, &end
, &isRect
, &rectStart
, &rectEnd
))
2011 /* Do the appropriate type of replace */
2013 BufReplaceRect(buf
, start
, end
, rectStart
, rectEnd
, text
);
2015 BufReplace(buf
, start
, end
, text
);
2017 /* Unselect (happens automatically in BufReplace, but BufReplaceRect
2018 can't detect when the contents of a selection goes away) */
2019 sel
->selected
= False
;
2020 redisplaySelection(buf
, &oldSelection
, sel
);
2023 static void addPadding(char *string
, int startIndent
, int toIndent
,
2024 int tabDist
, int useTabs
, char nullSubsChar
, int *charsAdded
)
2029 indent
= startIndent
;
2032 while (indent
< toIndent
) {
2033 len
= BufCharWidth('\t', indent
, tabDist
, nullSubsChar
);
2034 if (len
> 1 && indent
+ len
<= toIndent
) {
2043 while (indent
< toIndent
) {
2048 *charsAdded
= outPtr
- string
;
2052 ** Call the stored modify callback procedure(s) for this buffer to update the
2053 ** changed area(s) on the screen and any other listeners.
2055 static void callModifyCBs(textBuffer
*buf
, int pos
, int nDeleted
,
2056 int nInserted
, int nRestyled
, char *deletedText
)
2060 for (i
=0; i
<buf
->nModifyProcs
; i
++)
2061 (*buf
->modifyProcs
[i
])(pos
, nInserted
, nDeleted
, nRestyled
,
2062 deletedText
, buf
->cbArgs
[i
]);
2066 ** Call the stored pre-delete callback procedure(s) for this buffer to update
2067 ** the changed area(s) on the screen and any other listeners.
2069 static void callPreDeleteCBs(textBuffer
*buf
, int pos
, int nDeleted
)
2073 for (i
=0; i
<buf
->nPreDeleteProcs
; i
++)
2074 (*buf
->preDeleteProcs
[i
])(pos
, nDeleted
, buf
->preDeleteCbArgs
[i
]);
2078 ** Call the stored redisplay procedure(s) for this buffer to update the
2079 ** screen for a change in a selection.
2081 static void redisplaySelection(textBuffer
*buf
, selection
*oldSelection
,
2082 selection
*newSelection
)
2084 int oldStart
, oldEnd
, newStart
, newEnd
, ch1Start
, ch1End
, ch2Start
, ch2End
;
2086 /* If either selection is rectangular, add an additional character to
2087 the end of the selection to request the redraw routines to wipe out
2088 the parts of the selection beyond the end of the line */
2089 oldStart
= oldSelection
->start
;
2090 newStart
= newSelection
->start
;
2091 oldEnd
= oldSelection
->end
;
2092 newEnd
= newSelection
->end
;
2093 if (oldSelection
->rectangular
)
2095 if (newSelection
->rectangular
)
2098 /* If the old or new selection is unselected, just redisplay the
2099 single area that is (was) selected and return */
2100 if (!oldSelection
->selected
&& !newSelection
->selected
)
2102 if (!oldSelection
->selected
) {
2103 callModifyCBs(buf
, newStart
, 0, 0, newEnd
-newStart
, NULL
);
2106 if (!newSelection
->selected
) {
2107 callModifyCBs(buf
, oldStart
, 0, 0, oldEnd
-oldStart
, NULL
);
2111 /* If the selection changed from normal to rectangular or visa versa, or
2112 if a rectangular selection changed boundaries, redisplay everything */
2113 if ((oldSelection
->rectangular
&& !newSelection
->rectangular
) ||
2114 (!oldSelection
->rectangular
&& newSelection
->rectangular
) ||
2115 (oldSelection
->rectangular
&& (
2116 (oldSelection
->rectStart
!= newSelection
->rectStart
) ||
2117 (oldSelection
->rectEnd
!= newSelection
->rectEnd
)))) {
2118 callModifyCBs(buf
, min(oldStart
, newStart
), 0, 0,
2119 max(oldEnd
, newEnd
) - min(oldStart
, newStart
), NULL
);
2123 /* If the selections are non-contiguous, do two separate updates
2125 if (oldEnd
< newStart
|| newEnd
< oldStart
) {
2126 callModifyCBs(buf
, oldStart
, 0, 0, oldEnd
-oldStart
, NULL
);
2127 callModifyCBs(buf
, newStart
, 0, 0, newEnd
-newStart
, NULL
);
2131 /* Otherwise, separate into 3 separate regions: ch1, and ch2 (the two
2132 changed areas), and the unchanged area of their intersection,
2133 and update only the changed area(s) */
2134 ch1Start
= min(oldStart
, newStart
);
2135 ch2End
= max(oldEnd
, newEnd
);
2136 ch1End
= max(oldStart
, newStart
);
2137 ch2Start
= min(oldEnd
, newEnd
);
2138 if (ch1Start
!= ch1End
)
2139 callModifyCBs(buf
, ch1Start
, 0, 0, ch1End
-ch1Start
, NULL
);
2140 if (ch2Start
!= ch2End
)
2141 callModifyCBs(buf
, ch2Start
, 0, 0, ch2End
-ch2Start
, NULL
);
2144 static void moveGap(textBuffer
*buf
, int pos
)
2146 int gapLen
= buf
->gapEnd
- buf
->gapStart
;
2148 if (pos
> buf
->gapStart
)
2149 memmove(&buf
->buf
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
2150 pos
- buf
->gapStart
);
2152 memmove(&buf
->buf
[pos
+ gapLen
], &buf
->buf
[pos
], buf
->gapStart
- pos
);
2153 buf
->gapEnd
+= pos
- buf
->gapStart
;
2154 buf
->gapStart
+= pos
- buf
->gapStart
;
2158 ** reallocate the text storage in "buf" to have a gap starting at "newGapStart"
2159 ** and a gap size of "newGapLen", preserving the buffer's current contents.
2161 static void reallocateBuf(textBuffer
*buf
, int newGapStart
, int newGapLen
)
2166 newBuf
= XtMalloc(buf
->length
+ newGapLen
);
2167 newGapEnd
= newGapStart
+ newGapLen
;
2168 if (newGapStart
<= buf
->gapStart
) {
2169 memcpy(newBuf
, buf
->buf
, newGapStart
);
2170 memcpy(&newBuf
[newGapEnd
], &buf
->buf
[newGapStart
],
2171 buf
->gapStart
- newGapStart
);
2172 memcpy(&newBuf
[newGapEnd
+ buf
->gapStart
- newGapStart
],
2173 &buf
->buf
[buf
->gapEnd
], buf
->length
- buf
->gapStart
);
2174 } else { /* newGapStart > buf->gapStart */
2175 memcpy(newBuf
, buf
->buf
, buf
->gapStart
);
2176 memcpy(&newBuf
[buf
->gapStart
], &buf
->buf
[buf
->gapEnd
],
2177 newGapStart
- buf
->gapStart
);
2178 memcpy(&newBuf
[newGapEnd
],
2179 &buf
->buf
[buf
->gapEnd
+ newGapStart
- buf
->gapStart
],
2180 buf
->length
- newGapStart
);
2184 buf
->gapStart
= newGapStart
;
2185 buf
->gapEnd
= newGapEnd
;
2187 {int i
; for (i
=buf
->gapStart
; i
<buf
->gapEnd
; i
++) buf
->buf
[i
] = '.';}
2192 ** Update all of the selections in "buf" for changes in the buffer's text
2194 static void updateSelections(textBuffer
*buf
, int pos
, int nDeleted
,
2197 updateSelection(&buf
->primary
, pos
, nDeleted
, nInserted
);
2198 updateSelection(&buf
->secondary
, pos
, nDeleted
, nInserted
);
2199 updateSelection(&buf
->highlight
, pos
, nDeleted
, nInserted
);
2203 ** Update an individual selection for changes in the corresponding text
2205 static void updateSelection(selection
*sel
, int pos
, int nDeleted
,
2208 if ((!sel
->selected
&& !sel
->zeroWidth
) || pos
> sel
->end
)
2210 if (pos
+nDeleted
<= sel
->start
) {
2211 sel
->start
+= nInserted
- nDeleted
;
2212 sel
->end
+= nInserted
- nDeleted
;
2213 } else if (pos
<= sel
->start
&& pos
+nDeleted
>= sel
->end
) {
2216 sel
->selected
= False
;
2217 sel
->zeroWidth
= False
;
2218 } else if (pos
<= sel
->start
&& pos
+nDeleted
< sel
->end
) {
2220 sel
->end
= nInserted
+ sel
->end
- nDeleted
;
2221 } else if (pos
< sel
->end
) {
2222 sel
->end
+= nInserted
- nDeleted
;
2223 if (sel
->end
<= sel
->start
)
2224 sel
->selected
= False
;
2229 ** Search forwards in buffer "buf" for character "searchChar", starting
2230 ** with the character "startPos", and returning the result in "foundPos"
2231 ** returns True if found, False if not. (The difference between this and
2232 ** BufSearchForward is that it's optimized for single characters. The
2233 ** overall performance of the text widget is dependent on its ability to
2234 ** count lines quickly, hence searching for a single character: newline)
2236 static int searchForward(textBuffer
*buf
, int startPos
, char searchChar
,
2239 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
2242 while (pos
< buf
->gapStart
) {
2243 if (buf
->buf
[pos
] == searchChar
) {
2249 while (pos
< buf
->length
) {
2250 if (buf
->buf
[pos
+ gapLen
] == searchChar
) {
2256 *foundPos
= buf
->length
;
2261 ** Search backwards in buffer "buf" for character "searchChar", starting
2262 ** with the character BEFORE "startPos", returning the result in "foundPos"
2263 ** returns True if found, False if not. (The difference between this and
2264 ** BufSearchBackward is that it's optimized for single characters. The
2265 ** overall performance of the text widget is dependent on its ability to
2266 ** count lines quickly, hence searching for a single character: newline)
2268 static int searchBackward(textBuffer
*buf
, int startPos
, char searchChar
,
2271 int pos
, gapLen
= buf
->gapEnd
- buf
->gapStart
;
2273 if (startPos
== 0) {
2277 pos
= startPos
== 0 ? 0 : startPos
- 1;
2278 while (pos
>= buf
->gapStart
) {
2279 if (buf
->buf
[pos
+ gapLen
] == searchChar
) {
2286 if (buf
->buf
[pos
] == searchChar
) {
2297 ** Copy from "text" to end up to but not including newline (or end of "text")
2298 ** and return the copy as the function value, and the length of the line in
2301 static char *copyLine(const char *text
, int *lineLen
)
2307 for (c
=text
; *c
!='\0' && *c
!='\n'; c
++)
2309 outStr
= XtMalloc(len
+ 1);
2310 strncpy(outStr
, text
, len
);
2317 ** Count the number of newlines in a null-terminated text string;
2319 static int countLines(const char *string
)
2324 for (c
=string
; *c
!='\0'; c
++)
2325 if (*c
== '\n') lineCount
++;
2330 ** Measure the width in displayed characters of string "text"
2332 static int textWidth(const char *text
, int tabDist
, char nullSubsChar
)
2334 int width
= 0, maxWidth
= 0;
2337 for (c
=text
; *c
!='\0'; c
++) {
2339 if (width
> maxWidth
)
2343 width
+= BufCharWidth(*c
, width
, tabDist
, nullSubsChar
);
2345 if (width
> maxWidth
)
2351 ** Find the first and last character position in a line withing a rectangular
2352 ** selection (for copying). Includes tabs which cross rectStart, but not
2353 ** control characters which do so. Leaves off tabs which cross rectEnd.
2355 ** Technically, the calling routine should convert tab characters which
2356 ** cross the right boundary of the selection to spaces which line up with
2357 ** the edge of the selection. Unfortunately, the additional memory
2358 ** management required in the parent routine to allow for the changes
2359 ** in string size is not worth all the extra work just for a couple of
2360 ** shifted characters, so if a tab protrudes, just lop it off and hope
2361 ** that there are other characters in the selection to establish the right
2362 ** margin for subsequent columnar pastes of this data.
2364 static void findRectSelBoundariesForCopy(textBuffer
*buf
, int lineStartPos
,
2365 int rectStart
, int rectEnd
, int *selStart
, int *selEnd
)
2367 int pos
, width
, indent
= 0;
2370 /* find the start of the selection */
2371 for (pos
=lineStartPos
; pos
<buf
->length
; pos
++) {
2372 c
= BufGetCharacter(buf
, pos
);
2375 width
= BufCharWidth(c
, indent
, buf
->tabDist
, buf
->nullSubsChar
);
2376 if (indent
+ width
> rectStart
) {
2377 if (indent
!= rectStart
&& c
!= '\t') {
2388 for (; pos
<buf
->length
; pos
++) {
2389 c
= BufGetCharacter(buf
, pos
);
2392 width
= BufCharWidth(c
, indent
, buf
->tabDist
, buf
->nullSubsChar
);
2394 if (indent
> rectEnd
) {
2395 if (indent
-width
!= rectEnd
&& c
!= '\t')
2404 ** Adjust the space and tab characters from string "text" so that non-white
2405 ** characters remain stationary when the text is shifted from starting at
2406 ** "origIndent" to starting at "newIndent". Returns an allocated string
2407 ** which must be freed by the caller with XtFree.
2409 static char *realignTabs(const char *text
, int origIndent
, int newIndent
,
2410 int tabDist
, int useTabs
, char nullSubsChar
, int *newLength
)
2412 char *expStr
, *outStr
;
2415 /* If the tabs settings are the same, retain original tabs */
2416 if (origIndent
% tabDist
== newIndent
%tabDist
) {
2418 outStr
= XtMalloc(len
+ 1);
2419 strcpy(outStr
, text
);
2424 /* If the tab settings are not the same, brutally convert tabs to
2425 spaces, then back to tabs in the new position */
2426 expStr
= expandTabs(text
, origIndent
, tabDist
, nullSubsChar
, &len
);
2431 outStr
= unexpandTabs(expStr
, newIndent
, tabDist
, nullSubsChar
, newLength
);
2437 ** Expand tabs to spaces for a block of text. The additional parameter
2438 ** "startIndent" if nonzero, indicates that the text is a rectangular selection
2439 ** beginning at column "startIndent"
2441 static char *expandTabs(const char *text
, int startIndent
, int tabDist
,
2442 char nullSubsChar
, int *newLen
)
2444 char *outStr
, *outPtr
;
2446 int indent
, len
, outLen
= 0;
2448 /* rehearse the expansion to figure out length for output string */
2449 indent
= startIndent
;
2450 for (c
=text
; *c
!='\0'; c
++) {
2452 len
= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2455 } else if (*c
== '\n') {
2456 indent
= startIndent
;
2459 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2464 /* do the expansion */
2465 outStr
= XtMalloc(outLen
+1);
2467 indent
= startIndent
;
2468 for (c
=text
; *c
!= '\0'; c
++) {
2470 len
= BufExpandCharacter(*c
, indent
, outPtr
, tabDist
, nullSubsChar
);
2473 } else if (*c
== '\n') {
2474 indent
= startIndent
;
2477 indent
+= BufCharWidth(*c
, indent
, tabDist
, nullSubsChar
);
2481 outStr
[outLen
] = '\0';
2487 ** Convert sequences of spaces into tabs. The threshold for conversion is
2488 ** when 3 or more spaces can be converted into a single tab, this avoids
2489 ** converting double spaces after a period withing a block of text.
2491 static char *unexpandTabs(const char *text
, int startIndent
, int tabDist
,
2492 char nullSubsChar
, int *newLen
)
2494 char *outStr
, *outPtr
, expandedChar
[MAX_EXP_CHAR_LEN
];
2498 outStr
= XtMalloc(strlen(text
)+1);
2500 indent
= startIndent
;
2501 for (c
=text
; *c
!='\0';) {
2503 len
= BufExpandCharacter('\t', indent
, expandedChar
, tabDist
,
2505 if (len
>= 3 && !strncmp(c
, expandedChar
, len
)) {
2513 } else if (*c
== '\n') {
2514 indent
= startIndent
;
2522 *newLen
= outPtr
- outStr
;
2526 static int max(int i1
, int i2
)
2528 return i1
>= i2
? i1
: i2
;
2531 static int min(int i1
, int i2
)
2533 return i1
<= i2
? i1
: i2
;