OSDN Git Service

Commit DialogBox compile Okay
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / CellBuffer.cxx
1 // Scintilla source code edit control\r
2 /** @file CellBuffer.cxx\r
3  ** Manages a buffer of cells.\r
4  **/\r
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>\r
6 // The License.txt file describes the conditions under which this software may be distributed.\r
7 \r
8 #include <stdio.h>\r
9 #include <string.h>\r
10 #include <stdlib.h>\r
11 #include <stdarg.h>\r
12 \r
13 #include "Platform.h"\r
14 \r
15 #include "Scintilla.h"\r
16 #include "SplitVector.h"\r
17 #include "Partitioning.h"\r
18 #include "CellBuffer.h"\r
19 \r
20 #ifdef SCI_NAMESPACE\r
21 using namespace Scintilla;\r
22 #endif\r
23 \r
24 MarkerHandleSet::MarkerHandleSet() {\r
25         root = 0;\r
26 }\r
27 \r
28 MarkerHandleSet::~MarkerHandleSet() {\r
29         MarkerHandleNumber *mhn = root;\r
30         while (mhn) {\r
31                 MarkerHandleNumber *mhnToFree = mhn;\r
32                 mhn = mhn->next;\r
33                 delete mhnToFree;\r
34         }\r
35         root = 0;\r
36 }\r
37 \r
38 int MarkerHandleSet::Length() const {\r
39         int c = 0;\r
40         MarkerHandleNumber *mhn = root;\r
41         while (mhn) {\r
42                 c++;\r
43                 mhn = mhn->next;\r
44         }\r
45         return c;\r
46 }\r
47 \r
48 int MarkerHandleSet::NumberFromHandle(int handle) const {\r
49         MarkerHandleNumber *mhn = root;\r
50         while (mhn) {\r
51                 if (mhn->handle == handle) {\r
52                         return mhn->number;\r
53                 }\r
54                 mhn = mhn->next;\r
55         }\r
56         return - 1;\r
57 }\r
58 \r
59 int MarkerHandleSet::MarkValue() const {\r
60         unsigned int m = 0;\r
61         MarkerHandleNumber *mhn = root;\r
62         while (mhn) {\r
63                 m |= (1 << mhn->number);\r
64                 mhn = mhn->next;\r
65         }\r
66         return m;\r
67 }\r
68 \r
69 bool MarkerHandleSet::Contains(int handle) const {\r
70         MarkerHandleNumber *mhn = root;\r
71         while (mhn) {\r
72                 if (mhn->handle == handle) {\r
73                         return true;\r
74                 }\r
75                 mhn = mhn->next;\r
76         }\r
77         return false;\r
78 }\r
79 \r
80 bool MarkerHandleSet::InsertHandle(int handle, int markerNum) {\r
81         MarkerHandleNumber *mhn = new MarkerHandleNumber;\r
82         if (!mhn)\r
83                 return false;\r
84         mhn->handle = handle;\r
85         mhn->number = markerNum;\r
86         mhn->next = root;\r
87         root = mhn;\r
88         return true;\r
89 }\r
90 \r
91 void MarkerHandleSet::RemoveHandle(int handle) {\r
92         MarkerHandleNumber **pmhn = &root;\r
93         while (*pmhn) {\r
94                 MarkerHandleNumber *mhn = *pmhn;\r
95                 if (mhn->handle == handle) {\r
96                         *pmhn = mhn->next;\r
97                         delete mhn;\r
98                         return;\r
99                 }\r
100                 pmhn = &((*pmhn)->next);\r
101         }\r
102 }\r
103 \r
104 bool MarkerHandleSet::RemoveNumber(int markerNum) {\r
105         bool performedDeletion = false;\r
106         MarkerHandleNumber **pmhn = &root;\r
107         while (*pmhn) {\r
108                 MarkerHandleNumber *mhn = *pmhn;\r
109                 if (mhn->number == markerNum) {\r
110                         *pmhn = mhn->next;\r
111                         delete mhn;\r
112                         performedDeletion = true;\r
113                 } else {\r
114                         pmhn = &((*pmhn)->next);\r
115                 }\r
116         }\r
117         return performedDeletion;\r
118 }\r
119 \r
120 void MarkerHandleSet::CombineWith(MarkerHandleSet *other) {\r
121         MarkerHandleNumber **pmhn = &root;\r
122         while (*pmhn) {\r
123                 pmhn = &((*pmhn)->next);\r
124         }\r
125         *pmhn = other->root;\r
126         other->root = 0;\r
127 }\r
128 \r
129 LineVector::LineVector() : starts(256) {\r
130         handleCurrent = 1;\r
131 \r
132         Init();\r
133 }\r
134 \r
135 LineVector::~LineVector() {\r
136         starts.DeleteAll();\r
137         for (int line = 0; line < markers.Length(); line++) {\r
138                 delete markers[line];\r
139                 markers[line] = 0;\r
140         }\r
141         markers.DeleteAll();\r
142         levels.DeleteAll();\r
143 }\r
144 \r
145 void LineVector::Init() {\r
146         starts.DeleteAll();\r
147         for (int line = 0; line < markers.Length(); line++) {\r
148                 delete markers[line];\r
149                 markers[line] = 0;\r
150         }\r
151         markers.DeleteAll();\r
152         levels.DeleteAll();\r
153 }\r
154 \r
155 void LineVector::ExpandLevels(int sizeNew) {\r
156         levels.InsertValue(levels.Length(), sizeNew - levels.Length(), SC_FOLDLEVELBASE);\r
157 }\r
158 \r
159 void LineVector::ClearLevels() {\r
160         levels.DeleteAll();\r
161 }\r
162 \r
163 int LineVector::SetLevel(int line, int level) {\r
164         int prev = 0;\r
165         if ((line >= 0) && (line < Lines())) {\r
166                 if (!levels.Length()) {\r
167                         ExpandLevels(Lines() + 1);\r
168                 }\r
169                 prev = levels[line];\r
170                 if (prev != level) {\r
171                         levels[line] = level;\r
172                 }\r
173         }\r
174         return prev;\r
175 }\r
176 \r
177 int LineVector::GetLevel(int line) {\r
178         if (levels.Length() && (line >= 0) && (line < Lines())) {\r
179                 return levels[line];\r
180         } else {\r
181                 return SC_FOLDLEVELBASE;\r
182         }\r
183 }\r
184 \r
185 void LineVector::InsertText(int line, int delta) {\r
186         starts.InsertText(line, delta);\r
187 }\r
188 \r
189 void LineVector::InsertLine(int line, int position) {\r
190         starts.InsertPartition(line, position);\r
191         if (markers.Length()) {\r
192                 markers.Insert(line, 0);\r
193         }\r
194         if (levels.Length()) {\r
195                 int level = SC_FOLDLEVELBASE;\r
196                 if ((line > 0) && (line < Lines())) {\r
197                         level = levels[line-1] & ~SC_FOLDLEVELWHITEFLAG;\r
198                 }\r
199                 levels.InsertValue(line, 1, level);\r
200         }\r
201 }\r
202 \r
203 void LineVector::SetLineStart(int line, int position) {\r
204         starts.SetPartitionStartPosition(line, position);\r
205 }\r
206 \r
207 void LineVector::RemoveLine(int line) {\r
208         starts.RemovePartition(line);\r
209         // Retain the markers from the deleted line by oring them into the previous line\r
210         if (markers.Length()) {\r
211                 if (line > 0) {\r
212                         MergeMarkers(line - 1);\r
213                 }\r
214                 markers.Delete(line);\r
215         }\r
216         if (levels.Length()) {\r
217                 // Move up following lines but merge header flag from this line\r
218                 // to line before to avoid a temporary disappearence causing expansion.\r
219                 int firstHeader = levels[line] & SC_FOLDLEVELHEADERFLAG;\r
220                 levels.Delete(line);\r
221                 if (line > 0)\r
222                         levels[line-1] |= firstHeader;\r
223         }\r
224 }\r
225 \r
226 int LineVector::LineFromPosition(int pos) {\r
227         return starts.PartitionFromPosition(pos);\r
228 }\r
229 \r
230 int LineVector::MarkValue(int line) {\r
231         if (markers.Length() && markers[line])\r
232                 return markers[line]->MarkValue();\r
233         else\r
234                 return 0;\r
235 }\r
236 \r
237 int LineVector::AddMark(int line, int markerNum) {\r
238         handleCurrent++;\r
239         if (!markers.Length()) {\r
240                 // No existing markers so allocate one element per line\r
241                 markers.InsertValue(0, Lines(), 0);\r
242         }\r
243         if (!markers[line]) {\r
244                 // Need new structure to hold marker handle\r
245                 markers[line] = new MarkerHandleSet();\r
246                 if (!markers[line])\r
247                         return - 1;\r
248         }\r
249         markers[line]->InsertHandle(handleCurrent, markerNum);\r
250 \r
251         return handleCurrent;\r
252 }\r
253 \r
254 void LineVector::MergeMarkers(int pos) {\r
255         if (markers[pos + 1] != NULL) {\r
256                 if (markers[pos] == NULL)\r
257                         markers[pos] = new MarkerHandleSet;\r
258                 markers[pos]->CombineWith(markers[pos + 1]);\r
259                 delete markers[pos + 1];\r
260                 markers[pos + 1] = NULL;\r
261         }\r
262 }\r
263 \r
264 void LineVector::DeleteMark(int line, int markerNum, bool all) {\r
265         if (markers.Length() && markers[line]) {\r
266                 if (markerNum == -1) {\r
267                         delete markers[line];\r
268                         markers[line] = NULL;\r
269                 } else {\r
270                         bool performedDeletion = markers[line]->RemoveNumber(markerNum);\r
271                         while (all && performedDeletion) {\r
272                                 performedDeletion = markers[line]->RemoveNumber(markerNum);\r
273                         }\r
274                         if (markers[line]->Length() == 0) {\r
275                                 delete markers[line];\r
276                                 markers[line] = NULL;\r
277                         }\r
278                 }\r
279         }\r
280 }\r
281 \r
282 void LineVector::DeleteMarkFromHandle(int markerHandle) {\r
283         int line = LineFromHandle(markerHandle);\r
284         if (line >= 0) {\r
285                 markers[line]->RemoveHandle(markerHandle);\r
286                 if (markers[line]->Length() == 0) {\r
287                         delete markers[line];\r
288                         markers[line] = NULL;\r
289                 }\r
290         }\r
291 }\r
292 \r
293 int LineVector::LineFromHandle(int markerHandle) {\r
294         if (markers.Length()) {\r
295                 for (int line = 0; line < Lines(); line++) {\r
296                         if (markers[line]) {\r
297                                 if (markers[line]->Contains(markerHandle)) {\r
298                                         return line;\r
299                                 }\r
300                         }\r
301                 }\r
302         }\r
303         return -1;\r
304 }\r
305 \r
306 Action::Action() {\r
307         at = startAction;\r
308         position = 0;\r
309         data = 0;\r
310         lenData = 0;\r
311 }\r
312 \r
313 Action::~Action() {\r
314         Destroy();\r
315 }\r
316 \r
317 void Action::Create(actionType at_, int position_, char *data_, int lenData_, bool mayCoalesce_) {\r
318         delete []data;\r
319         position = position_;\r
320         at = at_;\r
321         data = data_;\r
322         lenData = lenData_;\r
323         mayCoalesce = mayCoalesce_;\r
324 }\r
325 \r
326 void Action::Destroy() {\r
327         delete []data;\r
328         data = 0;\r
329 }\r
330 \r
331 void Action::Grab(Action *source) {\r
332         delete []data;\r
333 \r
334         position = source->position;\r
335         at = source->at;\r
336         data = source->data;\r
337         lenData = source->lenData;\r
338         mayCoalesce = source->mayCoalesce;\r
339 \r
340         // Ownership of source data transferred to this\r
341         source->position = 0;\r
342         source->at = startAction;\r
343         source->data = 0;\r
344         source->lenData = 0;\r
345         source->mayCoalesce = true;\r
346 }\r
347 \r
348 // The undo history stores a sequence of user operations that represent the user's view of the\r
349 // commands executed on the text.\r
350 // Each user operation contains a sequence of text insertion and text deletion actions.\r
351 // All the user operations are stored in a list of individual actions with 'start' actions used\r
352 // as delimiters between user operations.\r
353 // Initially there is one start action in the history.\r
354 // As each action is performed, it is recorded in the history. The action may either become\r
355 // part of the current user operation or may start a new user operation. If it is to be part of the\r
356 // current operation, then it overwrites the current last action. If it is to be part of a new\r
357 // operation, it is appended after the current last action.\r
358 // After writing the new action, a new start action is appended at the end of the history.\r
359 // The decision of whether to start a new user operation is based upon two factors. If a\r
360 // compound operation has been explicitly started by calling BeginUndoAction and no matching\r
361 // EndUndoAction (these calls nest) has been called, then the action is coalesced into the current\r
362 // operation. If there is no outstanding BeginUndoAction call then a new operation is started\r
363 // unless it looks as if the new action is caused by the user typing or deleting a stream of text.\r
364 // Sequences that look like typing or deletion are coalesced into a single user operation.\r
365 \r
366 UndoHistory::UndoHistory() {\r
367 \r
368         lenActions = 100;\r
369         actions = new Action[lenActions];\r
370         maxAction = 0;\r
371         currentAction = 0;\r
372         undoSequenceDepth = 0;\r
373         savePoint = 0;\r
374 \r
375         actions[currentAction].Create(startAction);\r
376 }\r
377 \r
378 UndoHistory::~UndoHistory() {\r
379         delete []actions;\r
380         actions = 0;\r
381 }\r
382 \r
383 void UndoHistory::EnsureUndoRoom() {\r
384         // Have to test that there is room for 2 more actions in the array\r
385         // as two actions may be created by the calling function\r
386         if (currentAction >= (lenActions - 2)) {\r
387                 // Run out of undo nodes so extend the array\r
388                 int lenActionsNew = lenActions * 2;\r
389                 Action *actionsNew = new Action[lenActionsNew];\r
390                 if (!actionsNew)\r
391                         return;\r
392                 for (int act = 0; act <= currentAction; act++)\r
393                         actionsNew[act].Grab(&actions[act]);\r
394                 delete []actions;\r
395                 lenActions = lenActionsNew;\r
396                 actions = actionsNew;\r
397         }\r
398 }\r
399 \r
400 void UndoHistory::AppendAction(actionType at, int position, char *data, int lengthData,\r
401         bool &startSequence) {\r
402         EnsureUndoRoom();\r
403         //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);\r
404         //Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,\r
405         //      actions[currentAction - 1].position, actions[currentAction - 1].lenData);\r
406         if (currentAction < savePoint) {\r
407                 savePoint = -1;\r
408         }\r
409         int oldCurrentAction = currentAction;\r
410         if (currentAction >= 1) {\r
411                 if (0 == undoSequenceDepth) {\r
412                         // Top level actions may not always be coalesced\r
413                         Action &actPrevious = actions[currentAction - 1];\r
414                         // See if current action can be coalesced into previous action\r
415                         // Will work if both are inserts or deletes and position is same\r
416                         if (at != actPrevious.at) {\r
417                                 currentAction++;\r
418                         } else if (currentAction == savePoint) {\r
419                                 currentAction++;\r
420                         } else if ((at == insertAction) &&\r
421                                    (position != (actPrevious.position + actPrevious.lenData))) {\r
422                                 // Insertions must be immediately after to coalesce\r
423                                 currentAction++;\r
424                         } else if (!actions[currentAction].mayCoalesce) {\r
425                                 // Not allowed to coalesce if this set\r
426                                 currentAction++;\r
427                         } else if (at == removeAction) {\r
428                                 if ((lengthData == 1) || (lengthData == 2)){\r
429                                         if ((position + lengthData) == actPrevious.position) {\r
430                                                 ; // Backspace -> OK\r
431                                         } else if (position == actPrevious.position) {\r
432                                                 ; // Delete -> OK\r
433                                         } else {\r
434                                                 // Removals must be at same position to coalesce\r
435                                                 currentAction++;\r
436                                         }\r
437                                 } else {\r
438                                         // Removals must be of one character to coalesce\r
439                                         currentAction++;\r
440                                 }\r
441                         } else {\r
442                                 // Action coalesced.\r
443                         }\r
444 \r
445                 } else {\r
446                         // Actions not at top level are always coalesced unless this is after return to top level\r
447                         if (!actions[currentAction].mayCoalesce)\r
448                                 currentAction++;\r
449                 }\r
450         } else {\r
451                 currentAction++;\r
452         }\r
453         startSequence = oldCurrentAction != currentAction;\r
454         actions[currentAction].Create(at, position, data, lengthData);\r
455         currentAction++;\r
456         actions[currentAction].Create(startAction);\r
457         maxAction = currentAction;\r
458 }\r
459 \r
460 void UndoHistory::BeginUndoAction() {\r
461         EnsureUndoRoom();\r
462         if (undoSequenceDepth == 0) {\r
463                 if (actions[currentAction].at != startAction) {\r
464                         currentAction++;\r
465                         actions[currentAction].Create(startAction);\r
466                         maxAction = currentAction;\r
467                 }\r
468                 actions[currentAction].mayCoalesce = false;\r
469         }\r
470         undoSequenceDepth++;\r
471 }\r
472 \r
473 void UndoHistory::EndUndoAction() {\r
474         PLATFORM_ASSERT(undoSequenceDepth > 0);\r
475         EnsureUndoRoom();\r
476         undoSequenceDepth--;\r
477         if (0 == undoSequenceDepth) {\r
478                 if (actions[currentAction].at != startAction) {\r
479                         currentAction++;\r
480                         actions[currentAction].Create(startAction);\r
481                         maxAction = currentAction;\r
482                 }\r
483                 actions[currentAction].mayCoalesce = false;\r
484         }\r
485 }\r
486 \r
487 void UndoHistory::DropUndoSequence() {\r
488         undoSequenceDepth = 0;\r
489 }\r
490 \r
491 void UndoHistory::DeleteUndoHistory() {\r
492         for (int i = 1; i < maxAction; i++)\r
493                 actions[i].Destroy();\r
494         maxAction = 0;\r
495         currentAction = 0;\r
496         actions[currentAction].Create(startAction);\r
497         savePoint = 0;\r
498 }\r
499 \r
500 void UndoHistory::SetSavePoint() {\r
501         savePoint = currentAction;\r
502 }\r
503 \r
504 bool UndoHistory::IsSavePoint() const {\r
505         return savePoint == currentAction;\r
506 }\r
507 \r
508 bool UndoHistory::CanUndo() const {\r
509         return (currentAction > 0) && (maxAction > 0);\r
510 }\r
511 \r
512 int UndoHistory::StartUndo() {\r
513         // Drop any trailing startAction\r
514         if (actions[currentAction].at == startAction && currentAction > 0)\r
515                 currentAction--;\r
516 \r
517         // Count the steps in this action\r
518         int act = currentAction;\r
519         while (actions[act].at != startAction && act > 0) {\r
520                 act--;\r
521         }\r
522         return currentAction - act;\r
523 }\r
524 \r
525 const Action &UndoHistory::GetUndoStep() const {\r
526         return actions[currentAction];\r
527 }\r
528 \r
529 void UndoHistory::CompletedUndoStep() {\r
530         currentAction--;\r
531 }\r
532 \r
533 bool UndoHistory::CanRedo() const {\r
534         return maxAction > currentAction;\r
535 }\r
536 \r
537 int UndoHistory::StartRedo() {\r
538         // Drop any leading startAction\r
539         if (actions[currentAction].at == startAction && currentAction < maxAction)\r
540                 currentAction++;\r
541 \r
542         // Count the steps in this action\r
543         int act = currentAction;\r
544         while (actions[act].at != startAction && act < maxAction) {\r
545                 act++;\r
546         }\r
547         return act - currentAction;\r
548 }\r
549 \r
550 const Action &UndoHistory::GetRedoStep() const {\r
551         return actions[currentAction];\r
552 }\r
553 \r
554 void UndoHistory::CompletedRedoStep() {\r
555         currentAction++;\r
556 }\r
557 \r
558 CellBuffer::CellBuffer() {\r
559         readOnly = false;\r
560         collectingUndo = true;\r
561 }\r
562 \r
563 CellBuffer::~CellBuffer() {\r
564 }\r
565 \r
566 char CellBuffer::CharAt(int position) const {\r
567         return substance.ValueAt(position);\r
568 }\r
569 \r
570 void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) {\r
571         if (lengthRetrieve < 0)\r
572                 return;\r
573         if (position < 0)\r
574                 return;\r
575         if ((position + lengthRetrieve) > substance.Length()) {\r
576                 Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", position,\r
577                                       lengthRetrieve, substance.Length());\r
578                 return;\r
579         }\r
580         \r
581         for (int i=0; i<lengthRetrieve; i++) {\r
582                 *buffer++ = substance.ValueAt(position + i);\r
583         }\r
584 }\r
585 \r
586 char CellBuffer::StyleAt(int position) {\r
587         return style.ValueAt(position);\r
588 }\r
589 \r
590 const char *CellBuffer::BufferPointer() { \r
591         return substance.BufferPointer();\r
592 }\r
593 \r
594 // The char* returned is to an allocation owned by the undo history\r
595 const char *CellBuffer::InsertString(int position, const char *s, int insertLength, bool &startSequence) {\r
596         char *data = 0;\r
597         // InsertString and DeleteChars are the bottleneck though which all changes occur\r
598         if (!readOnly) {\r
599                 if (collectingUndo) {\r
600                         // Save into the undo/redo stack, but only the characters - not the formatting\r
601                         // This takes up about half load time\r
602                         data = new char[insertLength];\r
603                         for (int i = 0; i < insertLength; i++) {\r
604                                 data[i] = s[i];\r
605                         }\r
606                         uh.AppendAction(insertAction, position, data, insertLength, startSequence);\r
607                 }\r
608 \r
609                 BasicInsertString(position, s, insertLength);\r
610         }\r
611         return data;\r
612 }\r
613 \r
614 bool CellBuffer::SetStyleAt(int position, char styleValue, char mask) {\r
615         styleValue &= mask;\r
616         char curVal = style.ValueAt(position);\r
617         if ((curVal & mask) != styleValue) {\r
618                 style.SetValueAt(position, static_cast<char>((curVal & ~mask) | styleValue));\r
619                 return true;\r
620         } else {\r
621                 return false;\r
622         }\r
623 }\r
624 \r
625 bool CellBuffer::SetStyleFor(int position, int lengthStyle, char styleValue, char mask) {\r
626         bool changed = false;\r
627         PLATFORM_ASSERT(lengthStyle == 0 ||\r
628                 (lengthStyle > 0 && lengthStyle + position <= style.Length()));\r
629         while (lengthStyle--) {\r
630                 char curVal = style.ValueAt(position);\r
631                 if ((curVal & mask) != styleValue) {\r
632                         style.SetValueAt(position, static_cast<char>((curVal & ~mask) | styleValue));\r
633                         changed = true;\r
634                 }\r
635                 position++;\r
636         }\r
637         return changed;\r
638 }\r
639 \r
640 // The char* returned is to an allocation owned by the undo history\r
641 const char *CellBuffer::DeleteChars(int position, int deleteLength, bool &startSequence) {\r
642         // InsertString and DeleteChars are the bottleneck though which all changes occur\r
643         PLATFORM_ASSERT(deleteLength > 0);\r
644         char *data = 0;\r
645         if (!readOnly) {\r
646                 if (collectingUndo) {\r
647                         // Save into the undo/redo stack, but only the characters - not the formatting\r
648                         data = new char[deleteLength];\r
649                         for (int i = 0; i < deleteLength; i++) {\r
650                                 data[i] = substance.ValueAt(position + i);\r
651                         }\r
652                         uh.AppendAction(removeAction, position, data, deleteLength, startSequence);\r
653                 }\r
654 \r
655                 BasicDeleteChars(position, deleteLength);\r
656         }\r
657         return data;\r
658 }\r
659 \r
660 int CellBuffer::Length() const {\r
661         return substance.Length();\r
662 }\r
663 \r
664 void CellBuffer::Allocate(int newSize) {\r
665         substance.ReAllocate(newSize);\r
666         style.ReAllocate(newSize);\r
667 }\r
668 \r
669 int CellBuffer::Lines() const {\r
670         return lv.Lines();\r
671 }\r
672 \r
673 int CellBuffer::LineStart(int line) const {\r
674         if (line < 0)\r
675                 return 0;\r
676         else if (line >= Lines())\r
677                 return Length();\r
678         else\r
679                 return lv.LineStart(line);\r
680 }\r
681 \r
682 bool CellBuffer::IsReadOnly() {\r
683         return readOnly;\r
684 }\r
685 \r
686 void CellBuffer::SetReadOnly(bool set) {\r
687         readOnly = set;\r
688 }\r
689 \r
690 void CellBuffer::SetSavePoint() {\r
691         uh.SetSavePoint();\r
692 }\r
693 \r
694 bool CellBuffer::IsSavePoint() {\r
695         return uh.IsSavePoint();\r
696 }\r
697 \r
698 int CellBuffer::AddMark(int line, int markerNum) {\r
699         if ((line >= 0) && (line < Lines())) {\r
700                 return lv.AddMark(line, markerNum);\r
701         }\r
702         return - 1;\r
703 }\r
704 \r
705 void CellBuffer::DeleteMark(int line, int markerNum) {\r
706         if ((line >= 0) && (line < Lines())) {\r
707                 lv.DeleteMark(line, markerNum, false);\r
708         }\r
709 }\r
710 \r
711 void CellBuffer::DeleteMarkFromHandle(int markerHandle) {\r
712         lv.DeleteMarkFromHandle(markerHandle);\r
713 }\r
714 \r
715 int CellBuffer::GetMark(int line) {\r
716         if ((line >= 0) && (line < Lines()))\r
717                 return lv.MarkValue(line);\r
718         return 0;\r
719 }\r
720 \r
721 void CellBuffer::DeleteAllMarks(int markerNum) {\r
722         for (int line = 0; line < Lines(); line++) {\r
723                 lv.DeleteMark(line, markerNum, true);\r
724         }\r
725 }\r
726 \r
727 int CellBuffer::LineFromHandle(int markerHandle) {\r
728         return lv.LineFromHandle(markerHandle);\r
729 }\r
730 \r
731 // Without undo\r
732 \r
733 void CellBuffer::InsertLine(int line, int position) {\r
734         lv.InsertLine(line, position);\r
735         if (lineStates.Length()) {\r
736                 lineStates.EnsureLength(line);\r
737                 lineStates.Insert(line, 0);\r
738         }\r
739 }\r
740 \r
741 void CellBuffer::RemoveLine(int line) {\r
742         lv.RemoveLine(line);\r
743         if (lineStates.Length() > line) {\r
744                 lineStates.Delete(line);\r
745         }\r
746 }\r
747 \r
748 void CellBuffer::BasicInsertString(int position, const char *s, int insertLength) {\r
749         if (insertLength == 0)\r
750                 return;\r
751         PLATFORM_ASSERT(insertLength > 0);\r
752 \r
753         substance.InsertFromArray(position, s, 0, insertLength);\r
754         style.InsertValue(position, insertLength, 0);\r
755 \r
756         int lineInsert = lv.LineFromPosition(position) + 1;\r
757         // Point all the lines after the insertion point further along in the buffer\r
758         lv.InsertText(lineInsert-1, insertLength);\r
759         char chPrev = substance.ValueAt(position - 1);\r
760         char chAfter = substance.ValueAt(position + insertLength);\r
761         if (chPrev == '\r' && chAfter == '\n') {\r
762                 // Splitting up a crlf pair at position\r
763                 InsertLine(lineInsert, position);\r
764                 lineInsert++;\r
765         }\r
766         char ch = ' ';\r
767         for (int i = 0; i < insertLength; i++) {\r
768                 ch = s[i];\r
769                 if (ch == '\r') {\r
770                         InsertLine(lineInsert, (position + i) + 1);\r
771                         lineInsert++;\r
772                 } else if (ch == '\n') {\r
773                         if (chPrev == '\r') {\r
774                                 // Patch up what was end of line\r
775                                 lv.SetLineStart(lineInsert - 1, (position + i) + 1);\r
776                         } else {\r
777                                 InsertLine(lineInsert, (position + i) + 1);\r
778                                 lineInsert++;\r
779                         }\r
780                 }\r
781                 chPrev = ch;\r
782         }\r
783         // Joining two lines where last insertion is cr and following substance starts with lf\r
784         if (chAfter == '\n') {\r
785                 if (ch == '\r') {\r
786                         // End of line already in buffer so drop the newly created one\r
787                         RemoveLine(lineInsert - 1);\r
788                 }\r
789         }\r
790 }\r
791 \r
792 void CellBuffer::BasicDeleteChars(int position, int deleteLength) {\r
793         if (deleteLength == 0)\r
794                 return;\r
795 \r
796         if ((position == 0) && (deleteLength == substance.Length())) {\r
797                 // If whole buffer is being deleted, faster to reinitialise lines data\r
798                 // than to delete each line.\r
799                 lv.Init();\r
800         } else {\r
801                 // Have to fix up line positions before doing deletion as looking at text in buffer\r
802                 // to work out which lines have been removed\r
803 \r
804                 int lineRemove = lv.LineFromPosition(position) + 1;\r
805                 lv.InsertText(lineRemove-1, - (deleteLength));\r
806                 char chPrev = substance.ValueAt(position - 1);\r
807                 char chBefore = chPrev;\r
808                 char chNext = substance.ValueAt(position);\r
809                 bool ignoreNL = false;\r
810                 if (chPrev == '\r' && chNext == '\n') {\r
811                         // Move back one\r
812                         lv.SetLineStart(lineRemove, position);\r
813                         lineRemove++;\r
814                         ignoreNL = true;        // First \n is not real deletion\r
815                 }\r
816 \r
817                 char ch = chNext;\r
818                 for (int i = 0; i < deleteLength; i++) {\r
819                         chNext = substance.ValueAt(position + i + 1);\r
820                         if (ch == '\r') {\r
821                                 if (chNext != '\n') {\r
822                                         RemoveLine(lineRemove);\r
823                                 }\r
824                         } else if (ch == '\n') {\r
825                                 if (ignoreNL) {\r
826                                         ignoreNL = false;       // Further \n are real deletions\r
827                                 } else {\r
828                                         RemoveLine(lineRemove);\r
829                                 }\r
830                         }\r
831 \r
832                         ch = chNext;\r
833                 }\r
834                 // May have to fix up end if last deletion causes cr to be next to lf\r
835                 // or removes one of a crlf pair\r
836                 char chAfter = substance.ValueAt(position + deleteLength);\r
837                 if (chBefore == '\r' && chAfter == '\n') {\r
838                         // Using lineRemove-1 as cr ended line before start of deletion\r
839                         RemoveLine(lineRemove - 1);\r
840                         lv.SetLineStart(lineRemove - 1, position + 1);\r
841                 }\r
842         }\r
843         substance.DeleteRange(position, deleteLength);\r
844         style.DeleteRange(position, deleteLength);\r
845 }\r
846 \r
847 bool CellBuffer::SetUndoCollection(bool collectUndo) {\r
848         collectingUndo = collectUndo;\r
849         uh.DropUndoSequence();\r
850         return collectingUndo;\r
851 }\r
852 \r
853 bool CellBuffer::IsCollectingUndo() {\r
854         return collectingUndo;\r
855 }\r
856 \r
857 void CellBuffer::BeginUndoAction() {\r
858         uh.BeginUndoAction();\r
859 }\r
860 \r
861 void CellBuffer::EndUndoAction() {\r
862         uh.EndUndoAction();\r
863 }\r
864 \r
865 void CellBuffer::DeleteUndoHistory() {\r
866         uh.DeleteUndoHistory();\r
867 }\r
868 \r
869 bool CellBuffer::CanUndo() {\r
870         return uh.CanUndo();\r
871 }\r
872 \r
873 int CellBuffer::StartUndo() {\r
874         return uh.StartUndo();\r
875 }\r
876 \r
877 const Action &CellBuffer::GetUndoStep() const {\r
878         return uh.GetUndoStep();\r
879 }\r
880 \r
881 void CellBuffer::PerformUndoStep() {\r
882         const Action &actionStep = uh.GetUndoStep();\r
883         if (actionStep.at == insertAction) {\r
884                 BasicDeleteChars(actionStep.position, actionStep.lenData);\r
885         } else if (actionStep.at == removeAction) {\r
886                 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);\r
887         }\r
888         uh.CompletedUndoStep();\r
889 }\r
890 \r
891 bool CellBuffer::CanRedo() {\r
892         return uh.CanRedo();\r
893 }\r
894 \r
895 int CellBuffer::StartRedo() {\r
896         return uh.StartRedo();\r
897 }\r
898 \r
899 const Action &CellBuffer::GetRedoStep() const {\r
900         return uh.GetRedoStep();\r
901 }\r
902 \r
903 void CellBuffer::PerformRedoStep() {\r
904         const Action &actionStep = uh.GetRedoStep();\r
905         if (actionStep.at == insertAction) {\r
906                 BasicInsertString(actionStep.position, actionStep.data, actionStep.lenData);\r
907         } else if (actionStep.at == removeAction) {\r
908                 BasicDeleteChars(actionStep.position, actionStep.lenData);\r
909         }\r
910         uh.CompletedRedoStep();\r
911 }\r
912 \r
913 int CellBuffer::SetLineState(int line, int state) {\r
914         lineStates.EnsureLength(line + 1);\r
915         int stateOld = lineStates[line];\r
916         lineStates[line] = state;\r
917         return stateOld;\r
918 }\r
919 \r
920 int CellBuffer::GetLineState(int line) {\r
921         lineStates.EnsureLength(line + 1);\r
922         return lineStates[line];\r
923 }\r
924 \r
925 int CellBuffer::GetMaxLineState() {\r
926         return lineStates.Length();\r
927 }\r
928 \r
929 int CellBuffer::SetLevel(int line, int level) {\r
930         return lv.SetLevel(line, level);\r
931 }\r
932 \r
933 int CellBuffer::GetLevel(int line) {\r
934         return lv.GetLevel(line);\r
935 }\r
936 \r
937 void CellBuffer::ClearLevels() {\r
938         lv.ClearLevels();\r
939 }\r