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