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