]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/ScintillaBase.cxx
ea2e2d1d6db9eef8e3a48ccc347a2ea13c6b3aa1
[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 if (ac.ignoreCase) {
202 SetEmptySelection(currentPos - lenEntered);
203 pdoc->DeleteChars(currentPos, lenEntered);
204 SetEmptySelection(currentPos);
205 pdoc->InsertString(currentPos, list);
206 SetEmptySelection(currentPos + strlen(list));
207 } else {
208 SetEmptySelection(currentPos);
209 pdoc->InsertString(currentPos, list + lenEntered);
210 SetEmptySelection(currentPos + strlen(list + lenEntered));
211 }
212 return;
213 }
214 }
215 ac.Start(wMain, idAutoComplete, currentPos, lenEntered, vs.lineHeight, IsUnicodeMode());
216
217 PRectangle rcClient = GetClientRectangle();
218 Point pt = LocationFromPosition(currentPos - lenEntered);
219
220 int heightLB = 100;
221 int widthLB = 100;
222 if (pt.x >= rcClient.right - widthLB) {
223 HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB);
224 Redraw();
225 pt = LocationFromPosition(currentPos);
226 }
227 PRectangle rcac;
228 rcac.left = pt.x - ac.lb->CaretFromEdge();
229 if (pt.y >= rcClient.bottom - heightLB && // Wont fit below.
230 pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above.
231 rcac.top = pt.y - heightLB;
232 if (rcac.top < 0) {
233 heightLB += rcac.top;
234 rcac.top = 0;
235 }
236 } else {
237 rcac.top = pt.y + vs.lineHeight;
238 }
239 rcac.right = rcac.left + widthLB;
240 rcac.bottom = Platform::Minimum(rcac.top + heightLB, rcClient.bottom);
241 ac.lb->SetPositionRelative(rcac, wMain);
242 ac.lb->SetFont(vs.styles[STYLE_DEFAULT].font);
243 ac.lb->SetAverageCharWidth(vs.styles[STYLE_DEFAULT].aveCharWidth);
244 ac.lb->SetDoubleClickAction(AutoCompleteDoubleClick, this);
245
246 ac.SetList(list);
247
248 // Fiddle the position of the list so it is right next to the target and wide enough for all its strings
249 PRectangle rcList = ac.lb->GetDesiredRect();
250 int heightAlloced = rcList.bottom - rcList.top;
251 widthLB = Platform::Maximum(widthLB, rcList.right - rcList.left);
252 // Make an allowance for large strings in list
253 rcList.left = pt.x - ac.lb->CaretFromEdge();
254 rcList.right = rcList.left + widthLB;
255 if (((pt.y + vs.lineHeight) >= (rcClient.bottom - heightAlloced)) && // Wont fit below.
256 ((pt.y + vs.lineHeight / 2) >= (rcClient.bottom + rcClient.top) / 2)) { // and there is more room above.
257 rcList.top = pt.y - heightAlloced;
258 } else {
259 rcList.top = pt.y + vs.lineHeight;
260 }
261 rcList.bottom = rcList.top + heightAlloced;
262 ac.lb->SetPositionRelative(rcList, wMain);
263 ac.Show();
264 if (lenEntered != 0) {
265 AutoCompleteMoveToCurrentWord();
266 }
267 }
268
269 void ScintillaBase::AutoCompleteCancel() {
270 ac.Cancel();
271 }
272
273 void ScintillaBase::AutoCompleteMove(int delta) {
274 ac.Move(delta);
275 }
276
277 void ScintillaBase::AutoCompleteMoveToCurrentWord() {
278 char wordCurrent[1000];
279 int i;
280 int startWord = ac.posStart - ac.startLen;
281 for (i = startWord; i < currentPos; i++)
282 wordCurrent[i - startWord] = pdoc->CharAt(i);
283 wordCurrent[i - startWord] = '\0';
284 ac.Select(wordCurrent);
285 }
286
287 void ScintillaBase::AutoCompleteCharacterAdded(char ch) {
288 if (ac.IsFillUpChar(ch)) {
289 AutoCompleteCompleted();
290 } else if (ac.IsStopChar(ch)) {
291 ac.Cancel();
292 } else {
293 AutoCompleteMoveToCurrentWord();
294 }
295 }
296
297 void ScintillaBase::AutoCompleteCharacterDeleted() {
298 if (currentPos <= ac.posStart - ac.startLen) {
299 ac.Cancel();
300 } else if (ac.cancelAtStartPos && (currentPos <= ac.posStart)) {
301 ac.Cancel();
302 } else {
303 AutoCompleteMoveToCurrentWord();
304 }
305 }
306
307 void ScintillaBase::AutoCompleteCompleted() {
308 int item = ac.lb->GetSelection();
309 char selected[1000];
310 selected[0] = '\0';
311 if (item != -1) {
312 ac.lb->GetValue(item, selected, sizeof(selected));
313 }
314 ac.Cancel();
315
316 if (listType > 0) {
317 userListSelected = selected;
318 SCNotification scn;
319 scn.nmhdr.code = SCN_USERLISTSELECTION;
320 scn.message = 0;
321 scn.wParam = listType;
322 scn.listType = listType;
323 scn.lParam = 0;
324 scn.text = userListSelected.c_str();
325 NotifyParent(scn);
326 return;
327 }
328
329 Position firstPos = ac.posStart - ac.startLen;
330 Position endPos = currentPos;
331 if (ac.dropRestOfWord)
332 endPos = pdoc->ExtendWordSelect(endPos, 1, true);
333 if (endPos < firstPos)
334 return;
335 pdoc->BeginUndoAction();
336 if (endPos != firstPos) {
337 pdoc->DeleteChars(firstPos, endPos - firstPos);
338 }
339 SetEmptySelection(ac.posStart);
340 if (item != -1) {
341 SString piece = selected;
342 pdoc->InsertString(firstPos, piece.c_str());
343 SetEmptySelection(firstPos + piece.length());
344 }
345 pdoc->EndUndoAction();
346 }
347
348 void ScintillaBase::CallTipShow(Point pt, const char *defn) {
349 AutoCompleteCancel();
350 pt.y += vs.lineHeight;
351 PRectangle rc = ct.CallTipStart(currentPos, pt,
352 defn,
353 vs.styles[STYLE_DEFAULT].fontName,
354 vs.styles[STYLE_DEFAULT].sizeZoomed,
355 IsUnicodeMode(),
356 wMain);
357 // If the call-tip window would be out of the client
358 // space, adjust so it displays above the text.
359 PRectangle rcClient = GetClientRectangle();
360 if (rc.bottom > rcClient.bottom) {
361 int offset = vs.lineHeight + rc.Height();
362 rc.top -= offset;
363 rc.bottom -= offset;
364 }
365 // Now display the window.
366 CreateCallTipWindow(rc);
367 ct.wCallTip.SetPositionRelative(rc, wMain);
368 ct.wCallTip.Show();
369 }
370
371 void ScintillaBase::CallTipClick() {
372 SCNotification scn;
373 scn.nmhdr.code = SCN_CALLTIPCLICK;
374 scn.position = ct.clickPlace;
375 NotifyParent(scn);
376 }
377
378 void ScintillaBase::ContextMenu(Point pt) {
379 if (displayPopupMenu) {
380 bool writable = !WndProc(SCI_GETREADONLY, 0, 0);
381 popup.CreatePopUp();
382 AddToPopUp("Undo", idcmdUndo, writable && pdoc->CanUndo());
383 AddToPopUp("Redo", idcmdRedo, writable && pdoc->CanRedo());
384 AddToPopUp("");
385 AddToPopUp("Cut", idcmdCut, writable && currentPos != anchor);
386 AddToPopUp("Copy", idcmdCopy, currentPos != anchor);
387 AddToPopUp("Paste", idcmdPaste, writable && WndProc(SCI_CANPASTE, 0, 0));
388 AddToPopUp("Delete", idcmdDelete, writable && currentPos != anchor);
389 AddToPopUp("");
390 AddToPopUp("Select All", idcmdSelectAll);
391 popup.Show(pt, wMain);
392 }
393 }
394
395 void ScintillaBase::CancelModes() {
396 AutoCompleteCancel();
397 ct.CallTipCancel();
398 Editor::CancelModes();
399 }
400
401 void ScintillaBase::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
402 CancelModes();
403 Editor::ButtonDown(pt, curTime, shift, ctrl, alt);
404 }
405
406 #ifdef SCI_LEXER
407 void ScintillaBase::SetLexer(uptr_t wParam) {
408 lexLanguage = wParam;
409 lexCurrent = LexerModule::Find(lexLanguage);
410 if (!lexCurrent)
411 lexCurrent = LexerModule::Find(SCLEX_NULL);
412 }
413
414 void ScintillaBase::SetLexerLanguage(const char *languageName) {
415 lexLanguage = SCLEX_CONTAINER;
416 lexCurrent = LexerModule::Find(languageName);
417 if (!lexCurrent)
418 lexCurrent = LexerModule::Find(SCLEX_NULL);
419 if (lexCurrent)
420 lexLanguage = lexCurrent->GetLanguage();
421 }
422
423 void ScintillaBase::Colourise(int start, int end) {
424 int lengthDoc = pdoc->Length();
425 if (end == -1)
426 end = lengthDoc;
427 int len = end - start;
428
429 PLATFORM_ASSERT(len >= 0);
430 PLATFORM_ASSERT(start + len <= lengthDoc);
431
432 //WindowAccessor styler(wMain.GetID(), props);
433 DocumentAccessor styler(pdoc, props, wMain.GetID());
434
435 int styleStart = 0;
436 if (start > 0)
437 styleStart = styler.StyleAt(start - 1);
438 styler.SetCodePage(pdoc->dbcsCodePage);
439
440 if (lexCurrent && (len > 0)) { // Should always succeed as null lexer should always be available
441 lexCurrent->Lex(start, len, styleStart, keyWordLists, styler);
442 styler.Flush();
443 if (styler.GetPropertyInt("fold")) {
444 lexCurrent->Fold(start, len, styleStart, keyWordLists, styler);
445 styler.Flush();
446 }
447 }
448 }
449 #endif
450
451 void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded) {
452 #ifdef SCI_LEXER
453 if (lexLanguage != SCLEX_CONTAINER) {
454 int endStyled = WndProc(SCI_GETENDSTYLED, 0, 0);
455 int lineEndStyled = WndProc(SCI_LINEFROMPOSITION, endStyled, 0);
456 endStyled = WndProc(SCI_POSITIONFROMLINE, lineEndStyled, 0);
457 Colourise(endStyled, endStyleNeeded);
458 return;
459 }
460 #endif
461 Editor::NotifyStyleToNeeded(endStyleNeeded);
462 }
463
464 sptr_t ScintillaBase::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
465 switch (iMessage) {
466 case SCI_AUTOCSHOW:
467 listType = 0;
468 AutoCompleteStart(wParam, reinterpret_cast<const char *>(lParam));
469 break;
470
471 case SCI_AUTOCCANCEL:
472 AutoCompleteCancel();
473 break;
474
475 case SCI_AUTOCACTIVE:
476 return ac.Active();
477
478 case SCI_AUTOCPOSSTART:
479 return ac.posStart;
480
481 case SCI_AUTOCCOMPLETE:
482 AutoCompleteCompleted();
483 break;
484
485 case SCI_AUTOCSETSEPARATOR:
486 ac.SetSeparator(static_cast<char>(wParam));
487 break;
488
489 case SCI_AUTOCGETSEPARATOR:
490 return ac.GetSeparator();
491
492 case SCI_AUTOCSTOPS:
493 ac.SetStopChars(reinterpret_cast<char *>(lParam));
494 break;
495
496 case SCI_AUTOCSELECT:
497 ac.Select(reinterpret_cast<char *>(lParam));
498 break;
499
500 case SCI_AUTOCSETCANCELATSTART:
501 ac.cancelAtStartPos = wParam != 0;
502 break;
503
504 case SCI_AUTOCGETCANCELATSTART:
505 return ac.cancelAtStartPos;
506
507 case SCI_AUTOCSETFILLUPS:
508 ac.SetFillUpChars(reinterpret_cast<char *>(lParam));
509 break;
510
511 case SCI_AUTOCSETCHOOSESINGLE:
512 ac.chooseSingle = wParam != 0;
513 break;
514
515 case SCI_AUTOCGETCHOOSESINGLE:
516 return ac.chooseSingle;
517
518 case SCI_AUTOCSETIGNORECASE:
519 ac.ignoreCase = wParam != 0;
520 break;
521
522 case SCI_AUTOCGETIGNORECASE:
523 return ac.ignoreCase;
524
525 case SCI_USERLISTSHOW:
526 listType = wParam;
527 AutoCompleteStart(0, reinterpret_cast<const char *>(lParam));
528 break;
529
530 case SCI_AUTOCSETAUTOHIDE:
531 ac.autoHide = wParam != 0;
532 break;
533
534 case SCI_AUTOCGETAUTOHIDE:
535 return ac.autoHide;
536
537 case SCI_AUTOCSETDROPRESTOFWORD:
538 ac.dropRestOfWord = wParam != 0;
539 break;
540
541 case SCI_AUTOCGETDROPRESTOFWORD:
542 return ac.dropRestOfWord;
543
544 case SCI_REGISTERIMAGE:
545 ac.lb->RegisterImage(wParam, reinterpret_cast<const char *>(lParam));
546 break;
547
548 case SCI_CLEARREGISTEREDIMAGES:
549 ac.lb->ClearRegisteredImages();
550 break;
551
552 case SCI_AUTOCSETTYPESEPARATOR:
553 ac.SetTypesep(static_cast<char>(wParam));
554 break;
555
556 case SCI_AUTOCGETTYPESEPARATOR:
557 return ac.GetTypesep();
558
559 case SCI_CALLTIPSHOW:
560 CallTipShow(LocationFromPosition(wParam),
561 reinterpret_cast<const char *>(lParam));
562 break;
563
564 case SCI_CALLTIPCANCEL:
565 ct.CallTipCancel();
566 break;
567
568 case SCI_CALLTIPACTIVE:
569 return ct.inCallTipMode;
570
571 case SCI_CALLTIPPOSSTART:
572 return ct.posStartCallTip;
573
574 case SCI_CALLTIPSETHLT:
575 ct.SetHighlight(wParam, lParam);
576 break;
577
578 case SCI_CALLTIPSETBACK:
579 ct.colourBG = ColourDesired(wParam);
580 InvalidateStyleRedraw();
581 break;
582
583 case SCI_CALLTIPSETFORE:
584 ct.colourUnSel = ColourDesired(wParam);
585 InvalidateStyleRedraw();
586 break;
587
588 case SCI_CALLTIPSETFOREHLT:
589 ct.colourSel = ColourDesired(wParam);
590 InvalidateStyleRedraw();
591 break;
592
593 case SCI_USEPOPUP:
594 displayPopupMenu = wParam != 0;
595 break;
596
597 #ifdef SCI_LEXER
598 case SCI_SETLEXER:
599 SetLexer(wParam);
600 lexLanguage = wParam;
601 break;
602
603 case SCI_GETLEXER:
604 return lexLanguage;
605
606 case SCI_COLOURISE:
607 Colourise(wParam, lParam);
608 Redraw();
609 break;
610
611 case SCI_SETPROPERTY:
612 props.Set(reinterpret_cast<const char *>(wParam),
613 reinterpret_cast<const char *>(lParam));
614 break;
615
616 case SCI_SETKEYWORDS:
617 if (wParam < numWordLists) {
618 keyWordLists[wParam]->Clear();
619 keyWordLists[wParam]->Set(reinterpret_cast<const char *>(lParam));
620 }
621 break;
622
623 case SCI_SETLEXERLANGUAGE:
624 SetLexerLanguage(reinterpret_cast<const char *>(lParam));
625 break;
626
627 #endif
628
629 default:
630 return Editor::WndProc(iMessage, wParam, lParam);
631 }
632 return 0l;
633 }