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.
13 #include "Scintilla.h"
18 #include "WindowAccessor.h"
19 #include "DocumentAccessor.h"
22 #include "ContractionState.h"
24 #include "CellBuffer.h"
27 #include "Indicator.h"
28 #include "LineMarker.h"
30 #include "ViewStyle.h"
31 #include "AutoComplete.h"
34 #include "ScintillaBase.h"
36 ScintillaBase::ScintillaBase() {
38 lexLanguage
= SCLEX_CONTAINER
;
39 for (int wl
=0;wl
<numWordLists
;wl
++)
40 keyWordLists
[wl
] = new WordList
;
44 ScintillaBase::~ScintillaBase() {
46 for (int wl
=0;wl
<numWordLists
;wl
++)
47 delete keyWordLists
[wl
];
51 void ScintillaBase::Finalise() {
56 void ScintillaBase::RefreshColourPalette(Palette
&pal
, bool want
) {
57 Editor::RefreshColourPalette(pal
, want
);
58 ct
.RefreshColourPalette(pal
, want
);
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]);
69 void ScintillaBase::Command(int cmdId
) {
73 case idAutoComplete
: // Nothing to do
76 case idCallTip
: // Nothing to do
80 WndProc(SCI_UNDO
, 0, 0);
84 WndProc(SCI_REDO
, 0, 0);
88 WndProc(SCI_CUT
, 0, 0);
92 WndProc(SCI_COPY
, 0, 0);
96 WndProc(SCI_PASTE
, 0, 0);
100 WndProc(SCI_CLEAR
, 0, 0);
104 WndProc(SCI_SELECTALL
, 0, 0);
109 int ScintillaBase::KeyCommand(unsigned int iMessage
) {
110 // Most key commands cancel autocompletion mode
118 AutoCompleteMove( -1);
124 AutoCompleteMove( -5);
127 AutoCompleteMove( -5000);
130 AutoCompleteMove(5000);
134 AutoCompleteChanged();
135 EnsureCaretVisible();
138 AutoCompleteCompleted();
141 AutoCompleteCompleted();
149 if (ct
.inCallTipMode
) {
151 (iMessage
!= SCI_CHARLEFT
) &&
152 (iMessage
!= SCI_CHARLEFTEXTEND
) &&
153 (iMessage
!= SCI_CHARRIGHT
) &&
154 (iMessage
!= SCI_CHARLEFTEXTEND
) &&
155 (iMessage
!= SCI_EDITTOGGLEOVERTYPE
) &&
156 (iMessage
!= SCI_DELETEBACK
)
160 if (iMessage
== SCI_DELETEBACK
) {
161 if (currentPos
<= ct
.posStartCallTip
) {
166 return Editor::KeyCommand(iMessage
);
169 void ScintillaBase::AutoCompleteStart(int lenEntered
, const char *list
) {
170 //Platform::DebugPrintf("AutoComplete %s\n", list);
173 if (ac
.chooseSingle
) {
174 if (list
&& !strchr(list
, ac
.GetSeparator())) {
176 SetEmptySelection(currentPos
- lenEntered
);
177 pdoc
->DeleteChars(currentPos
, lenEntered
);
178 SetEmptySelection(currentPos
);
179 pdoc
->InsertString(currentPos
, list
);
180 SetEmptySelection(currentPos
+ strlen(list
));
182 SetEmptySelection(currentPos
);
183 pdoc
->InsertString(currentPos
, list
+ lenEntered
);
184 SetEmptySelection(currentPos
+ strlen(list
+ lenEntered
));
189 ac
.Start(wDraw
, idAutoComplete
, currentPos
, lenEntered
);
191 PRectangle rcClient
= GetClientRectangle();
192 Point pt
= LocationFromPosition(currentPos
-lenEntered
);
196 if (pt
.x
>= rcClient
.right
- widthLB
) {
197 HorizontalScrollTo(xOffset
+ pt
.x
- rcClient
.right
+ widthLB
);
199 pt
= LocationFromPosition(currentPos
);
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
;
207 heightLB
+= rcac
.top
;
211 rcac
.top
= pt
.y
+ vs
.lineHeight
;
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
);
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
;
232 rcList
.top
= pt
.y
+ vs
.lineHeight
;
234 rcList
.bottom
= rcList
.top
+ heightAlloced
;
235 ac
.lb
.SetPositionRelative(rcList
, wMain
);
237 if (lenEntered
!= 0) {
238 AutoCompleteMoveToCurrentWord();
242 void ScintillaBase::AutoCompleteCancel() {
246 void ScintillaBase::AutoCompleteMove(int delta
) {
250 void ScintillaBase::AutoCompleteMoveToCurrentWord() {
251 char wordCurrent
[1000];
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
);
260 void ScintillaBase::AutoCompleteChanged(char ch
) {
261 if (ac
.IsFillUpChar(ch
)) {
262 AutoCompleteCompleted(ch
);
263 } else if (currentPos
<= ac
.posStart
- ac
.startLen
) {
265 } else if (ac
.cancelAtStartPos
&& currentPos
<= ac
.posStart
) {
267 } else if (ac
.IsStopChar(ch
)) {
270 AutoCompleteMoveToCurrentWord();
274 void ScintillaBase::AutoCompleteCompleted(char fillUp
/*='\0'*/) {
275 int item
= ac
.lb
.GetSelection();
278 ac
.lb
.GetValue(item
, selected
, sizeof(selected
));
283 if (currentPos
!= ac
.posStart
) {
284 pdoc
->DeleteChars(ac
.posStart
, currentPos
- ac
.posStart
);
286 SetEmptySelection(ac
.posStart
- ac
.startLen
);
287 pdoc
->DeleteChars(ac
.posStart
- ac
.startLen
, ac
.startLen
);
289 SString piece
= selected
;
292 pdoc
->InsertString(currentPos
, piece
.c_str());
293 SetEmptySelection(currentPos
+ piece
.length());
296 if (currentPos
!= ac
.posStart
) {
297 pdoc
->DeleteChars(ac
.posStart
, currentPos
- ac
.posStart
);
299 SetEmptySelection(ac
.posStart
);
301 SString piece
= selected
+ ac
.startLen
;
304 pdoc
->InsertString(currentPos
, piece
.c_str());
305 SetEmptySelection(currentPos
+ piece
.length());
310 void ScintillaBase::ContextMenu(Point pt
) {
312 AddToPopUp("Undo", idcmdUndo
, pdoc
->CanUndo());
313 AddToPopUp("Redo", idcmdRedo
, pdoc
->CanRedo());
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
);
320 AddToPopUp("Select All", idcmdSelectAll
);
321 popup
.Show(pt
, wMain
);
324 void ScintillaBase::CancelModes() {
325 AutoCompleteCancel();
327 Editor::CancelModes();
330 void ScintillaBase::ButtonDown(Point pt
, unsigned int curTime
, bool shift
, bool ctrl
, bool alt
) {
332 Editor::ButtonDown(pt
, curTime
, shift
, ctrl
, alt
);
336 void ScintillaBase::Colourise(int start
, int end
) {
337 int lengthDoc
= Platform::SendScintilla(wMain
.GetID(), SCI_GETLENGTH
, 0, 0);
340 int len
= end
- start
;
342 //WindowAccessor styler(wMain.GetID(), props);
343 DocumentAccessor
styler(pdoc
, props
);
347 styleStart
= styler
.StyleAt(start
- 1);
348 styler
.SetCodePage(pdoc
->dbcsCodePage
);
350 LexerModule::Colourise(start
, len
, styleStart
, lexLanguage
, keyWordLists
, styler
);
355 void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded
) {
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
);
365 Editor::NotifyStyleToNeeded(endStyleNeeded
);
368 long ScintillaBase::WndProc(unsigned int iMessage
, unsigned long wParam
, long lParam
) {
371 AutoCompleteStart(wParam
, reinterpret_cast<const char *>(lParam
));
374 case SCI_AUTOCCANCEL
:
375 AutoCompleteCancel();
378 case SCI_AUTOCACTIVE
:
381 case SCI_AUTOCPOSSTART
:
384 case SCI_AUTOCCOMPLETE
:
385 AutoCompleteCompleted();
388 case SCI_AUTOCSETSEPARATOR
:
389 ac
.SetSeparator(static_cast<char>(wParam
));
392 case SCI_AUTOCGETSEPARATOR
:
393 return ac
.GetSeparator();
396 ac
.SetStopChars(reinterpret_cast<char *>(lParam
));
399 case SCI_AUTOCSELECT
:
400 ac
.Select(reinterpret_cast<char *>(lParam
));
403 case SCI_AUTOCSETCANCELATSTART
:
404 ac
.cancelAtStartPos
= wParam
;
407 case SCI_AUTOCGETCANCELATSTART
:
408 return ac
.cancelAtStartPos
;
410 case SCI_AUTOCSETFILLUPS
:
411 ac
.SetFillUpChars(reinterpret_cast<char *>(lParam
));
414 case SCI_AUTOCSETCHOOSESINGLE
:
415 ac
.chooseSingle
= wParam
;
418 case SCI_AUTOCGETCHOOSESINGLE
:
419 return ac
.chooseSingle
;
421 case SCI_AUTOCSETIGNORECASE
:
422 ac
.ignoreCase
= wParam
;
425 case SCI_AUTOCGETIGNORECASE
:
426 return ac
.ignoreCase
;
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();
442 // Now display the window.
443 CreateCallTipWindow(rc
);
444 ct
.wCallTip
.SetPositionRelative(rc
, wDraw
);
450 case SCI_CALLTIPCANCEL
:
454 case SCI_CALLTIPACTIVE
:
455 return ct
.inCallTipMode
;
457 case SCI_CALLTIPPOSSTART
:
458 return ct
.posStartCallTip
;
460 case SCI_CALLTIPSETHLT
:
461 ct
.SetHighlight(wParam
, lParam
);
464 case SCI_CALLTIPSETBACK
:
465 ct
.colourBG
= Colour(wParam
);
466 InvalidateStyleRedraw();
471 lexLanguage
= wParam
;
478 Colourise(wParam
, lParam
);
482 case SCI_SETPROPERTY
:
483 props
.Set(reinterpret_cast<const char *>(wParam
),
484 reinterpret_cast<const char *>(lParam
));
487 case SCI_SETKEYWORDS
:
488 if (wParam
< numWordLists
) {
489 keyWordLists
[wParam
]->Clear();
490 keyWordLists
[wParam
]->Set(reinterpret_cast<const char *>(lParam
));
496 return Editor::WndProc(iMessage
, wParam
, lParam
);