]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/ScintillaBase.cxx
Patch #1106564, corrects possible 100% CPU load condition.
[wxWidgets.git] / contrib / src / stc / scintilla / src / ScintillaBase.cxx
1 // Scintilla source code edit control
2 /** @file ScintillaBase.cxx
3 ** An enhanced subclass of Editor with calltips, autocomplete and context menu.
4 **/
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12
13 #include "Platform.h"
14
15 #include "Scintilla.h"
16 #include "PropSet.h"
17 #ifdef SCI_LEXER
18 #include "SciLexer.h"
19 #include "Accessor.h"
20 #include "DocumentAccessor.h"
21 #include "KeyWords.h"
22 #endif
23 #include "ContractionState.h"
24 #include "SVector.h"
25 #include "CellBuffer.h"
26 #include "CallTip.h"
27 #include "KeyMap.h"
28 #include "Indicator.h"
29 #include "XPM.h"
30 #include "LineMarker.h"
31 #include "Style.h"
32 #include "ViewStyle.h"
33 #include "AutoComplete.h"
34 #include "Document.h"
35 #include "Editor.h"
36 #include "ScintillaBase.h"
37
38 ScintillaBase::ScintillaBase() {
39 displayPopupMenu = true;
40 listType = 0;
41 #ifdef SCI_LEXER
42 lexLanguage = SCLEX_CONTAINER;
43 lexCurrent = 0;
44 for (int wl = 0;wl < numWordLists;wl++)
45 keyWordLists[wl] = new WordList;
46 keyWordLists[numWordLists] = 0;
47 #endif
48 }
49
50 ScintillaBase::~ScintillaBase() {
51 #ifdef SCI_LEXER
52 for (int wl = 0;wl < numWordLists;wl++)
53 delete keyWordLists[wl];
54 #endif
55 }
56
57 void ScintillaBase::Finalise() {
58 Editor::Finalise();
59 popup.Destroy();
60 }
61
62 void ScintillaBase::RefreshColourPalette(Palette &pal, bool want) {
63 Editor::RefreshColourPalette(pal, want);
64 ct.RefreshColourPalette(pal, want);
65 }
66
67 void ScintillaBase::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
68 bool isFillUp = ac.Active() && ac.IsFillUpChar(*s);
69 if (!isFillUp) {
70 Editor::AddCharUTF(s, len, treatAsDBCS);
71 }
72 if (ac.Active()) {
73 AutoCompleteCharacterAdded(s[0]);
74 // For fill ups add the character after the autocompletion has
75 // triggered so containers see the key so can display a calltip.
76 if (isFillUp) {
77 Editor::AddCharUTF(s, len, treatAsDBCS);
78 }
79 }
80 }
81
82 void ScintillaBase::Command(int cmdId) {
83
84 switch (cmdId) {
85
86 case idAutoComplete: // Nothing to do
87
88 break;
89
90 case idCallTip: // Nothing to do
91
92 break;
93
94 case idcmdUndo:
95 WndProc(SCI_UNDO, 0, 0);
96 break;
97
98 case idcmdRedo:
99 WndProc(SCI_REDO, 0, 0);
100 break;
101
102 case idcmdCut:
103 WndProc(SCI_CUT, 0, 0);
104 break;
105
106 case idcmdCopy:
107 WndProc(SCI_COPY, 0, 0);
108 break;
109
110 case idcmdPaste:
111 WndProc(SCI_PASTE, 0, 0);
112 break;
113
114 case idcmdDelete:
115 WndProc(SCI_CLEAR, 0, 0);
116 break;
117
118 case idcmdSelectAll:
119 WndProc(SCI_SELECTALL, 0, 0);
120 break;
121 }
122 }
123
124 int ScintillaBase::KeyCommand(unsigned int iMessage) {
125 // Most key commands cancel autocompletion mode
126 if (ac.Active()) {
127 switch (iMessage) {
128 // Except for these
129 case SCI_LINEDOWN:
130 AutoCompleteMove(1);
131 return 0;
132 case SCI_LINEUP:
133 AutoCompleteMove( -1);
134 return 0;
135 case SCI_PAGEDOWN:
136 AutoCompleteMove(5);
137 return 0;
138 case SCI_PAGEUP:
139 AutoCompleteMove( -5);
140 return 0;
141 case SCI_VCHOME:
142 AutoCompleteMove( -5000);
143 return 0;
144 case SCI_LINEEND:
145 AutoCompleteMove(5000);
146 return 0;
147 case SCI_DELETEBACK:
148 DelCharBack(true);
149 AutoCompleteCharacterDeleted();
150 EnsureCaretVisible();
151 return 0;
152 case SCI_DELETEBACKNOTLINE:
153 DelCharBack(false);
154 AutoCompleteCharacterDeleted();
155 EnsureCaretVisible();
156 return 0;
157 case SCI_TAB:
158 AutoCompleteCompleted();
159 return 0;
160 case SCI_NEWLINE:
161 AutoCompleteCompleted();
162 return 0;
163
164 default:
165 ac.Cancel();
166 }
167 }
168
169 if (ct.inCallTipMode) {
170 if (
171 (iMessage != SCI_CHARLEFT) &&
172 (iMessage != SCI_CHARLEFTEXTEND) &&
173 (iMessage != SCI_CHARRIGHT) &&
174 (iMessage != SCI_CHARLEFTEXTEND) &&
175 (iMessage != SCI_EDITTOGGLEOVERTYPE) &&
176 (iMessage != SCI_DELETEBACK) &&
177 (iMessage != SCI_DELETEBACKNOTLINE)
178 ) {
179 ct.CallTipCancel();
180 }
181 if ((iMessage == SCI_DELETEBACK) || (iMessage == SCI_DELETEBACKNOTLINE)) {
182 if (currentPos <= ct.posStartCallTip) {
183 ct.CallTipCancel();
184 }
185 }
186 }
187 return Editor::KeyCommand(iMessage);
188 }
189
190 void ScintillaBase::AutoCompleteDoubleClick(void* p) {
191 ScintillaBase* sci = reinterpret_cast<ScintillaBase*>(p);
192 sci->AutoCompleteCompleted();
193 }
194
195 void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) {
196 //Platform::DebugPrintf("AutoComplete %s\n", list);
197 ct.CallTipCancel();
198
199 if (ac.chooseSingle && (listType == 0)) {
200 if (list && !strchr(list, ac.GetSeparator())) {
201 const char *typeSep = strchr(list, ac.GetTypesep());
202 size_t lenInsert = (typeSep) ? (typeSep-list) : strlen(list);
203 if (ac.ignoreCase) {
204 SetEmptySelection(currentPos - lenEntered);
205 pdoc->DeleteChars(currentPos, lenEntered);
206 SetEmptySelection(currentPos);
207 pdoc->InsertString(currentPos, list, lenInsert);
208 SetEmptySelection(currentPos + lenInsert);
209 } else {
210 SetEmptySelection(currentPos);
211 pdoc->InsertString(currentPos, list + lenEntered, lenInsert - lenEntered);
212 SetEmptySelection(currentPos + lenInsert - lenEntered);
213 }
214 return;
215 }
216 }
217 ac.Start(wMain, idAutoComplete, currentPos, lenEntered, vs.lineHeight, IsUnicodeMode());
218
219 PRectangle rcClient = GetClientRectangle();
220 Point pt = LocationFromPosition(currentPos - lenEntered);
221
222 int heightLB = 100;
223 int widthLB = 100;
224 if (pt.x >= rcClient.right - widthLB) {
225 HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB);
226 Redraw();
227 pt = LocationFromPosition(currentPos);
228 }
229 PRectangle rcac;
230 rcac.left = pt.x - ac.lb->CaretFromEdge();
231 if (pt.y >= rcClient.bottom - heightLB && // Wont fit below.
232 pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above.
233 rcac.top = pt.y - heightLB;
234 if (rcac.top < 0) {
235 heightLB += rcac.top;
236 rcac.top = 0;
237 }
238 } else {
239 rcac.top = pt.y + vs.lineHeight;
240 }
241 rcac.right = rcac.left + widthLB;
242 rcac.bottom = Platform::Minimum(rcac.top + heightLB, rcClient.bottom);
243 ac.lb->SetPositionRelative(rcac, wMain);
244 ac.lb->SetFont(vs.styles[STYLE_DEFAULT].font);
245 ac.lb->SetAverageCharWidth(vs.styles[STYLE_DEFAULT].aveCharWidth);
246 ac.lb->SetDoubleClickAction(AutoCompleteDoubleClick, this);
247
248 ac.SetList(list);
249
250 // Fiddle the position of the list so it is right next to the target and wide enough for all its strings
251 PRectangle rcList = ac.lb->GetDesiredRect();
252 int heightAlloced = rcList.bottom - rcList.top;
253 widthLB = Platform::Maximum(widthLB, rcList.right - rcList.left);
254 // Make an allowance for large strings in list
255 rcList.left = pt.x - ac.lb->CaretFromEdge();
256 rcList.right = rcList.left + widthLB;
257 if (((pt.y + vs.lineHeight) >= (rcClient.bottom - heightAlloced)) && // Wont fit below.
258 ((pt.y + vs.lineHeight / 2) >= (rcClient.bottom + rcClient.top) / 2)) { // and there is more room above.
259 rcList.top = pt.y - heightAlloced;
260 } else {
261 rcList.top = pt.y + vs.lineHeight;
262 }
263 rcList.bottom = rcList.top + heightAlloced;
264 ac.lb->SetPositionRelative(rcList, wMain);
265 ac.Show();
266 if (lenEntered != 0) {
267 AutoCompleteMoveToCurrentWord();
268 }
269 }
270
271 void ScintillaBase::AutoCompleteCancel() {
272 ac.Cancel();
273 }
274
275 void ScintillaBase::AutoCompleteMove(int delta) {
276 ac.Move(delta);
277 }
278
279 void ScintillaBase::AutoCompleteMoveToCurrentWord() {
280 char wordCurrent[1000];
281 int i;
282 int startWord = ac.posStart - ac.startLen;
283 for (i = startWord; i < currentPos; i++)
284 wordCurrent[i - startWord] = pdoc->CharAt(i);
285 wordCurrent[i - startWord] = '\0';
286 ac.Select(wordCurrent);
287 }
288
289 void ScintillaBase::AutoCompleteCharacterAdded(char ch) {
290 if (ac.IsFillUpChar(ch)) {
291 AutoCompleteCompleted();
292 } else if (ac.IsStopChar(ch)) {
293 ac.Cancel();
294 } else {
295 AutoCompleteMoveToCurrentWord();
296 }
297 }
298
299 void ScintillaBase::AutoCompleteCharacterDeleted() {
300 if (currentPos < ac.posStart - ac.startLen) {
301 ac.Cancel();
302 } else if (ac.cancelAtStartPos && (currentPos <= ac.posStart)) {
303 ac.Cancel();
304 } else {
305 AutoCompleteMoveToCurrentWord();
306 }
307 }
308
309 void ScintillaBase::AutoCompleteCompleted() {
310 int item = ac.lb->GetSelection();
311 char selected[1000];
312 selected[0] = '\0';
313 if (item != -1) {
314 ac.lb->GetValue(item, selected, sizeof(selected));
315 }
316 ac.Cancel();
317 if (item == -1)
318 return;
319
320 if (listType > 0) {
321 userListSelected = selected;
322 SCNotification scn;
323 scn.nmhdr.code = SCN_USERLISTSELECTION;
324 scn.message = 0;
325 scn.wParam = listType;
326 scn.listType = listType;
327 scn.lParam = 0;
328 scn.text = userListSelected.c_str();
329 NotifyParent(scn);
330 return;
331 }
332
333 Position firstPos = ac.posStart - ac.startLen;
334 Position endPos = currentPos;
335 if (ac.dropRestOfWord)
336 endPos = pdoc->ExtendWordSelect(endPos, 1, true);
337 if (endPos < firstPos)
338 return;
339 pdoc->BeginUndoAction();
340 if (endPos != firstPos) {
341 pdoc->DeleteChars(firstPos, endPos - firstPos);
342 }
343 SetEmptySelection(ac.posStart);
344 if (item != -1) {
345 SString piece = selected;
346 pdoc->InsertString(firstPos, piece.c_str());
347 SetEmptySelection(firstPos + static_cast<int>(piece.length()));
348 }
349 pdoc->EndUndoAction();
350 }
351
352 int ScintillaBase::AutoCompleteGetCurrent() {
353 return ac.lb->GetSelection();
354 }
355
356 void ScintillaBase::CallTipShow(Point pt, const char *defn) {
357 AutoCompleteCancel();
358 pt.y += vs.lineHeight;
359 PRectangle rc = ct.CallTipStart(currentPos, pt,
360 defn,
361 vs.styles[STYLE_DEFAULT].fontName,
362 vs.styles[STYLE_DEFAULT].sizeZoomed,
363 IsUnicodeMode(),
364 wMain);
365 // If the call-tip window would be out of the client
366 // space, adjust so it displays above the text.
367 PRectangle rcClient = GetClientRectangle();
368 if (rc.bottom > rcClient.bottom) {
369 int offset = vs.lineHeight + rc.Height();
370 rc.top -= offset;
371 rc.bottom -= offset;
372 }
373 // Now display the window.
374 CreateCallTipWindow(rc);
375 ct.wCallTip.SetPositionRelative(rc, wMain);
376 ct.wCallTip.Show();
377 }
378
379 void ScintillaBase::CallTipClick() {
380 SCNotification scn;
381 scn.nmhdr.code = SCN_CALLTIPCLICK;
382 scn.position = ct.clickPlace;
383 NotifyParent(scn);
384 }
385
386 void ScintillaBase::ContextMenu(Point pt) {
387 if (displayPopupMenu) {
388 bool writable = !WndProc(SCI_GETREADONLY, 0, 0);
389 popup.CreatePopUp();
390 AddToPopUp("Undo", idcmdUndo, writable && pdoc->CanUndo());
391 AddToPopUp("Redo", idcmdRedo, writable && pdoc->CanRedo());
392 AddToPopUp("");
393 AddToPopUp("Cut", idcmdCut, writable && currentPos != anchor);
394 AddToPopUp("Copy", idcmdCopy, currentPos != anchor);
395 AddToPopUp("Paste", idcmdPaste, writable && WndProc(SCI_CANPASTE, 0, 0));
396 AddToPopUp("Delete", idcmdDelete, writable && currentPos != anchor);
397 AddToPopUp("");
398 AddToPopUp("Select All", idcmdSelectAll);
399 popup.Show(pt, wMain);
400 }
401 }
402
403 void ScintillaBase::CancelModes() {
404 AutoCompleteCancel();
405 ct.CallTipCancel();
406 Editor::CancelModes();
407 }
408
409 void ScintillaBase::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
410 CancelModes();
411 Editor::ButtonDown(pt, curTime, shift, ctrl, alt);
412 }
413
414 #ifdef SCI_LEXER
415 void ScintillaBase::SetLexer(uptr_t wParam) {
416 lexLanguage = wParam;
417 lexCurrent = LexerModule::Find(lexLanguage);
418 if (!lexCurrent)
419 lexCurrent = LexerModule::Find(SCLEX_NULL);
420 }
421
422 void ScintillaBase::SetLexerLanguage(const char *languageName) {
423 lexLanguage = SCLEX_CONTAINER;
424 lexCurrent = LexerModule::Find(languageName);
425 if (!lexCurrent)
426 lexCurrent = LexerModule::Find(SCLEX_NULL);
427 if (lexCurrent)
428 lexLanguage = lexCurrent->GetLanguage();
429 }
430
431 void ScintillaBase::Colourise(int start, int end) {
432 int lengthDoc = pdoc->Length();
433 if (end == -1)
434 end = lengthDoc;
435 int len = end - start;
436
437 PLATFORM_ASSERT(len >= 0);
438 PLATFORM_ASSERT(start + len <= lengthDoc);
439
440 //WindowAccessor styler(wMain.GetID(), props);
441 DocumentAccessor styler(pdoc, props, wMain.GetID());
442
443 int styleStart = 0;
444 if (start > 0)
445 styleStart = styler.StyleAt(start - 1);
446 styler.SetCodePage(pdoc->dbcsCodePage);
447
448 if (lexCurrent && (len > 0)) { // Should always succeed as null lexer should always be available
449 lexCurrent->Lex(start, len, styleStart, keyWordLists, styler);
450 styler.Flush();
451 if (styler.GetPropertyInt("fold")) {
452 lexCurrent->Fold(start, len, styleStart, keyWordLists, styler);
453 styler.Flush();
454 }
455 }
456 }
457 #endif
458
459 void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded) {
460 #ifdef SCI_LEXER
461 if (lexLanguage != SCLEX_CONTAINER) {
462 int endStyled = WndProc(SCI_GETENDSTYLED, 0, 0);
463 int lineEndStyled = WndProc(SCI_LINEFROMPOSITION, endStyled, 0);
464 endStyled = WndProc(SCI_POSITIONFROMLINE, lineEndStyled, 0);
465 Colourise(endStyled, endStyleNeeded);
466 return;
467 }
468 #endif
469 Editor::NotifyStyleToNeeded(endStyleNeeded);
470 }
471
472 sptr_t ScintillaBase::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
473 switch (iMessage) {
474 case SCI_AUTOCSHOW:
475 listType = 0;
476 AutoCompleteStart(wParam, reinterpret_cast<const char *>(lParam));
477 break;
478
479 case SCI_AUTOCCANCEL:
480 AutoCompleteCancel();
481 break;
482
483 case SCI_AUTOCACTIVE:
484 return ac.Active();
485
486 case SCI_AUTOCPOSSTART:
487 return ac.posStart;
488
489 case SCI_AUTOCCOMPLETE:
490 AutoCompleteCompleted();
491 break;
492
493 case SCI_AUTOCSETSEPARATOR:
494 ac.SetSeparator(static_cast<char>(wParam));
495 break;
496
497 case SCI_AUTOCGETSEPARATOR:
498 return ac.GetSeparator();
499
500 case SCI_AUTOCSTOPS:
501 ac.SetStopChars(reinterpret_cast<char *>(lParam));
502 break;
503
504 case SCI_AUTOCSELECT:
505 ac.Select(reinterpret_cast<char *>(lParam));
506 break;
507
508 case SCI_AUTOCGETCURRENT:
509 return AutoCompleteGetCurrent();
510
511 case SCI_AUTOCSETCANCELATSTART:
512 ac.cancelAtStartPos = wParam != 0;
513 break;
514
515 case SCI_AUTOCGETCANCELATSTART:
516 return ac.cancelAtStartPos;
517
518 case SCI_AUTOCSETFILLUPS:
519 ac.SetFillUpChars(reinterpret_cast<char *>(lParam));
520 break;
521
522 case SCI_AUTOCSETCHOOSESINGLE:
523 ac.chooseSingle = wParam != 0;
524 break;
525
526 case SCI_AUTOCGETCHOOSESINGLE:
527 return ac.chooseSingle;
528
529 case SCI_AUTOCSETIGNORECASE:
530 ac.ignoreCase = wParam != 0;
531 break;
532
533 case SCI_AUTOCGETIGNORECASE:
534 return ac.ignoreCase;
535
536 case SCI_USERLISTSHOW:
537 listType = wParam;
538 AutoCompleteStart(0, reinterpret_cast<const char *>(lParam));
539 break;
540
541 case SCI_AUTOCSETAUTOHIDE:
542 ac.autoHide = wParam != 0;
543 break;
544
545 case SCI_AUTOCGETAUTOHIDE:
546 return ac.autoHide;
547
548 case SCI_AUTOCSETDROPRESTOFWORD:
549 ac.dropRestOfWord = wParam != 0;
550 break;
551
552 case SCI_AUTOCGETDROPRESTOFWORD:
553 return ac.dropRestOfWord;
554
555 case SCI_REGISTERIMAGE:
556 ac.lb->RegisterImage(wParam, reinterpret_cast<const char *>(lParam));
557 break;
558
559 case SCI_CLEARREGISTEREDIMAGES:
560 ac.lb->ClearRegisteredImages();
561 break;
562
563 case SCI_AUTOCSETTYPESEPARATOR:
564 ac.SetTypesep(static_cast<char>(wParam));
565 break;
566
567 case SCI_AUTOCGETTYPESEPARATOR:
568 return ac.GetTypesep();
569
570 case SCI_CALLTIPSHOW:
571 CallTipShow(LocationFromPosition(wParam),
572 reinterpret_cast<const char *>(lParam));
573 break;
574
575 case SCI_CALLTIPCANCEL:
576 ct.CallTipCancel();
577 break;
578
579 case SCI_CALLTIPACTIVE:
580 return ct.inCallTipMode;
581
582 case SCI_CALLTIPPOSSTART:
583 return ct.posStartCallTip;
584
585 case SCI_CALLTIPSETHLT:
586 ct.SetHighlight(wParam, lParam);
587 break;
588
589 case SCI_CALLTIPSETBACK:
590 ct.colourBG = ColourDesired(wParam);
591 InvalidateStyleRedraw();
592 break;
593
594 case SCI_CALLTIPSETFORE:
595 ct.colourUnSel = ColourDesired(wParam);
596 InvalidateStyleRedraw();
597 break;
598
599 case SCI_CALLTIPSETFOREHLT:
600 ct.colourSel = ColourDesired(wParam);
601 InvalidateStyleRedraw();
602 break;
603
604 case SCI_USEPOPUP:
605 displayPopupMenu = wParam != 0;
606 break;
607
608 #ifdef SCI_LEXER
609 case SCI_SETLEXER:
610 SetLexer(wParam);
611 lexLanguage = wParam;
612 break;
613
614 case SCI_GETLEXER:
615 return lexLanguage;
616
617 case SCI_COLOURISE:
618 Colourise(wParam, lParam);
619 Redraw();
620 break;
621
622 case SCI_SETPROPERTY:
623 props.Set(reinterpret_cast<const char *>(wParam),
624 reinterpret_cast<const char *>(lParam));
625 break;
626
627 case SCI_SETKEYWORDS:
628 if (wParam < numWordLists) {
629 keyWordLists[wParam]->Clear();
630 keyWordLists[wParam]->Set(reinterpret_cast<const char *>(lParam));
631 }
632 break;
633
634 case SCI_SETLEXERLANGUAGE:
635 SetLexerLanguage(reinterpret_cast<const char *>(lParam));
636 break;
637
638 #endif
639
640 default:
641 return Editor::WndProc(iMessage, wParam, lParam);
642 }
643 return 0l;
644 }