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-2001 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
15 #include "Scintilla.h"
20 #include "DocumentAccessor.h"
23 #include "ContractionState.h"
25 #include "CellBuffer.h"
28 #include "Indicator.h"
29 #include "LineMarker.h"
31 #include "ViewStyle.h"
32 #include "AutoComplete.h"
35 #include "ScintillaBase.h"
37 ScintillaBase::ScintillaBase() {
40 lexLanguage
= SCLEX_CONTAINER
;
42 for (int wl
= 0;wl
< numWordLists
;wl
++)
43 keyWordLists
[wl
] = new WordList
;
44 keyWordLists
[numWordLists
] = 0;
48 ScintillaBase::~ScintillaBase() {
50 for (int wl
= 0;wl
< numWordLists
;wl
++)
51 delete keyWordLists
[wl
];
55 void ScintillaBase::Finalise() {
60 void ScintillaBase::RefreshColourPalette(Palette
&pal
, bool want
) {
61 Editor::RefreshColourPalette(pal
, want
);
62 ct
.RefreshColourPalette(pal
, want
);
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]);
73 void ScintillaBase::Command(int cmdId
) {
77 case idAutoComplete
: // Nothing to do
81 case idCallTip
: // Nothing to do
86 WndProc(SCI_UNDO
, 0, 0);
90 WndProc(SCI_REDO
, 0, 0);
94 WndProc(SCI_CUT
, 0, 0);
98 WndProc(SCI_COPY
, 0, 0);
102 WndProc(SCI_PASTE
, 0, 0);
106 WndProc(SCI_CLEAR
, 0, 0);
110 WndProc(SCI_SELECTALL
, 0, 0);
115 int ScintillaBase::KeyCommand(unsigned int iMessage
) {
116 // Most key commands cancel autocompletion mode
124 AutoCompleteMove( -1);
130 AutoCompleteMove( -5);
133 AutoCompleteMove( -5000);
136 AutoCompleteMove(5000);
140 AutoCompleteChanged();
141 EnsureCaretVisible();
144 AutoCompleteCompleted();
147 AutoCompleteCompleted();
155 if (ct
.inCallTipMode
) {
157 (iMessage
!= SCI_CHARLEFT
) &&
158 (iMessage
!= SCI_CHARLEFTEXTEND
) &&
159 (iMessage
!= SCI_CHARRIGHT
) &&
160 (iMessage
!= SCI_CHARLEFTEXTEND
) &&
161 (iMessage
!= SCI_EDITTOGGLEOVERTYPE
) &&
162 (iMessage
!= SCI_DELETEBACK
)
166 if (iMessage
== SCI_DELETEBACK
) {
167 if (currentPos
<= ct
.posStartCallTip
) {
172 return Editor::KeyCommand(iMessage
);
175 void ScintillaBase::AutoCompleteStart(int lenEntered
, const char *list
) {
176 //Platform::DebugPrintf("AutoComplete %s\n", list);
179 if (ac
.chooseSingle
&& (listType
== 0)) {
180 if (list
&& !strchr(list
, ac
.GetSeparator())) {
182 SetEmptySelection(currentPos
- lenEntered
);
183 pdoc
->DeleteChars(currentPos
, lenEntered
);
184 SetEmptySelection(currentPos
);
185 pdoc
->InsertString(currentPos
, list
);
186 SetEmptySelection(currentPos
+ strlen(list
));
188 SetEmptySelection(currentPos
);
189 pdoc
->InsertString(currentPos
, list
+ lenEntered
);
190 SetEmptySelection(currentPos
+ strlen(list
+ lenEntered
));
195 ac
.Start(wMain
, idAutoComplete
, currentPos
, lenEntered
);
197 PRectangle rcClient
= GetClientRectangle();
198 Point pt
= LocationFromPosition(currentPos
- lenEntered
);
202 if (pt
.x
>= rcClient
.right
- widthLB
) {
203 HorizontalScrollTo(xOffset
+ pt
.x
- rcClient
.right
+ widthLB
);
205 pt
= LocationFromPosition(currentPos
);
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
;
213 heightLB
+= rcac
.top
;
217 rcac
.top
= pt
.y
+ vs
.lineHeight
;
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
);
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
;
238 rcList
.top
= pt
.y
+ vs
.lineHeight
;
240 rcList
.bottom
= rcList
.top
+ heightAlloced
;
241 ac
.lb
.SetPositionRelative(rcList
, wMain
);
243 if (lenEntered
!= 0) {
244 AutoCompleteMoveToCurrentWord();
248 void ScintillaBase::AutoCompleteCancel() {
252 void ScintillaBase::AutoCompleteMove(int delta
) {
256 void ScintillaBase::AutoCompleteMoveToCurrentWord() {
257 char wordCurrent
[1000];
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
);
266 void ScintillaBase::AutoCompleteChanged(char ch
) {
267 if (ac
.IsFillUpChar(ch
)) {
268 AutoCompleteCompleted(ch
);
269 } else if (currentPos
<= ac
.posStart
- ac
.startLen
) {
271 } else if (ac
.cancelAtStartPos
&& currentPos
<= ac
.posStart
) {
273 } else if (ac
.IsStopChar(ch
)) {
276 AutoCompleteMoveToCurrentWord();
280 void ScintillaBase::AutoCompleteCompleted(char fillUp
/*='\0'*/) {
281 int item
= ac
.lb
.GetSelection();
284 ac
.lb
.GetValue(item
, selected
, sizeof(selected
));
289 userListSelected
= selected
;
291 scn
.nmhdr
.code
= SCN_USERLISTSELECTION
;
293 scn
.wParam
= listType
;
295 scn
.text
= userListSelected
.c_str();
300 Position firstPos
= ac
.posStart
- ac
.startLen
;
301 if (currentPos
< firstPos
)
303 if (currentPos
!= firstPos
) {
304 pdoc
->DeleteChars(firstPos
, currentPos
- firstPos
);
306 SetEmptySelection(ac
.posStart
);
308 SString piece
= selected
;
311 pdoc
->InsertString(firstPos
, piece
.c_str());
312 SetEmptySelection(firstPos
+ piece
.length());
316 void ScintillaBase::ContextMenu(Point pt
) {
317 bool writable
= !WndProc(SCI_GETREADONLY
, 0, 0);
319 AddToPopUp("Undo", idcmdUndo
, writable
&& pdoc
->CanUndo());
320 AddToPopUp("Redo", idcmdRedo
, writable
&& pdoc
->CanRedo());
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
);
327 AddToPopUp("Select All", idcmdSelectAll
);
328 popup
.Show(pt
, wMain
);
331 void ScintillaBase::CancelModes() {
332 AutoCompleteCancel();
334 Editor::CancelModes();
337 void ScintillaBase::ButtonDown(Point pt
, unsigned int curTime
, bool shift
, bool ctrl
, bool alt
) {
339 Editor::ButtonDown(pt
, curTime
, shift
, ctrl
, alt
);
343 void ScintillaBase::SetLexer(uptr_t wParam
) {
344 lexLanguage
= wParam
;
345 lexCurrent
= LexerModule::Find(lexLanguage
);
347 lexCurrent
= LexerModule::Find(SCLEX_NULL
);
350 void ScintillaBase::SetLexerLanguage(const char *languageName
) {
351 lexLanguage
= SCLEX_CONTAINER
;
352 lexCurrent
= LexerModule::Find(languageName
);
354 lexCurrent
= LexerModule::Find(SCLEX_NULL
);
356 lexLanguage
= lexCurrent
->GetLanguage();
359 void ScintillaBase::Colourise(int start
, int end
) {
360 int lengthDoc
= pdoc
->Length();
363 int len
= end
- start
;
365 PLATFORM_ASSERT(len
>= 0);
366 PLATFORM_ASSERT(start
+ len
<= lengthDoc
);
368 //WindowAccessor styler(wMain.GetID(), props);
369 DocumentAccessor
styler(pdoc
, props
, wMain
.GetID());
373 styleStart
= styler
.StyleAt(start
- 1);
374 styler
.SetCodePage(pdoc
->dbcsCodePage
);
376 if (lexCurrent
) { // Should always succeed as null lexer should always be available
377 lexCurrent
->Lex(start
, len
, styleStart
, keyWordLists
, styler
);
379 if (styler
.GetPropertyInt("fold")) {
380 lexCurrent
->Fold(start
, len
, styleStart
, keyWordLists
, styler
);
387 void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded
) {
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
);
397 Editor::NotifyStyleToNeeded(endStyleNeeded
);
400 sptr_t
ScintillaBase::WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
404 AutoCompleteStart(wParam
, reinterpret_cast<const char *>(lParam
));
407 case SCI_AUTOCCANCEL
:
408 AutoCompleteCancel();
411 case SCI_AUTOCACTIVE
:
414 case SCI_AUTOCPOSSTART
:
417 case SCI_AUTOCCOMPLETE
:
418 AutoCompleteCompleted();
421 case SCI_AUTOCSETSEPARATOR
:
422 ac
.SetSeparator(static_cast<char>(wParam
));
425 case SCI_AUTOCGETSEPARATOR
:
426 return ac
.GetSeparator();
429 ac
.SetStopChars(reinterpret_cast<char *>(lParam
));
432 case SCI_AUTOCSELECT
:
433 ac
.Select(reinterpret_cast<char *>(lParam
));
436 case SCI_AUTOCSETCANCELATSTART
:
437 ac
.cancelAtStartPos
= wParam
;
440 case SCI_AUTOCGETCANCELATSTART
:
441 return ac
.cancelAtStartPos
;
443 case SCI_AUTOCSETFILLUPS
:
444 ac
.SetFillUpChars(reinterpret_cast<char *>(lParam
));
447 case SCI_AUTOCSETCHOOSESINGLE
:
448 ac
.chooseSingle
= wParam
;
451 case SCI_AUTOCGETCHOOSESINGLE
:
452 return ac
.chooseSingle
;
454 case SCI_AUTOCSETIGNORECASE
:
455 ac
.ignoreCase
= wParam
;
458 case SCI_AUTOCGETIGNORECASE
:
459 return ac
.ignoreCase
;
461 case SCI_USERLISTSHOW
:
463 AutoCompleteStart(0, reinterpret_cast<const char *>(lParam
));
466 case SCI_AUTOCSETAUTOHIDE
:
467 ac
.autoHide
= wParam
;
470 case SCI_AUTOCGETAUTOHIDE
:
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();
490 // Now display the window.
491 CreateCallTipWindow(rc
);
492 ct
.wCallTip
.SetPositionRelative(rc
, wMain
);
498 case SCI_CALLTIPCANCEL
:
502 case SCI_CALLTIPACTIVE
:
503 return ct
.inCallTipMode
;
505 case SCI_CALLTIPPOSSTART
:
506 return ct
.posStartCallTip
;
508 case SCI_CALLTIPSETHLT
:
509 ct
.SetHighlight(wParam
, lParam
);
512 case SCI_CALLTIPSETBACK
:
513 ct
.colourBG
= Colour(wParam
);
514 InvalidateStyleRedraw();
520 lexLanguage
= wParam
;
527 Colourise(wParam
, lParam
);
531 case SCI_SETPROPERTY
:
532 props
.Set(reinterpret_cast<const char *>(wParam
),
533 reinterpret_cast<const char *>(lParam
));
536 case SCI_SETKEYWORDS
:
537 if (wParam
< numWordLists
) {
538 keyWordLists
[wParam
]->Clear();
539 keyWordLists
[wParam
]->Set(reinterpret_cast<const char *>(lParam
));
543 case SCI_SETLEXERLANGUAGE
:
544 SetLexerLanguage(reinterpret_cast<const char *>(lParam
));
550 return Editor::WndProc(iMessage
, wParam
, lParam
);