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