1 // Scintilla source code edit control
2 // CellBuffer.cxx - manages a buffer of cells
3 // Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
4 // The License.txt file describes the conditions under which this software may be distributed.
13 #include "Scintilla.h"
15 #include "CellBuffer.h"
17 MarkerHandleSet::MarkerHandleSet() {
21 MarkerHandleSet::~MarkerHandleSet() {
22 MarkerHandleNumber
*mhn
= root
;
24 MarkerHandleNumber
*mhnToFree
= mhn
;
31 int MarkerHandleSet::Length() {
33 MarkerHandleNumber
*mhn
= root
;
41 int MarkerHandleSet::NumberFromHandle(int handle
) {
42 MarkerHandleNumber
*mhn
= root
;
44 if (mhn
->handle
== handle
) {
52 int MarkerHandleSet::MarkValue() {
54 MarkerHandleNumber
*mhn
= root
;
56 m
|= (1 << mhn
->number
);
62 bool MarkerHandleSet::Contains(int handle
) {
63 MarkerHandleNumber
*mhn
= root
;
65 if (mhn
->handle
== handle
) {
73 bool MarkerHandleSet::InsertHandle(int handle
, int markerNum
) {
74 MarkerHandleNumber
*mhn
= new MarkerHandleNumber
;
78 mhn
->number
= markerNum
;
84 void MarkerHandleSet::RemoveHandle(int handle
) {
85 MarkerHandleNumber
**pmhn
= &root
;
87 MarkerHandleNumber
*mhn
= *pmhn
;
88 if (mhn
->handle
== handle
) {
93 pmhn
= &((*pmhn
)->next
);
97 void MarkerHandleSet::RemoveNumber(int markerNum
) {
98 MarkerHandleNumber
**pmhn
= &root
;
100 MarkerHandleNumber
*mhn
= *pmhn
;
101 if (mhn
->number
== markerNum
) {
106 pmhn
= &((*pmhn
)->next
);
110 void MarkerHandleSet::CombineWith(MarkerHandleSet
*other
) {
111 MarkerHandleNumber
**pmhn
= &root
;
113 pmhn
= &((*pmhn
)->next
);
119 LineVector::LineVector() {
126 LineVector::~LineVector() {
127 for (int line
= 0; line
< lines
; line
++) {
128 delete linesData
[line
].handleSet
;
129 linesData
[line
].handleSet
= 0;
137 void LineVector::Init() {
138 for (int line
= 0; line
< lines
; line
++) {
139 delete linesData
[line
].handleSet
;
140 linesData
[line
].handleSet
= 0;
143 linesData
= new LineData
[static_cast<int>(growSize
)];
151 void LineVector::Expand(int sizeNew
) {
152 LineData
*linesDataNew
= new LineData
[sizeNew
];
154 for (int i
= 0; i
< size
; i
++)
155 linesDataNew
[i
] = linesData
[i
];
156 // Do not delete handleSets here as they are transferred to new linesData
158 linesData
= linesDataNew
;
161 Platform::DebugPrintf("No memory available\n");
166 void LineVector::ExpandLevels(int sizeNew
) {
169 int *levelsNew
= new int[sizeNew
];
172 for (; i
< sizeLevels
; i
++)
173 levelsNew
[i
] = levels
[i
];
174 for (; i
< sizeNew
; i
++)
175 levelsNew
[i
] = SC_FOLDLEVELBASE
;
178 sizeLevels
= sizeNew
;
180 Platform::DebugPrintf("No memory available\n");
185 void LineVector::InsertValue(int pos
, int value
) {
186 //Platform::DebugPrintf("InsertValue[%d] = %d\n", pos, value);
187 if ((lines
+ 2) >= size
) {
188 Expand(size
+ growSize
);
190 ExpandLevels(size
+ growSize
);
194 for (int i
= lines
+ 1; i
> pos
; i
--) {
195 linesData
[i
] = linesData
[i
- 1];
197 linesData
[pos
].startPosition
= value
;
198 linesData
[pos
].handleSet
= 0;
201 void LineVector::SetValue(int pos
, int value
) {
202 //Platform::DebugPrintf("SetValue[%d] = %d\n", pos, value);
203 if ((pos
+ 2) >= size
) {
204 //Platform::DebugPrintf("Resize %d %d\n", size,pos);
205 Expand(pos
+ growSize
);
206 //Platform::DebugPrintf("end Resize %d %d\n", size,pos);
209 ExpandLevels(pos
+ growSize
);
212 linesData
[pos
].startPosition
= value
;
215 void LineVector::Remove(int pos
) {
216 //Platform::DebugPrintf("Remove %d\n", pos);
217 // Retain the markers from the deleted line by oring them into the previous line
219 MergeMarkers(pos
- 1);
221 for (int i
= pos
; i
< lines
; i
++) {
222 linesData
[i
] = linesData
[i
+ 1];
227 int LineVector::LineFromPosition(int pos
) {
228 //Platform::DebugPrintf("LineFromPostion %d lines=%d end = %d\n", pos, lines, linesData[lines].startPosition);
231 //Platform::DebugPrintf("LineFromPosition %d\n", pos);
232 if (pos
>= linesData
[lines
].startPosition
)
238 middle
= (upper
+ lower
+ 1) / 2; // Round high
239 if (pos
< linesData
[middle
].startPosition
) {
244 } while (lower
< upper
);
245 //Platform::DebugPrintf("LineFromPostion %d %d %d\n", pos, lower, linesData[lower].startPosition, linesData[lower > 1 ? lower - 1 : 0].startPosition);
249 int LineVector::AddMark(int line
, int markerNum
) {
251 if (!linesData
[line
].handleSet
) {
252 // Need new structure to hold marker handle
253 linesData
[line
].handleSet
= new MarkerHandleSet
;
254 if (!linesData
[line
].handleSet
)
257 linesData
[line
].handleSet
->InsertHandle(handleCurrent
, markerNum
);
259 return handleCurrent
;
262 void LineVector::MergeMarkers(int pos
) {
263 if (linesData
[pos
].handleSet
|| linesData
[pos
+ 1].handleSet
) {
264 if (linesData
[pos
].handleSet
&& linesData
[pos
+ 1].handleSet
) {
265 linesData
[pos
].handleSet
->CombineWith(linesData
[pos
].handleSet
);
266 linesData
[pos
].handleSet
= 0;
271 void LineVector::DeleteMark(int line
, int markerNum
) {
272 if (linesData
[line
].handleSet
) {
273 if (markerNum
== -1) {
274 delete linesData
[line
].handleSet
;
275 linesData
[line
].handleSet
= 0;
277 linesData
[line
].handleSet
->RemoveNumber(markerNum
);
278 if (linesData
[line
].handleSet
->Length() == 0) {
279 delete linesData
[line
].handleSet
;
280 linesData
[line
].handleSet
= 0;
286 void LineVector::DeleteMarkFromHandle(int markerHandle
) {
287 int line
= LineFromHandle(markerHandle
);
289 linesData
[line
].handleSet
->RemoveHandle(markerHandle
);
290 if (linesData
[line
].handleSet
->Length() == 0) {
291 delete linesData
[line
].handleSet
;
292 linesData
[line
].handleSet
= 0;
297 int LineVector::LineFromHandle(int markerHandle
) {
298 for (int line
= 0; line
< lines
; line
++) {
299 if (linesData
[line
].handleSet
) {
300 if (linesData
[line
].handleSet
->Contains(markerHandle
)) {
319 void Action::Create(actionType at_
, int position_
, char *data_
, int lenData_
) {
321 position
= position_
;
327 void Action::Destroy() {
332 void Action::Grab(Action
*source
) {
335 position
= source
->position
;
338 lenData
= source
->lenData
;
340 // Ownership of source data transferred to this
341 source
->position
= 0;
342 source
->at
= startAction
;
347 CellBuffer::CellBuffer(int initialLength
) {
348 body
= new char[initialLength
];
349 size
= initialLength
;
352 gaplen
= initialLength
;
353 part2body
= body
+ gaplen
;
357 actions
= new Action
[lenActions
];
360 collectingUndo
= undoCollectAutoStart
;
361 undoSequenceDepth
= 0;
364 actions
[currentAction
].Create(startAction
);
367 CellBuffer::~CellBuffer() {
374 void CellBuffer::GapTo(int position
) {
375 if (position
== part1len
)
377 if (position
< part1len
) {
378 int diff
= part1len
- position
;
379 //Platform::DebugPrintf("Move gap backwards to %d diff = %d part1len=%d length=%d \n", position,diff, part1len, length);
380 for (int i
= 0; i
< diff
; i
++)
381 body
[part1len
+ gaplen
- i
- 1] = body
[part1len
- i
- 1];
382 } else { // position > part1len
383 int diff
= position
- part1len
;
384 //Platform::DebugPrintf("Move gap forwards to %d diff =%d\n", position,diff);
385 for (int i
= 0; i
< diff
; i
++)
386 body
[part1len
+ i
] = body
[part1len
+ gaplen
+ i
];
389 part2body
= body
+ gaplen
;
392 void CellBuffer::RoomFor(int insertionLength
) {
393 //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength);
394 if (gaplen
<= insertionLength
) {
395 //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength);
397 int newSize
= size
+ insertionLength
+ 4000;
398 //Platform::DebugPrintf("moved gap %d\n", newSize);
399 char *newBody
= new char[newSize
];
400 memcpy(newBody
, body
, size
);
403 gaplen
+= newSize
- size
;
404 part2body
= body
+ gaplen
;
406 //Platform::DebugPrintf("end need room %d %d - size=%d length=%d\n", gaplen, insertionLength,size,length);
410 // To make it easier to write code that uses ByteAt, a position outside the range of the buffer
411 // can be retrieved. All characters outside the range have the value '\0'.
412 char CellBuffer::ByteAt(int position
) {
413 if (position
< part1len
) {
417 return body
[position
];
420 if (position
>= length
) {
423 return part2body
[position
];
428 void CellBuffer::SetByteAt(int position
, char ch
) {
431 //Platform::DebugPrintf("Bad position %d\n",position);
434 if (position
>= length
+ 11) {
435 Platform::DebugPrintf("Very Bad position %d of %d\n", position
, length
);
439 if (position
>= length
) {
440 //Platform::DebugPrintf("Bad position %d of %d\n",position,length);
444 if (position
< part1len
) {
447 part2body
[position
] = ch
;
451 char CellBuffer::CharAt(int position
) {
452 return ByteAt(position
*2);
455 void CellBuffer::GetCharRange(char *buffer
, int position
, int lengthRetrieve
) {
456 if (lengthRetrieve
< 0)
460 int bytePos
= position
* 2;
461 if ((bytePos
+ lengthRetrieve
* 2) > length
) {
462 Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n",bytePos
,
463 lengthRetrieve
, length
);
466 GapTo(0); // Move the buffer so its easy to subscript into it
467 char *pb
= part2body
+ bytePos
;
468 while (lengthRetrieve
--) {
474 char CellBuffer::StyleAt(int position
) {
475 return ByteAt(position
*2 + 1);
478 const char *CellBuffer::InsertString(int position
, char *s
, int insertLength
) {
480 // InsertString and DeleteChars are the bottleneck though which all changes occur
482 if (collectingUndo
) {
483 // Save into the undo/redo stack, but only the characters - not the formatting
484 // This takes up about half load time
485 data
= new char[insertLength
/ 2];
486 for (int i
= 0; i
< insertLength
/ 2; i
++) {
489 AppendAction(insertAction
, position
, data
, insertLength
/ 2);
492 BasicInsertString(position
, s
, insertLength
);
497 void CellBuffer::InsertCharStyle(int position
, char ch
, char style
) {
501 InsertString(position
*2, s
, 2);
504 bool CellBuffer::SetStyleAt(int position
, char style
, char mask
) {
505 char curVal
= ByteAt(position
*2 + 1);
506 if ((curVal
& mask
) != style
) {
507 SetByteAt(position
*2 + 1, (curVal
& ~mask
) | style
);
514 bool CellBuffer::SetStyleFor(int position
, int lengthStyle
, char style
, char mask
) {
515 int bytePos
= position
* 2 + 1;
516 bool changed
= false;
517 while (lengthStyle
--) {
518 char curVal
= ByteAt(bytePos
);
519 if ((curVal
& mask
) != style
) {
520 SetByteAt(bytePos
, (curVal
& ~mask
) | style
);
528 void CellBuffer::EnsureUndoRoom() {
529 //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, length, currentAction);
530 if (currentAction
>= 2) {
531 // Have to test that there is room for 2 more actions in the array
532 // as two actions may be created by this function
533 if (currentAction
>= (lenActions
- 2)) {
534 // Run out of undo nodes so extend the array
535 int lenActionsNew
= lenActions
* 2;
536 Action
*actionsNew
= new Action
[lenActionsNew
];
539 for (int act
= 0; act
<= currentAction
; act
++)
540 actionsNew
[act
].Grab(&actions
[act
]);
542 lenActions
= lenActionsNew
;
543 actions
= actionsNew
;
548 void CellBuffer::AppendAction(actionType at
, int position
, char *data
, int lengthData
) {
550 //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
551 if (currentAction
>= 2) {
552 // See if current action can be coalesced into previous action
553 // Will work if both are inserts or deletes and position is same or two different
554 if ((at
!= actions
[currentAction
- 1].at
) || (abs(position
- actions
[currentAction
- 1].position
) > 2)) {
556 } else if (currentAction
== savePoint
) {
562 actions
[currentAction
].Create(at
, position
, data
, lengthData
);
563 if ((collectingUndo
== undoCollectAutoStart
) && (0 == undoSequenceDepth
)) {
565 actions
[currentAction
].Create(startAction
);
567 maxAction
= currentAction
;
570 const char *CellBuffer::DeleteChars(int position
, int deleteLength
) {
571 // InsertString and DeleteChars are the bottleneck though which all changes occur
574 if (collectingUndo
) {
575 // Save into the undo/redo stack, but only the characters - not the formatting
576 data
= new char[deleteLength
/ 2];
577 for (int i
= 0; i
< deleteLength
/ 2; i
++) {
578 data
[i
] = ByteAt(position
+ i
* 2);
580 AppendAction(removeAction
, position
, data
, deleteLength
/ 2);
583 BasicDeleteChars(position
, deleteLength
);
588 int CellBuffer::ByteLength() {
592 int CellBuffer::Length() {
593 return ByteLength() / 2;
596 int CellBuffer::Lines() {
597 //Platform::DebugPrintf("Lines = %d\n", lv.lines);
601 int CellBuffer::LineStart(int line
) {
604 else if (line
> lv
.lines
)
607 return lv
.linesData
[line
].startPosition
;
610 bool CellBuffer::IsReadOnly() {
614 void CellBuffer::SetReadOnly(bool set
) {
618 void CellBuffer::SetSavePoint() {
619 savePoint
= currentAction
;
622 bool CellBuffer::IsSavePoint() {
623 return savePoint
== currentAction
;
626 int CellBuffer::AddMark(int line
, int markerNum
) {
627 if ((line
>= 0) && (line
< lv
.lines
)) {
628 return lv
.AddMark(line
, markerNum
);
633 void CellBuffer::DeleteMark(int line
, int markerNum
) {
634 if ((line
>= 0) && (line
< lv
.lines
)) {
635 lv
.DeleteMark(line
, markerNum
);
639 void CellBuffer::DeleteMarkFromHandle(int markerHandle
) {
640 lv
.DeleteMarkFromHandle(markerHandle
);
643 int CellBuffer::GetMark(int line
) {
644 if ((line
>= 0) && (line
< lv
.lines
) && (lv
.linesData
[line
].handleSet
))
645 return lv
.linesData
[line
].handleSet
->MarkValue();
649 void CellBuffer::DeleteAllMarks(int markerNum
) {
650 for (int line
= 0; line
< lv
.lines
; line
++) {
651 lv
.DeleteMark(line
, markerNum
);
655 int CellBuffer::LineFromHandle(int markerHandle
) {
656 return lv
.LineFromHandle(markerHandle
);
661 void CellBuffer::BasicInsertString(int position
, char *s
, int insertLength
) {
662 //Platform::DebugPrintf("Inserting at %d for %d\n", position, insertLength);
663 if (insertLength
== 0)
665 RoomFor(insertLength
);
668 memcpy(body
+ part1len
, s
, insertLength
);
669 length
+= insertLength
;
670 part1len
+= insertLength
;
671 gaplen
-= insertLength
;
672 part2body
= body
+ gaplen
;
674 int lineInsert
= lv
.LineFromPosition(position
/ 2) + 1;
675 // Point all the lines after the insertion point further along in the buffer
676 for (int lineAfter
= lineInsert
; lineAfter
<= lv
.lines
; lineAfter
++) {
677 lv
.linesData
[lineAfter
].startPosition
+= insertLength
/ 2;
680 if ((position
- 2) >= 0)
681 chPrev
= ByteAt(position
- 2);
683 if ((position
+ insertLength
) < length
)
684 chAfter
= ByteAt(position
+ insertLength
);
685 if (chPrev
== '\r' && chAfter
== '\n') {
686 //Platform::DebugPrintf("Splitting a crlf pair at %d\n", lineInsert);
687 // Splitting up a crlf pair at position
688 lv
.InsertValue(lineInsert
, position
/ 2);
692 for (int i
= 0; i
< insertLength
; i
+= 2) {
695 //Platform::DebugPrintf("Inserting cr at %d\n", lineInsert);
696 lv
.InsertValue(lineInsert
, (position
+ i
) / 2 + 1);
698 } else if (ch
== '\n') {
699 if (chPrev
== '\r') {
700 //Platform::DebugPrintf("Patching cr before lf at %d\n", lineInsert-1);
701 // Patch up what was end of line
702 lv
.SetValue(lineInsert
- 1, (position
+ i
) / 2 + 1);
704 //Platform::DebugPrintf("Inserting lf at %d\n", lineInsert);
705 lv
.InsertValue(lineInsert
, (position
+ i
) / 2 + 1);
711 // Joining two lines where last insertion is cr and following text starts with lf
712 if (chAfter
== '\n') {
714 //Platform::DebugPrintf("Joining cr before lf at %d\n", lineInsert-1);
715 // End of line already in buffer so drop the newly created one
716 lv
.Remove(lineInsert
- 1);
721 void CellBuffer::BasicDeleteChars(int position
, int deleteLength
) {
722 //Platform::DebugPrintf("Deleting at %d for %d\n", position, deleteLength);
723 if (deleteLength
== 0)
726 if ((position
== 0) && (deleteLength
== length
)) {
727 // If whole buffer is being deleted, faster to reinitialise lines data
728 // than to delete each line.
729 //printf("Whole buffer being deleted\n");
732 // Have to fix up line positions before doing deletion as looking at text in buffer
733 // to work out which lines have been removed
735 int lineRemove
= lv
.LineFromPosition(position
/ 2) + 1;
736 // Point all the lines after the insertion point further along in the buffer
737 for (int lineAfter
= lineRemove
; lineAfter
<= lv
.lines
; lineAfter
++) {
738 lv
.linesData
[lineAfter
].startPosition
-= deleteLength
/ 2;
742 chPrev
= ByteAt(position
- 2);
743 char chBefore
= chPrev
;
745 if (position
< length
)
746 chNext
= ByteAt(position
);
747 bool ignoreNL
= false;
748 if (chPrev
== '\r' && chNext
== '\n') {
749 //Platform::DebugPrintf("Deleting lf after cr, move line end to cr at %d\n", lineRemove);
751 lv
.SetValue(lineRemove
, position
/ 2);
753 ignoreNL
= true; // First \n is not real deletion
757 for (int i
= 0; i
< deleteLength
; i
+= 2) {
759 if ((position
+ i
+ 2) < length
)
760 chNext
= ByteAt(position
+ i
+ 2);
761 //Platform::DebugPrintf("Deleting %d %x\n", i, ch);
763 if (chNext
!= '\n') {
764 //Platform::DebugPrintf("Removing cr end of line\n");
765 lv
.Remove(lineRemove
);
767 } else if ((ch
== '\n') && !ignoreNL
) {
768 //Platform::DebugPrintf("Removing lf end of line\n");
769 lv
.Remove(lineRemove
);
770 ignoreNL
= false; // Further \n are not real deletions
775 // May have to fix up end if last deletion causes cr to be next to lf
776 // or removes one of a crlf pair
778 if ((position
+ deleteLength
) < length
)
779 chAfter
= ByteAt(position
+ deleteLength
);
780 if (chBefore
== '\r' && chAfter
== '\n') {
781 //d.printf("Joining cr before lf at %d\n", lineRemove);
782 // Using lineRemove-1 as cr ended line before start of deletion
783 lv
.Remove(lineRemove
- 1);
784 lv
.SetValue(lineRemove
- 1, position
/ 2 + 1);
788 length
-= deleteLength
;
789 gaplen
+= deleteLength
;
790 part2body
= body
+ gaplen
;
793 undoCollectionType
CellBuffer::SetUndoCollection(undoCollectionType collectUndo
) {
794 collectingUndo
= collectUndo
;
795 undoSequenceDepth
= 0;
796 return collectingUndo
;
799 bool CellBuffer::IsCollectingUndo() {
800 return collectingUndo
;
803 void CellBuffer::AppendUndoStartAction() {
805 // Finish any currently active undo sequence
806 undoSequenceDepth
= 0;
807 if (actions
[currentAction
].at
!= startAction
) {
810 actions
[currentAction
].Create(startAction
);
811 maxAction
= currentAction
;
815 void CellBuffer::BeginUndoAction() {
817 if (undoSequenceDepth
== 0) {
818 if (actions
[currentAction
].at
!= startAction
) {
820 actions
[currentAction
].Create(startAction
);
821 maxAction
= currentAction
;
827 void CellBuffer::EndUndoAction() {
830 if (0 == undoSequenceDepth
) {
831 if (actions
[currentAction
].at
!= startAction
) {
833 actions
[currentAction
].Create(startAction
);
834 maxAction
= currentAction
;
839 void CellBuffer::DeleteUndoHistory() {
840 for (int i
= 1; i
< maxAction
; i
++)
841 actions
[i
].Destroy();
847 bool CellBuffer::CanUndo() {
848 return (!readOnly
) && ((currentAction
> 0) && (maxAction
> 0));
851 int CellBuffer::StartUndo() {
852 // Drop any trailing startAction
853 if (actions
[currentAction
].at
== startAction
&& currentAction
> 0)
856 // Count the steps in this action
857 int act
= currentAction
;
858 while (actions
[act
].at
!= startAction
&& act
> 0) {
861 return currentAction
- act
;
864 const Action
&CellBuffer::UndoStep() {
865 const Action
&actionStep
= actions
[currentAction
];
866 if (actionStep
.at
== insertAction
) {
867 BasicDeleteChars(actionStep
.position
, actionStep
.lenData
*2);
868 } else if (actionStep
.at
== removeAction
) {
869 char *styledData
= new char[actionStep
.lenData
* 2];
870 for (int i
= 0; i
< actionStep
.lenData
; i
++) {
871 styledData
[i
*2] = actionStep
.data
[i
];
872 styledData
[i
*2+1] = 0;
874 BasicInsertString(actionStep
.position
, styledData
, actionStep
.lenData
*2);
881 bool CellBuffer::CanRedo() {
882 return (!readOnly
) && (maxAction
> currentAction
);
885 int CellBuffer::StartRedo() {
886 // Drop any leading startAction
887 if (actions
[currentAction
].at
== startAction
&& currentAction
< maxAction
)
890 // Count the steps in this action
891 int act
= currentAction
;
892 while (actions
[act
].at
!= startAction
&& act
< maxAction
) {
895 return act
- currentAction
;
898 const Action
&CellBuffer::RedoStep() {
899 const Action
&actionStep
= actions
[currentAction
];
900 if (actionStep
.at
== insertAction
) {
901 char *styledData
= new char[actionStep
.lenData
* 2];
902 for (int i
= 0; i
< actionStep
.lenData
; i
++) {
903 styledData
[i
*2] = actionStep
.data
[i
];
904 styledData
[i
*2+1] = 0;
906 BasicInsertString(actionStep
.position
, styledData
, actionStep
.lenData
*2);
908 } else if (actionStep
.at
== removeAction
) {
909 BasicDeleteChars(actionStep
.position
, actionStep
.lenData
*2);
915 int CellBuffer::SetLineState(int line
, int state
) {
916 int stateOld
= lineStates
[line
];
917 lineStates
[line
] = state
;
921 int CellBuffer::GetLineState(int line
) {
922 return lineStates
[line
];
925 int CellBuffer::GetMaxLineState() {
926 return lineStates
.Length();
929 int CellBuffer::SetLevel(int line
, int level
) {
931 if ((line
>= 0) && (line
< lv
.lines
)) {
935 prev
= lv
.levels
[line
];
936 if (lv
.levels
[line
] != level
) {
937 lv
.levels
[line
] = level
;
943 int CellBuffer::GetLevel(int line
) {
944 if (lv
.levels
&& (line
>= 0) && (line
< lv
.lines
)) {
945 return lv
.levels
[line
];
947 return SC_FOLDLEVELBASE
;