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