]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/Document.cxx
95107eb0874f7f38c5ad8fd2be1acf6cc8bd1d58
[wxWidgets.git] / src / stc / scintilla / src / Document.cxx
1 // Scintilla source code edit control
2 // Document.cxx - text document that handles notifications, DBCS, styling, words and end of line
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.
5
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <ctype.h>
10
11 #include "Platform.h"
12
13 #include "Scintilla.h"
14 #include "SVector.h"
15 #include "CellBuffer.h"
16 #include "Document.h"
17
18 Document::Document() {
19 refCount = 0;
20 #ifdef unix
21 eolMode = SC_EOL_LF;
22 #else
23 eolMode = SC_EOL_CRLF;
24 #endif
25 dbcsCodePage = 0;
26 stylingBits = 5;
27 stylingBitsMask = 0x1F;
28 stylingPos = 0;
29 stylingMask = 0;
30 for (int ch = 0; ch < 256; ch++) {
31 wordchars[ch] = isalnum(ch) || ch == '_';
32 }
33 endStyled = 0;
34 enteredCount = 0;
35 tabInChars = 8;
36 watchers = 0;
37 lenWatchers = 0;
38 }
39
40 Document::~Document() {
41 for (int i = 0; i < lenWatchers; i++) {
42 watchers[i].watcher->NotifyDeleted(this, watchers[i].userData);
43 }
44 delete []watchers;
45 watchers = 0;
46 lenWatchers = 0;
47 }
48
49 // Increase reference count and return its previous value.
50 int Document::AddRef() {
51 return refCount++;
52 }
53
54 // Decrease reference count and return its provius value.
55 // Delete the document if reference count reaches zero.
56 int Document::Release() {
57 int curRefCount = --refCount;
58 if (curRefCount == 0)
59 delete this;
60 return curRefCount;
61 }
62
63 void Document::SetSavePoint() {
64 cb.SetSavePoint();
65 NotifySavePoint(true);
66 }
67
68 int Document::AddMark(int line, int markerNum) {
69 int prev = cb.AddMark(line, markerNum);
70 DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0);
71 NotifyModified(mh);
72 return prev;
73 }
74
75 void Document::DeleteMark(int line, int markerNum) {
76 cb.DeleteMark(line, markerNum);
77 DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0);
78 NotifyModified(mh);
79 }
80
81 void Document::DeleteMarkFromHandle(int markerHandle) {
82 cb.DeleteMarkFromHandle(markerHandle);
83 DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
84 NotifyModified(mh);
85 }
86
87 void Document::DeleteAllMarks(int markerNum) {
88 cb.DeleteAllMarks(markerNum);
89 DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
90 NotifyModified(mh);
91 }
92
93 int Document::LineStart(int line) {
94 return cb.LineStart(line);
95 }
96
97 int Document::LineEnd(int line) {
98 if (line == LinesTotal() - 1) {
99 return LineStart(line + 1);
100 } else {
101 int position = LineStart(line + 1) - 1;
102 // When line terminator is CR+LF, may need to go back one more
103 if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) {
104 position--;
105 }
106 return position;
107 }
108 }
109
110 int Document::LineFromPosition(int pos) {
111 return cb.LineFromPosition(pos);
112 }
113
114 int Document::LineEndPosition(int position) {
115 return LineEnd(LineFromPosition(position));
116 }
117
118 int Document::VCHomePosition(int position) {
119 int line = LineFromPosition(position);
120 int startPosition = LineStart(line);
121 int endLine = LineStart(line + 1) - 1;
122 int startText = startPosition;
123 while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t' ) )
124 startText++;
125 if (position == startText)
126 return startPosition;
127 else
128 return startText;
129 }
130
131 int Document::SetLevel(int line, int level) {
132 int prev = cb.SetLevel(line, level);
133 if (prev != level) {
134 DocModification mh(SC_MOD_CHANGEFOLD, LineStart(line), 0, 0, 0);
135 mh.line = line;
136 mh.foldLevelNow = level;
137 mh.foldLevelPrev = prev;
138 NotifyModified(mh);
139 }
140 return prev;
141 }
142
143 static bool IsSubordinate(int levelStart, int levelTry) {
144 if (levelTry & SC_FOLDLEVELWHITEFLAG)
145 return true;
146 else
147 return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK);
148 }
149
150 int Document::GetLastChild(int lineParent, int level) {
151 if (level == -1)
152 level = GetLevel(lineParent) & SC_FOLDLEVELNUMBERMASK;
153 int maxLine = LinesTotal();
154 int lineMaxSubord = lineParent;
155 while (lineMaxSubord < maxLine-1) {
156 EnsureStyledTo(LineStart(lineMaxSubord+2));
157 if (!IsSubordinate(level, GetLevel(lineMaxSubord+1)))
158 break;
159 lineMaxSubord++;
160 }
161 if (lineMaxSubord > lineParent) {
162 if (level > (GetLevel(lineMaxSubord+1) & SC_FOLDLEVELNUMBERMASK)) {
163 // Have chewed up some whitespace that belongs to a parent so seek back
164 if ((lineMaxSubord > lineParent) && (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG)) {
165 lineMaxSubord--;
166 }
167 }
168 }
169 return lineMaxSubord;
170 }
171
172 int Document::GetFoldParent(int line) {
173 int level = GetLevel(line);
174 int lineLook = line-1;
175 while ((lineLook > 0) && (
176 (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) ||
177 ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) >= level))
178 ) {
179 lineLook--;
180 }
181 if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) &&
182 ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) {
183 return lineLook;
184 } else {
185 return -1;
186 }
187 }
188
189 int Document::ClampPositionIntoDocument(int pos) {
190 return Platform::Clamp(pos, 0, Length());
191 }
192
193 bool Document::IsCrLf(int pos) {
194 if (pos < 0)
195 return false;
196 if (pos >= (Length() - 1))
197 return false;
198 return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');
199 }
200
201 bool Document::IsDBCS(int pos) {
202 #if PLAT_WIN
203 if (dbcsCodePage) {
204 // Anchor DBCS calculations at start of line because start of line can
205 // not be a DBCS trail byte.
206 int startLine = pos;
207 while (startLine > 0 && cb.CharAt(startLine) != '\r' && cb.CharAt(startLine) != '\n')
208 startLine--;
209 while (startLine <= pos) {
210 if (IsDBCSLeadByteEx(dbcsCodePage, cb.CharAt(startLine))) {
211 startLine++;
212 if (startLine >= pos)
213 return true;
214 }
215 startLine++;
216 }
217 }
218 return false;
219 #else
220 return false;
221 #endif
222 }
223
224 // Normalise a position so that it is not halfway through a two byte character.
225 // This can occur in two situations -
226 // When lines are terminated with \r\n pairs which should be treated as one character.
227 // When displaying DBCS text such as Japanese.
228 // If moving, move the position in the indicated direction.
229 int Document::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
230 //Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir);
231 // If out of range, just return value - should be fixed up after
232 if (pos < 0)
233 return pos;
234 if (pos > Length())
235 return pos;
236
237 // Position 0 and Length() can not be between any two characters
238 if (pos == 0)
239 return pos;
240 if (pos == Length())
241 return pos;
242
243 // assert pos > 0 && pos < Length()
244 if (checkLineEnd && IsCrLf(pos - 1)) {
245 if (moveDir > 0)
246 return pos + 1;
247 else
248 return pos - 1;
249 }
250
251 // Not between CR and LF
252
253 #if PLAT_WIN
254 if (dbcsCodePage) {
255 // Anchor DBCS calculations at start of line because start of line can
256 // not be a DBCS trail byte.
257 int startLine = pos;
258 while (startLine > 0 && cb.CharAt(startLine) != '\r' && cb.CharAt(startLine) != '\n')
259 startLine--;
260 bool atLeadByte = false;
261 while (startLine < pos) {
262 if (atLeadByte)
263 atLeadByte = false;
264 else if (IsDBCSLeadByteEx(dbcsCodePage, cb.CharAt(startLine)))
265 atLeadByte = true;
266 else
267 atLeadByte = false;
268 startLine++;
269 //Platform::DebugPrintf("DBCS %s\n", atlead ? "D" : "-");
270 }
271
272 if (atLeadByte) {
273 // Position is between a lead byte and a trail byte
274 if (moveDir > 0)
275 return pos + 1;
276 else
277 return pos - 1;
278 }
279 }
280 #endif
281
282 return pos;
283 }
284
285 void Document::ModifiedAt(int pos) {
286 if (endStyled > pos)
287 endStyled = pos;
288 }
289
290 // Document only modified by gateways DeleteChars, InsertStyledString, Undo, Redo, and SetStyleAt.
291 // SetStyleAt does not change the persistent state of a document
292
293 // Unlike Undo, Redo, and InsertStyledString, the pos argument is a cell number not a char number
294 void Document::DeleteChars(int pos, int len) {
295 if (enteredCount == 0) {
296 enteredCount++;
297 if (cb.IsReadOnly())
298 NotifyModifyAttempt();
299 if (!cb.IsReadOnly()) {
300 int prevLinesTotal = LinesTotal();
301 bool startSavePoint = cb.IsSavePoint();
302 const char *text = cb.DeleteChars(pos*2, len * 2);
303 if (startSavePoint && cb.IsCollectingUndo())
304 NotifySavePoint(!startSavePoint);
305 ModifiedAt(pos);
306 int modFlags = SC_MOD_DELETETEXT | SC_PERFORMED_USER;
307 DocModification mh(modFlags, pos, len, LinesTotal() - prevLinesTotal, text);
308 NotifyModified(mh);
309 }
310 enteredCount--;
311 }
312 }
313
314 void Document::InsertStyledString(int position, char *s, int insertLength) {
315 if (enteredCount == 0) {
316 enteredCount++;
317 if (cb.IsReadOnly())
318 NotifyModifyAttempt();
319 if (!cb.IsReadOnly()) {
320 int prevLinesTotal = LinesTotal();
321 bool startSavePoint = cb.IsSavePoint();
322 const char *text = cb.InsertString(position, s, insertLength);
323 if (startSavePoint && cb.IsCollectingUndo())
324 NotifySavePoint(!startSavePoint);
325 ModifiedAt(position / 2);
326
327 int modFlags = SC_MOD_INSERTTEXT | SC_PERFORMED_USER;
328 DocModification mh(modFlags, position / 2, insertLength / 2, LinesTotal() - prevLinesTotal, text);
329 NotifyModified(mh);
330 }
331 enteredCount--;
332 }
333 }
334
335 int Document::Undo() {
336 int newPos = 0;
337 if (enteredCount == 0) {
338 enteredCount++;
339 bool startSavePoint = cb.IsSavePoint();
340 int steps = cb.StartUndo();
341 //Platform::DebugPrintf("Steps=%d\n", steps);
342 for (int step=0; step<steps; step++) {
343 int prevLinesTotal = LinesTotal();
344 const Action &action = cb.UndoStep();
345 int cellPosition = action.position / 2;
346 ModifiedAt(cellPosition);
347 newPos = cellPosition;
348
349 int modFlags = SC_PERFORMED_UNDO;
350 // With undo, an insertion action becomes a deletion notification
351 if (action.at == removeAction) {
352 newPos += action.lenData;
353 modFlags |= SC_MOD_INSERTTEXT;
354 } else {
355 modFlags |= SC_MOD_DELETETEXT;
356 }
357 if (step == steps-1)
358 modFlags |= SC_LASTSTEPINUNDOREDO;
359 NotifyModified(DocModification(modFlags, cellPosition, action.lenData,
360 LinesTotal() - prevLinesTotal, action.data));
361 }
362
363 bool endSavePoint = cb.IsSavePoint();
364 if (startSavePoint != endSavePoint)
365 NotifySavePoint(endSavePoint);
366 enteredCount--;
367 }
368 return newPos;
369 }
370
371 int Document::Redo() {
372 int newPos = 0;
373 if (enteredCount == 0) {
374 enteredCount++;
375 bool startSavePoint = cb.IsSavePoint();
376 int steps = cb.StartRedo();
377 for (int step=0; step<steps; step++) {
378 int prevLinesTotal = LinesTotal();
379 const Action &action = cb.RedoStep();
380 int cellPosition = action.position / 2;
381 ModifiedAt(cellPosition);
382 newPos = cellPosition;
383
384 int modFlags = SC_PERFORMED_REDO;
385 if (action.at == insertAction) {
386 newPos += action.lenData;
387 modFlags |= SC_MOD_INSERTTEXT;
388 } else {
389 modFlags |= SC_MOD_DELETETEXT;
390 }
391 if (step == steps-1)
392 modFlags |= SC_LASTSTEPINUNDOREDO;
393 NotifyModified(DocModification(modFlags, cellPosition, action.lenData,
394 LinesTotal() - prevLinesTotal, action.data));
395 }
396
397 bool endSavePoint = cb.IsSavePoint();
398 if (startSavePoint != endSavePoint)
399 NotifySavePoint(endSavePoint);
400 enteredCount--;
401 }
402 return newPos;
403 }
404
405 void Document::InsertChar(int pos, char ch) {
406 char chs[2];
407 chs[0] = ch;
408 chs[1] = 0;
409 InsertStyledString(pos*2, chs, 2);
410 }
411
412 // Insert a null terminated string
413 void Document::InsertString(int position, const char *s) {
414 InsertString(position, s, strlen(s));
415 }
416
417 // Insert a string with a length
418 void Document::InsertString(int position, const char *s, int insertLength) {
419 char *sWithStyle = new char[insertLength * 2];
420 if (sWithStyle) {
421 for (int i = 0; i < insertLength; i++) {
422 sWithStyle[i*2] = s[i];
423 sWithStyle[i*2 + 1] = 0;
424 }
425 InsertStyledString(position*2, sWithStyle, insertLength*2);
426 delete []sWithStyle;
427 }
428 }
429
430 void Document::ChangeChar(int pos, char ch) {
431 DeleteChars(pos, 1);
432 InsertChar(pos, ch);
433 }
434
435 void Document::DelChar(int pos) {
436 if (IsCrLf(pos)) {
437 DeleteChars(pos, 2);
438 } else if (IsDBCS(pos)) {
439 DeleteChars(pos, 2);
440 } else if (pos < Length()) {
441 DeleteChars(pos, 1);
442 }
443 }
444
445 int Document::DelCharBack(int pos) {
446 if (pos <= 0) {
447 return pos;
448 } else if (IsCrLf(pos - 2)) {
449 DeleteChars(pos - 2, 2);
450 return pos - 2;
451 } else if (IsDBCS(pos - 1)) {
452 DeleteChars(pos - 2, 2);
453 return pos - 2;
454 } else {
455 DeleteChars(pos - 1, 1);
456 return pos - 1;
457 }
458 }
459
460 void Document::Indent(bool forwards, int lineBottom, int lineTop) {
461 if (forwards) {
462 // Indent by a tab
463 for (int line = lineBottom; line >= lineTop; line--) {
464 InsertChar(LineStart(line), '\t');
465 }
466 } else {
467 // Dedent - suck white space off the front of the line to dedent by equivalent of a tab
468 for (int line = lineBottom; line >= lineTop; line--) {
469 int ispc = 0;
470 while (ispc < tabInChars && cb.CharAt(LineStart(line) + ispc) == ' ')
471 ispc++;
472 int posStartLine = LineStart(line);
473 if (ispc == tabInChars) {
474 DeleteChars(posStartLine, ispc);
475 } else if (cb.CharAt(posStartLine + ispc) == '\t') {
476 DeleteChars(posStartLine, ispc + 1);
477 } else { // Hit a non-white
478 DeleteChars(posStartLine, ispc);
479 }
480 }
481 }
482 }
483
484 void Document::ConvertLineEnds(int eolModeSet) {
485 BeginUndoAction();
486 for (int pos = 0; pos < Length(); pos++) {
487 if (cb.CharAt(pos) == '\r') {
488 if (cb.CharAt(pos+1) == '\n') {
489 if (eolModeSet != SC_EOL_CRLF) {
490 DeleteChars(pos, 2);
491 if (eolModeSet == SC_EOL_CR)
492 InsertString(pos, "\r", 1);
493 else
494 InsertString(pos, "\n", 1);
495 } else {
496 pos++;
497 }
498 } else {
499 if (eolModeSet != SC_EOL_CR) {
500 DeleteChars(pos, 1);
501 if (eolModeSet == SC_EOL_CRLF) {
502 InsertString(pos, "\r\n", 2);
503 pos++;
504 } else {
505 InsertString(pos, "\n", 1);
506 }
507 }
508 }
509 } else if (cb.CharAt(pos) == '\n') {
510 if (eolModeSet != SC_EOL_LF) {
511 DeleteChars(pos, 1);
512 if (eolModeSet == SC_EOL_CRLF) {
513 InsertString(pos, "\r\n", 2);
514 pos++;
515 } else {
516 InsertString(pos, "\r", 1);
517 }
518 }
519 }
520 }
521 EndUndoAction();
522 }
523
524 bool Document::IsWordChar(unsigned char ch) {
525 return wordchars[ch];
526 }
527
528 int Document::ExtendWordSelect(int pos, int delta) {
529 if (delta < 0) {
530 while (pos > 0 && IsWordChar(cb.CharAt(pos - 1)))
531 pos--;
532 } else {
533 while (pos < (Length()) && IsWordChar(cb.CharAt(pos)))
534 pos++;
535 }
536 return pos;
537 }
538
539 int Document::NextWordStart(int pos, int delta) {
540 if (delta < 0) {
541 while (pos > 0 && (cb.CharAt(pos - 1) == ' ' || cb.CharAt(pos - 1) == '\t'))
542 pos--;
543 if (isspace(cb.CharAt(pos - 1))) { // Back up to previous line
544 while (pos > 0 && isspace(cb.CharAt(pos - 1)))
545 pos--;
546 } else {
547 bool startAtWordChar = IsWordChar(cb.CharAt(pos - 1));
548 while (pos > 0 && !isspace(cb.CharAt(pos - 1)) && (startAtWordChar == IsWordChar(cb.CharAt(pos - 1))))
549 pos--;
550 }
551 } else {
552 bool startAtWordChar = IsWordChar(cb.CharAt(pos));
553 while (pos < (Length()) && isspace(cb.CharAt(pos)))
554 pos++;
555 while (pos < (Length()) && !isspace(cb.CharAt(pos)) && (startAtWordChar == IsWordChar(cb.CharAt(pos))))
556 pos++;
557 while (pos < (Length()) && (cb.CharAt(pos) == ' ' || cb.CharAt(pos) == '\t'))
558 pos++;
559 }
560 return pos;
561 }
562
563 bool Document::IsWordAt(int start, int end) {
564 int lengthDoc = Length();
565 if (start > 0) {
566 char ch = CharAt(start - 1);
567 if (IsWordChar(ch))
568 return false;
569 }
570 if (end < lengthDoc - 1) {
571 char ch = CharAt(end);
572 if (IsWordChar(ch))
573 return false;
574 }
575 return true;
576 }
577
578 // Find text in document, supporting both forward and backward
579 // searches (just pass minPos > maxPos to do a backward search)
580 // Has not been tested with backwards DBCS searches yet.
581 long Document::FindText(int minPos, int maxPos, const char *s, bool caseSensitive, bool word) {
582 bool forward = minPos <= maxPos;
583 int increment = forward ? 1 : -1;
584
585 // Range endpoints should not be inside DBCS characters, but just in case, move them.
586 int startPos = MovePositionOutsideChar(minPos, increment, false);
587 int endPos = MovePositionOutsideChar(maxPos, increment, false);
588
589 // Compute actual search ranges needed
590 int lengthFind = strlen(s);
591 int endSearch = 0;
592 if (startPos <= endPos) {
593 endSearch = endPos - lengthFind + 1;
594 } else {
595 endSearch = endPos;
596 }
597 //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);
598 char firstChar = s[0];
599 if (!caseSensitive)
600 firstChar = toupper(firstChar);
601 int pos = startPos;
602 while (forward ? (pos < endSearch) : (pos >= endSearch)) {
603 char ch = CharAt(pos);
604 if (caseSensitive) {
605 if (ch == firstChar) {
606 bool found = true;
607 for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {
608 ch = CharAt(pos + posMatch);
609 if (ch != s[posMatch])
610 found = false;
611 }
612 if (found) {
613 if ((!word) || IsWordAt(pos, pos + lengthFind))
614 return pos;
615 }
616 }
617 } else {
618 if (toupper(ch) == firstChar) {
619 bool found = true;
620 for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {
621 ch = CharAt(pos + posMatch);
622 if (toupper(ch) != toupper(s[posMatch]))
623 found = false;
624 }
625 if (found) {
626 if ((!word) || IsWordAt(pos, pos + lengthFind))
627 return pos;
628 }
629 }
630 }
631 pos += increment;
632 if (dbcsCodePage) {
633 // Ensure trying to match from start of character
634 pos = MovePositionOutsideChar(pos, increment, false);
635 }
636 }
637 //Platform::DebugPrintf("Not found\n");
638 return - 1;
639 }
640
641 int Document::LinesTotal() {
642 return cb.Lines();
643 }
644
645 void Document::ChangeCase(Range r, bool makeUpperCase) {
646 for (int pos=r.start; pos<r.end; pos++) {
647 char ch = CharAt(pos);
648 if (dbcsCodePage && IsDBCS(pos)) {
649 pos++;
650 } else {
651 if (makeUpperCase) {
652 if (islower(ch)) {
653 ChangeChar(pos, toupper(ch));
654 }
655 } else {
656 if (isupper(ch)) {
657 ChangeChar(pos, tolower(ch));
658 }
659 }
660 }
661 }
662 }
663
664 void Document::SetWordChars(unsigned char *chars) {
665 int ch;
666 for (ch = 0; ch < 256; ch++) {
667 wordchars[ch] = false;
668 }
669 if (chars) {
670 while (*chars) {
671 wordchars[*chars] = true;
672 chars++;
673 }
674 } else {
675 for (ch = 0; ch < 256; ch++) {
676 wordchars[ch] = isalnum(ch) || ch == '_';
677 }
678 }
679 }
680
681 void Document::SetStylingBits(int bits) {
682 stylingBits = bits;
683 stylingBitsMask = 0;
684 for (int bit=0; bit<stylingBits; bit++) {
685 stylingBitsMask <<= 1;
686 stylingBitsMask |= 1;
687 }
688 }
689
690 void Document::StartStyling(int position, char mask) {
691 stylingPos = position;
692 stylingMask = mask;
693 }
694
695 void Document::SetStyleFor(int length, char style) {
696 if (enteredCount == 0) {
697 enteredCount++;
698 int prevEndStyled = endStyled;
699 if (cb.SetStyleFor(stylingPos, length, style, stylingMask)) {
700 DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
701 prevEndStyled, length);
702 NotifyModified(mh);
703 }
704 stylingPos += length;
705 endStyled = stylingPos;
706 enteredCount--;
707 }
708 }
709
710 void Document::SetStyles(int length, char *styles) {
711 if (enteredCount == 0) {
712 enteredCount++;
713 int prevEndStyled = endStyled;
714 bool didChange = false;
715 for (int iPos = 0; iPos < length; iPos++, stylingPos++) {
716 if (cb.SetStyleAt(stylingPos, styles[iPos], stylingMask)) {
717 didChange = true;
718 }
719 }
720 endStyled = stylingPos;
721 if (didChange) {
722 DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
723 prevEndStyled, endStyled - prevEndStyled);
724 NotifyModified(mh);
725 }
726 enteredCount--;
727 }
728 }
729
730 bool Document::EnsureStyledTo(int pos) {
731 // Ask the watchers to style, and stop as soon as one responds.
732 for (int i = 0; pos > GetEndStyled() && i < lenWatchers; i++)
733 watchers[i].watcher->NotifyStyleNeeded(this, watchers[i].userData, pos);
734 return pos <= GetEndStyled();
735 }
736
737 bool Document::AddWatcher(DocWatcher *watcher, void *userData) {
738 for (int i = 0; i < lenWatchers; i++) {
739 if ((watchers[i].watcher == watcher) &&
740 (watchers[i].userData == userData))
741 return false;
742 }
743 WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers + 1];
744 if (!pwNew)
745 return false;
746 for (int j = 0; j < lenWatchers; j++)
747 pwNew[j] = watchers[j];
748 pwNew[lenWatchers].watcher = watcher;
749 pwNew[lenWatchers].userData = userData;
750 delete []watchers;
751 watchers = pwNew;
752 lenWatchers++;
753 return true;
754 }
755
756 bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) {
757 for (int i = 0; i < lenWatchers; i++) {
758 if ((watchers[i].watcher == watcher) &&
759 (watchers[i].userData == userData)) {
760 if (lenWatchers == 1) {
761 delete []watchers;
762 watchers = 0;
763 lenWatchers = 0;
764 } else {
765 WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers];
766 if (!pwNew)
767 return false;
768 for (int j = 0; j < lenWatchers - 1; j++) {
769 pwNew[j] = (j < i) ? watchers[j] : watchers[j + 1];
770 }
771 delete []watchers;
772 watchers = pwNew;
773 lenWatchers--;
774 }
775 return true;
776 }
777 }
778 return false;
779 }
780
781 void Document::NotifyModifyAttempt() {
782 for (int i = 0; i < lenWatchers; i++) {
783 watchers[i].watcher->NotifyModifyAttempt(this, watchers[i].userData);
784 }
785 }
786
787 void Document::NotifySavePoint(bool atSavePoint) {
788 for (int i = 0; i < lenWatchers; i++) {
789 watchers[i].watcher->NotifySavePoint(this, watchers[i].userData, atSavePoint);
790 }
791 }
792
793 void Document::NotifyModified(DocModification mh) {
794 for (int i = 0; i < lenWatchers; i++) {
795 watchers[i].watcher->NotifyModified(this, mh, watchers[i].userData);
796 }
797 }