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
);