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