1
// Scintilla source code edit control
2
/** @file ScintillaBase.cxx
3
** An enhanced subclass of Editor with calltips, autocomplete and context menu.
5
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6
// The License.txt file describes the conditions under which this software may be distributed.
18
#include "Scintilla.h"
20
#include "PropSetSimple.h"
24
#include "DocumentAccessor.h"
27
#include "SplitVector.h"
28
#include "Partitioning.h"
29
#include "RunStyles.h"
30
#include "ContractionState.h"
31
#include "CellBuffer.h"
34
#include "Indicator.h"
36
#include "LineMarker.h"
38
#include "ViewStyle.h"
39
#include "AutoComplete.h"
40
#include "CharClassify.h"
41
#include "Decoration.h"
43
#include "Selection.h"
44
#include "PositionCache.h"
46
#include "ScintillaBase.h"
49
using namespace Scintilla;
52
ScintillaBase::ScintillaBase() {
53
displayPopupMenu = true;
57
lexLanguage = SCLEX_CONTAINER;
58
performingStyle = false;
60
for (int wl = 0; wl < numWordLists; wl++)
61
keyWordLists[wl] = new WordList;
62
keyWordLists[numWordLists] = 0;
66
ScintillaBase::~ScintillaBase() {
68
for (int wl = 0; wl < numWordLists; wl++)
69
delete keyWordLists[wl];
73
void ScintillaBase::Finalise() {
78
void ScintillaBase::RefreshColourPalette(Palette &pal, bool want) {
79
Editor::RefreshColourPalette(pal, want);
80
ct.RefreshColourPalette(pal, want);
83
void ScintillaBase::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
84
bool isFillUp = ac.Active() && ac.IsFillUpChar(*s);
86
Editor::AddCharUTF(s, len, treatAsDBCS);
89
AutoCompleteCharacterAdded(s[0]);
90
// For fill ups add the character after the autocompletion has
91
// triggered so containers see the key so can display a calltip.
93
Editor::AddCharUTF(s, len, treatAsDBCS);
98
void ScintillaBase::Command(int cmdId) {
102
case idAutoComplete: // Nothing to do
106
case idCallTip: // Nothing to do
111
WndProc(SCI_UNDO, 0, 0);
115
WndProc(SCI_REDO, 0, 0);
119
WndProc(SCI_CUT, 0, 0);
123
WndProc(SCI_COPY, 0, 0);
127
WndProc(SCI_PASTE, 0, 0);
131
WndProc(SCI_CLEAR, 0, 0);
135
WndProc(SCI_SELECTALL, 0, 0);
140
int ScintillaBase::KeyCommand(unsigned int iMessage) {
141
// Most key commands cancel autocompletion mode
149
AutoCompleteMove( -1);
155
AutoCompleteMove( -5);
158
AutoCompleteMove( -5000);
161
AutoCompleteMove(5000);
165
AutoCompleteCharacterDeleted();
166
EnsureCaretVisible();
168
case SCI_DELETEBACKNOTLINE:
170
AutoCompleteCharacterDeleted();
171
EnsureCaretVisible();
174
AutoCompleteCompleted();
177
AutoCompleteCompleted();
181
AutoCompleteCancel();
185
if (ct.inCallTipMode) {
187
(iMessage != SCI_CHARLEFT) &&
188
(iMessage != SCI_CHARLEFTEXTEND) &&
189
(iMessage != SCI_CHARRIGHT) &&
190
(iMessage != SCI_CHARRIGHTEXTEND) &&
191
(iMessage != SCI_EDITTOGGLEOVERTYPE) &&
192
(iMessage != SCI_DELETEBACK) &&
193
(iMessage != SCI_DELETEBACKNOTLINE)
197
if ((iMessage == SCI_DELETEBACK) || (iMessage == SCI_DELETEBACKNOTLINE)) {
198
if (sel.MainCaret() <= ct.posStartCallTip) {
203
return Editor::KeyCommand(iMessage);
206
void ScintillaBase::AutoCompleteDoubleClick(void *p) {
207
ScintillaBase *sci = reinterpret_cast<ScintillaBase *>(p);
208
sci->AutoCompleteCompleted();
211
void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) {
212
//Platform::DebugPrintf("AutoComplete %s\n", list);
215
if (ac.chooseSingle && (listType == 0)) {
216
if (list && !strchr(list, ac.GetSeparator())) {
217
const char *typeSep = strchr(list, ac.GetTypesep());
218
size_t lenInsert = (typeSep) ? (typeSep-list) : strlen(list);
220
SetEmptySelection(sel.MainCaret() - lenEntered);
221
pdoc->DeleteChars(sel.MainCaret(), lenEntered);
222
SetEmptySelection(sel.MainCaret());
223
pdoc->InsertString(sel.MainCaret(), list, lenInsert);
224
SetEmptySelection(sel.MainCaret() + lenInsert);
226
SetEmptySelection(sel.MainCaret());
227
pdoc->InsertString(sel.MainCaret(), list + lenEntered, lenInsert - lenEntered);
228
SetEmptySelection(sel.MainCaret() + lenInsert - lenEntered);
233
ac.Start(wMain, idAutoComplete, sel.MainCaret(), PointMainCaret(),
234
lenEntered, vs.lineHeight, IsUnicodeMode());
236
PRectangle rcClient = GetClientRectangle();
237
Point pt = LocationFromPosition(sel.MainCaret() - lenEntered);
238
PRectangle rcPopupBounds = wMain.GetMonitorRect(pt);
239
if (rcPopupBounds.Height() == 0)
240
rcPopupBounds = rcClient;
244
if (pt.x >= rcClient.right - widthLB) {
245
HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB);
247
pt = PointMainCaret();
250
rcac.left = pt.x - ac.lb->CaretFromEdge();
251
if (pt.y >= rcPopupBounds.bottom - heightLB && // Wont fit below.
252
pt.y >= (rcPopupBounds.bottom + rcPopupBounds.top) / 2) { // and there is more room above.
253
rcac.top = pt.y - heightLB;
254
if (rcac.top < rcPopupBounds.top) {
255
heightLB -= (rcPopupBounds.top - rcac.top);
256
rcac.top = rcPopupBounds.top;
259
rcac.top = pt.y + vs.lineHeight;
261
rcac.right = rcac.left + widthLB;
262
rcac.bottom = Platform::Minimum(rcac.top + heightLB, rcPopupBounds.bottom);
263
ac.lb->SetPositionRelative(rcac, wMain);
264
ac.lb->SetFont(vs.styles[STYLE_DEFAULT].font);
265
unsigned int aveCharWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
266
ac.lb->SetAverageCharWidth(aveCharWidth);
267
ac.lb->SetDoubleClickAction(AutoCompleteDoubleClick, this);
271
// Fiddle the position of the list so it is right next to the target and wide enough for all its strings
272
PRectangle rcList = ac.lb->GetDesiredRect();
273
int heightAlloced = rcList.bottom - rcList.top;
274
widthLB = Platform::Maximum(widthLB, rcList.right - rcList.left);
275
if (maxListWidth != 0)
276
widthLB = Platform::Minimum(widthLB, aveCharWidth*maxListWidth);
277
// Make an allowance for large strings in list
278
rcList.left = pt.x - ac.lb->CaretFromEdge();
279
rcList.right = rcList.left + widthLB;
280
if (((pt.y + vs.lineHeight) >= (rcPopupBounds.bottom - heightAlloced)) && // Wont fit below.
281
((pt.y + vs.lineHeight / 2) >= (rcPopupBounds.bottom + rcPopupBounds.top) / 2)) { // and there is more room above.
282
rcList.top = pt.y - heightAlloced;
284
rcList.top = pt.y + vs.lineHeight;
286
rcList.bottom = rcList.top + heightAlloced;
287
ac.lb->SetPositionRelative(rcList, wMain);
289
if (lenEntered != 0) {
290
AutoCompleteMoveToCurrentWord();
294
void ScintillaBase::AutoCompleteCancel() {
296
SCNotification scn = {0};
297
scn.nmhdr.code = SCN_AUTOCCANCELLED;
305
void ScintillaBase::AutoCompleteMove(int delta) {
309
void ScintillaBase::AutoCompleteMoveToCurrentWord() {
310
char wordCurrent[1000];
312
int startWord = ac.posStart - ac.startLen;
313
for (i = startWord; i < sel.MainCaret() && i - startWord < 1000; i++)
314
wordCurrent[i - startWord] = pdoc->CharAt(i);
315
wordCurrent[Platform::Minimum(i - startWord, 999)] = '\0';
316
ac.Select(wordCurrent);
319
void ScintillaBase::AutoCompleteCharacterAdded(char ch) {
320
if (ac.IsFillUpChar(ch)) {
321
AutoCompleteCompleted();
322
} else if (ac.IsStopChar(ch)) {
323
AutoCompleteCancel();
325
AutoCompleteMoveToCurrentWord();
329
void ScintillaBase::AutoCompleteCharacterDeleted() {
330
if (sel.MainCaret() < ac.posStart - ac.startLen) {
331
AutoCompleteCancel();
332
} else if (ac.cancelAtStartPos && (sel.MainCaret() <= ac.posStart)) {
333
AutoCompleteCancel();
335
AutoCompleteMoveToCurrentWord();
337
SCNotification scn = {0};
338
scn.nmhdr.code = SCN_AUTOCCHARDELETED;
344
void ScintillaBase::AutoCompleteCompleted() {
345
int item = ac.lb->GetSelection();
349
ac.lb->GetValue(item, selected, sizeof(selected));
351
AutoCompleteCancel();
357
SCNotification scn = {0};
358
scn.nmhdr.code = listType > 0 ? SCN_USERLISTSELECTION : SCN_AUTOCSELECTION;
360
scn.wParam = listType;
361
scn.listType = listType;
362
Position firstPos = ac.posStart - ac.startLen;
363
scn.lParam = firstPos;
374
Position endPos = sel.MainCaret();
375
if (ac.dropRestOfWord)
376
endPos = pdoc->ExtendWordSelect(endPos, 1, true);
377
if (endPos < firstPos)
380
if (endPos != firstPos) {
381
pdoc->DeleteChars(firstPos, endPos - firstPos);
383
SetEmptySelection(ac.posStart);
385
pdoc->InsertCString(firstPos, selected);
386
SetEmptySelection(firstPos + static_cast<int>(strlen(selected)));
390
int ScintillaBase::AutoCompleteGetCurrent() {
393
return ac.lb->GetSelection();
396
int ScintillaBase::AutoCompleteGetCurrentText(char *buffer) {
398
int item = ac.lb->GetSelection();
402
ac.lb->GetValue(item, selected, sizeof(selected));
404
strcpy(buffer, selected);
405
return strlen(selected);
413
void ScintillaBase::CallTipShow(Point pt, const char *defn) {
415
pt.y += vs.lineHeight;
416
// If container knows about STYLE_CALLTIP then use it in place of the
417
// STYLE_DEFAULT for the face name, size and character set. Also use it
418
// for the foreground and background colour.
419
int ctStyle = ct.UseStyleCallTip() ? STYLE_CALLTIP : STYLE_DEFAULT;
420
if (ct.UseStyleCallTip()) {
421
ct.SetForeBack(vs.styles[STYLE_CALLTIP].fore, vs.styles[STYLE_CALLTIP].back);
423
PRectangle rc = ct.CallTipStart(sel.MainCaret(), pt,
425
vs.styles[ctStyle].fontName,
426
vs.styles[ctStyle].sizeZoomed,
428
vs.styles[ctStyle].characterSet,
430
// If the call-tip window would be out of the client
431
// space, adjust so it displays above the text.
432
PRectangle rcClient = GetClientRectangle();
433
if (rc.bottom > rcClient.bottom) {
434
int offset = vs.lineHeight + rc.Height();
438
// Now display the window.
439
CreateCallTipWindow(rc);
440
ct.wCallTip.SetPositionRelative(rc, wMain);
444
void ScintillaBase::CallTipClick() {
445
SCNotification scn = {0};
446
scn.nmhdr.code = SCN_CALLTIPCLICK;
447
scn.position = ct.clickPlace;
451
void ScintillaBase::ContextMenu(Point pt) {
452
if (displayPopupMenu) {
453
bool writable = !WndProc(SCI_GETREADONLY, 0, 0);
455
AddToPopUp("Undo", idcmdUndo, writable && pdoc->CanUndo());
456
AddToPopUp("Redo", idcmdRedo, writable && pdoc->CanRedo());
458
AddToPopUp("Cut", idcmdCut, writable && !sel.Empty());
459
AddToPopUp("Copy", idcmdCopy, !sel.Empty());
460
AddToPopUp("Paste", idcmdPaste, writable && WndProc(SCI_CANPASTE, 0, 0));
461
AddToPopUp("Delete", idcmdDelete, writable && !sel.Empty());
463
AddToPopUp("Select All", idcmdSelectAll);
464
popup.Show(pt, wMain);
468
void ScintillaBase::CancelModes() {
469
AutoCompleteCancel();
471
Editor::CancelModes();
474
void ScintillaBase::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
476
Editor::ButtonDown(pt, curTime, shift, ctrl, alt);
480
void ScintillaBase::SetLexer(uptr_t wParam) {
481
lexLanguage = wParam;
482
lexCurrent = LexerModule::Find(lexLanguage);
484
lexCurrent = LexerModule::Find(SCLEX_NULL);
485
int bits = lexCurrent ? lexCurrent->GetStyleBitsNeeded() : 5;
486
vs.EnsureStyle((1 << bits) - 1);
489
void ScintillaBase::SetLexerLanguage(const char *languageName) {
490
lexLanguage = SCLEX_CONTAINER;
491
lexCurrent = LexerModule::Find(languageName);
493
lexCurrent = LexerModule::Find(SCLEX_NULL);
495
lexLanguage = lexCurrent->GetLanguage();
496
int bits = lexCurrent ? lexCurrent->GetStyleBitsNeeded() : 5;
497
vs.EnsureStyle((1 << bits) - 1);
500
void ScintillaBase::Colourise(int start, int end) {
501
if (!performingStyle) {
502
// Protect against reentrance, which may occur, for example, when
503
// fold points are discovered while performing styling and the folding
504
// code looks for child lines which may trigger styling.
505
performingStyle = true;
507
int lengthDoc = pdoc->Length();
510
int len = end - start;
512
PLATFORM_ASSERT(len >= 0);
513
PLATFORM_ASSERT(start + len <= lengthDoc);
515
//WindowAccessor styler(wMain.GetID(), props);
516
DocumentAccessor styler(pdoc, props, wMain.GetID());
520
styleStart = styler.StyleAt(start - 1) & pdoc->stylingBitsMask;
521
styler.SetCodePage(pdoc->dbcsCodePage);
523
if (lexCurrent && (len > 0)) { // Should always succeed as null lexer should always be available
524
lexCurrent->Lex(start, len, styleStart, keyWordLists, styler);
526
if (styler.GetPropertyInt("fold")) {
527
lexCurrent->Fold(start, len, styleStart, keyWordLists, styler);
532
performingStyle = false;
537
void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded) {
539
if (lexLanguage != SCLEX_CONTAINER) {
540
int endStyled = WndProc(SCI_GETENDSTYLED, 0, 0);
541
int lineEndStyled = WndProc(SCI_LINEFROMPOSITION, endStyled, 0);
542
endStyled = WndProc(SCI_POSITIONFROMLINE, lineEndStyled, 0);
543
Colourise(endStyled, endStyleNeeded);
547
Editor::NotifyStyleToNeeded(endStyleNeeded);
550
sptr_t ScintillaBase::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
554
AutoCompleteStart(wParam, reinterpret_cast<const char *>(lParam));
557
case SCI_AUTOCCANCEL:
561
case SCI_AUTOCACTIVE:
564
case SCI_AUTOCPOSSTART:
567
case SCI_AUTOCCOMPLETE:
568
AutoCompleteCompleted();
571
case SCI_AUTOCSETSEPARATOR:
572
ac.SetSeparator(static_cast<char>(wParam));
575
case SCI_AUTOCGETSEPARATOR:
576
return ac.GetSeparator();
579
ac.SetStopChars(reinterpret_cast<char *>(lParam));
582
case SCI_AUTOCSELECT:
583
ac.Select(reinterpret_cast<char *>(lParam));
586
case SCI_AUTOCGETCURRENT:
587
return AutoCompleteGetCurrent();
589
case SCI_AUTOCGETCURRENTTEXT:
590
return AutoCompleteGetCurrentText(reinterpret_cast<char *>(lParam));
592
case SCI_AUTOCSETCANCELATSTART:
593
ac.cancelAtStartPos = wParam != 0;
596
case SCI_AUTOCGETCANCELATSTART:
597
return ac.cancelAtStartPos;
599
case SCI_AUTOCSETFILLUPS:
600
ac.SetFillUpChars(reinterpret_cast<char *>(lParam));
603
case SCI_AUTOCSETCHOOSESINGLE:
604
ac.chooseSingle = wParam != 0;
607
case SCI_AUTOCGETCHOOSESINGLE:
608
return ac.chooseSingle;
610
case SCI_AUTOCSETIGNORECASE:
611
ac.ignoreCase = wParam != 0;
614
case SCI_AUTOCGETIGNORECASE:
615
return ac.ignoreCase;
617
case SCI_USERLISTSHOW:
619
AutoCompleteStart(0, reinterpret_cast<const char *>(lParam));
622
case SCI_AUTOCSETAUTOHIDE:
623
ac.autoHide = wParam != 0;
626
case SCI_AUTOCGETAUTOHIDE:
629
case SCI_AUTOCSETDROPRESTOFWORD:
630
ac.dropRestOfWord = wParam != 0;
633
case SCI_AUTOCGETDROPRESTOFWORD:
634
return ac.dropRestOfWord;
636
case SCI_AUTOCSETMAXHEIGHT:
637
ac.lb->SetVisibleRows(wParam);
640
case SCI_AUTOCGETMAXHEIGHT:
641
return ac.lb->GetVisibleRows();
643
case SCI_AUTOCSETMAXWIDTH:
644
maxListWidth = wParam;
647
case SCI_AUTOCGETMAXWIDTH:
650
case SCI_REGISTERIMAGE:
651
ac.lb->RegisterImage(wParam, reinterpret_cast<const char *>(lParam));
654
case SCI_CLEARREGISTEREDIMAGES:
655
ac.lb->ClearRegisteredImages();
658
case SCI_AUTOCSETTYPESEPARATOR:
659
ac.SetTypesep(static_cast<char>(wParam));
662
case SCI_AUTOCGETTYPESEPARATOR:
663
return ac.GetTypesep();
665
case SCI_CALLTIPSHOW:
666
CallTipShow(LocationFromPosition(wParam),
667
reinterpret_cast<const char *>(lParam));
670
case SCI_CALLTIPCANCEL:
674
case SCI_CALLTIPACTIVE:
675
return ct.inCallTipMode;
677
case SCI_CALLTIPPOSSTART:
678
return ct.posStartCallTip;
680
case SCI_CALLTIPSETHLT:
681
ct.SetHighlight(wParam, lParam);
684
case SCI_CALLTIPSETBACK:
685
ct.colourBG = ColourDesired(wParam);
686
vs.styles[STYLE_CALLTIP].back = ct.colourBG;
687
InvalidateStyleRedraw();
690
case SCI_CALLTIPSETFORE:
691
ct.colourUnSel = ColourDesired(wParam);
692
vs.styles[STYLE_CALLTIP].fore = ct.colourUnSel;
693
InvalidateStyleRedraw();
696
case SCI_CALLTIPSETFOREHLT:
697
ct.colourSel = ColourDesired(wParam);
698
InvalidateStyleRedraw();
701
case SCI_CALLTIPUSESTYLE:
702
ct.SetTabSize((int)wParam);
703
InvalidateStyleRedraw();
707
displayPopupMenu = wParam != 0;
713
lexLanguage = wParam;
720
if (lexLanguage == SCLEX_CONTAINER) {
721
pdoc->ModifiedAt(wParam);
722
NotifyStyleToNeeded((lParam == -1) ? pdoc->Length() : lParam);
724
Colourise(wParam, lParam);
729
case SCI_SETPROPERTY:
730
props.Set(reinterpret_cast<const char *>(wParam),
731
reinterpret_cast<const char *>(lParam));
734
case SCI_GETPROPERTY:
735
return StringResult(lParam, props.Get(reinterpret_cast<const char *>(wParam)));
737
case SCI_GETPROPERTYEXPANDED: {
738
char *val = props.Expanded(reinterpret_cast<const char *>(wParam));
739
const int n = strlen(val);
741
char *ptr = reinterpret_cast<char *>(lParam);
745
return n; // Not including NUL
748
case SCI_GETPROPERTYINT:
749
return props.GetInt(reinterpret_cast<const char *>(wParam), lParam);
751
case SCI_SETKEYWORDS:
752
if (wParam < numWordLists) {
753
keyWordLists[wParam]->Clear();
754
keyWordLists[wParam]->Set(reinterpret_cast<const char *>(lParam));
758
case SCI_SETLEXERLANGUAGE:
759
SetLexerLanguage(reinterpret_cast<const char *>(lParam));
762
case SCI_GETLEXERLANGUAGE:
763
return StringResult(lParam, lexCurrent ? lexCurrent->languageName : "");
765
case SCI_GETSTYLEBITSNEEDED:
766
return lexCurrent ? lexCurrent->GetStyleBitsNeeded() : 5;
771
return Editor::WndProc(iMessage, wParam, lParam);