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.
15 #include "Scintilla.h"
20 #include "DocumentAccessor.h"
23 #include "ContractionState.h"
25 #include "CellBuffer.h"
28 #include "Indicator.h"
30 #include "LineMarker.h"
32 #include "ViewStyle.h"
33 #include "AutoComplete.h"
36 #include "ScintillaBase.h"
38 ScintillaBase::ScintillaBase() {
39 displayPopupMenu
= true;
42 lexLanguage
= SCLEX_CONTAINER
;
44 for (int wl
= 0;wl
< numWordLists
;wl
++)
45 keyWordLists
[wl
] = new WordList
;
46 keyWordLists
[numWordLists
] = 0;
50 ScintillaBase::~ScintillaBase() {
52 for (int wl
= 0;wl
< numWordLists
;wl
++)
53 delete keyWordLists
[wl
];
57 void ScintillaBase::Finalise() {
62 void ScintillaBase::RefreshColourPalette(Palette
&pal
, bool want
) {
63 Editor::RefreshColourPalette(pal
, want
);
64 ct
.RefreshColourPalette(pal
, want
);
67 void ScintillaBase::AddCharUTF(char *s
, unsigned int len
, bool treatAsDBCS
) {
68 bool isFillUp
= ac
.Active() && ac
.IsFillUpChar(*s
);
70 Editor::AddCharUTF(s
, len
, treatAsDBCS
);
73 AutoCompleteCharacterAdded(s
[0]);
74 // For fill ups add the character after the autocompletion has
75 // triggered so containers see the key so can display a calltip.
77 Editor::AddCharUTF(s
, len
, treatAsDBCS
);
82 void ScintillaBase::Command(int cmdId
) {
86 case idAutoComplete
: // Nothing to do
90 case idCallTip
: // Nothing to do
95 WndProc(SCI_UNDO
, 0, 0);
99 WndProc(SCI_REDO
, 0, 0);
103 WndProc(SCI_CUT
, 0, 0);
107 WndProc(SCI_COPY
, 0, 0);
111 WndProc(SCI_PASTE
, 0, 0);
115 WndProc(SCI_CLEAR
, 0, 0);
119 WndProc(SCI_SELECTALL
, 0, 0);
124 int ScintillaBase::KeyCommand(unsigned int iMessage
) {
125 // Most key commands cancel autocompletion mode
133 AutoCompleteMove( -1);
139 AutoCompleteMove( -5);
142 AutoCompleteMove( -5000);
145 AutoCompleteMove(5000);
149 AutoCompleteCharacterDeleted();
150 EnsureCaretVisible();
152 case SCI_DELETEBACKNOTLINE
:
154 AutoCompleteCharacterDeleted();
155 EnsureCaretVisible();
158 AutoCompleteCompleted();
161 AutoCompleteCompleted();
169 if (ct
.inCallTipMode
) {
171 (iMessage
!= SCI_CHARLEFT
) &&
172 (iMessage
!= SCI_CHARLEFTEXTEND
) &&
173 (iMessage
!= SCI_CHARRIGHT
) &&
174 (iMessage
!= SCI_CHARLEFTEXTEND
) &&
175 (iMessage
!= SCI_EDITTOGGLEOVERTYPE
) &&
176 (iMessage
!= SCI_DELETEBACK
) &&
177 (iMessage
!= SCI_DELETEBACKNOTLINE
)
181 if ((iMessage
== SCI_DELETEBACK
) || (iMessage
== SCI_DELETEBACKNOTLINE
)) {
182 if (currentPos
<= ct
.posStartCallTip
) {
187 return Editor::KeyCommand(iMessage
);
190 void ScintillaBase::AutoCompleteDoubleClick(void* p
) {
191 ScintillaBase
* sci
= reinterpret_cast<ScintillaBase
*>(p
);
192 sci
->AutoCompleteCompleted();
195 void ScintillaBase::AutoCompleteStart(int lenEntered
, const char *list
) {
196 //Platform::DebugPrintf("AutoComplete %s\n", list);
199 if (ac
.chooseSingle
&& (listType
== 0)) {
200 if (list
&& !strchr(list
, ac
.GetSeparator())) {
201 const char *typeSep
= strchr(list
, ac
.GetTypesep());
202 size_t lenInsert
= (typeSep
) ? (typeSep
-list
) : strlen(list
);
204 SetEmptySelection(currentPos
- lenEntered
);
205 pdoc
->DeleteChars(currentPos
, lenEntered
);
206 SetEmptySelection(currentPos
);
207 pdoc
->InsertString(currentPos
, list
, lenInsert
);
208 SetEmptySelection(currentPos
+ lenInsert
);
210 SetEmptySelection(currentPos
);
211 pdoc
->InsertString(currentPos
, list
+ lenEntered
, lenInsert
- lenEntered
);
212 SetEmptySelection(currentPos
+ lenInsert
- lenEntered
);
217 ac
.Start(wMain
, idAutoComplete
, currentPos
, lenEntered
, vs
.lineHeight
, IsUnicodeMode());
219 PRectangle rcClient
= GetClientRectangle();
220 Point pt
= LocationFromPosition(currentPos
- lenEntered
);
224 if (pt
.x
>= rcClient
.right
- widthLB
) {
225 HorizontalScrollTo(xOffset
+ pt
.x
- rcClient
.right
+ widthLB
);
227 pt
= LocationFromPosition(currentPos
);
230 rcac
.left
= pt
.x
- ac
.lb
->CaretFromEdge();
231 if (pt
.y
>= rcClient
.bottom
- heightLB
&& // Wont fit below.
232 pt
.y
>= (rcClient
.bottom
+ rcClient
.top
) / 2) { // and there is more room above.
233 rcac
.top
= pt
.y
- heightLB
;
235 heightLB
+= rcac
.top
;
239 rcac
.top
= pt
.y
+ vs
.lineHeight
;
241 rcac
.right
= rcac
.left
+ widthLB
;
242 rcac
.bottom
= Platform::Minimum(rcac
.top
+ heightLB
, rcClient
.bottom
);
243 ac
.lb
->SetPositionRelative(rcac
, wMain
);
244 ac
.lb
->SetFont(vs
.styles
[STYLE_DEFAULT
].font
);
245 ac
.lb
->SetAverageCharWidth(vs
.styles
[STYLE_DEFAULT
].aveCharWidth
);
246 ac
.lb
->SetDoubleClickAction(AutoCompleteDoubleClick
, this);
250 // Fiddle the position of the list so it is right next to the target and wide enough for all its strings
251 PRectangle rcList
= ac
.lb
->GetDesiredRect();
252 int heightAlloced
= rcList
.bottom
- rcList
.top
;
253 widthLB
= Platform::Maximum(widthLB
, rcList
.right
- rcList
.left
);
254 // Make an allowance for large strings in list
255 rcList
.left
= pt
.x
- ac
.lb
->CaretFromEdge();
256 rcList
.right
= rcList
.left
+ widthLB
;
257 if (((pt
.y
+ vs
.lineHeight
) >= (rcClient
.bottom
- heightAlloced
)) && // Wont fit below.
258 ((pt
.y
+ vs
.lineHeight
/ 2) >= (rcClient
.bottom
+ rcClient
.top
) / 2)) { // and there is more room above.
259 rcList
.top
= pt
.y
- heightAlloced
;
261 rcList
.top
= pt
.y
+ vs
.lineHeight
;
263 rcList
.bottom
= rcList
.top
+ heightAlloced
;
264 ac
.lb
->SetPositionRelative(rcList
, wMain
);
266 if (lenEntered
!= 0) {
267 AutoCompleteMoveToCurrentWord();
271 void ScintillaBase::AutoCompleteCancel() {
275 void ScintillaBase::AutoCompleteMove(int delta
) {
279 void ScintillaBase::AutoCompleteMoveToCurrentWord() {
280 char wordCurrent
[1000];
282 int startWord
= ac
.posStart
- ac
.startLen
;
283 for (i
= startWord
; i
< currentPos
&& i
- startWord
< 1000; i
++)
284 wordCurrent
[i
- startWord
] = pdoc
->CharAt(i
);
285 wordCurrent
[Platform::Minimum(i
- startWord
, 999)] = '\0';
286 ac
.Select(wordCurrent
);
289 void ScintillaBase::AutoCompleteCharacterAdded(char ch
) {
290 if (ac
.IsFillUpChar(ch
)) {
291 AutoCompleteCompleted();
292 } else if (ac
.IsStopChar(ch
)) {
295 AutoCompleteMoveToCurrentWord();
299 void ScintillaBase::AutoCompleteCharacterDeleted() {
300 if (currentPos
< ac
.posStart
- ac
.startLen
) {
302 } else if (ac
.cancelAtStartPos
&& (currentPos
<= ac
.posStart
)) {
305 AutoCompleteMoveToCurrentWord();
309 void ScintillaBase::AutoCompleteCompleted() {
310 int item
= ac
.lb
->GetSelection();
314 ac
.lb
->GetValue(item
, selected
, sizeof(selected
));
321 userListSelected
= selected
;
323 scn
.nmhdr
.code
= SCN_USERLISTSELECTION
;
325 scn
.wParam
= listType
;
326 scn
.listType
= listType
;
328 scn
.text
= userListSelected
.c_str();
333 Position firstPos
= ac
.posStart
- ac
.startLen
;
334 Position endPos
= currentPos
;
335 if (ac
.dropRestOfWord
)
336 endPos
= pdoc
->ExtendWordSelect(endPos
, 1, true);
337 if (endPos
< firstPos
)
339 pdoc
->BeginUndoAction();
340 if (endPos
!= firstPos
) {
341 pdoc
->DeleteChars(firstPos
, endPos
- firstPos
);
343 SetEmptySelection(ac
.posStart
);
345 SString piece
= selected
;
346 pdoc
->InsertString(firstPos
, piece
.c_str());
347 SetEmptySelection(firstPos
+ static_cast<int>(piece
.length()));
349 pdoc
->EndUndoAction();
352 int ScintillaBase::AutoCompleteGetCurrent() {
353 return ac
.lb
->GetSelection();
356 void ScintillaBase::CallTipShow(Point pt
, const char *defn
) {
357 AutoCompleteCancel();
358 pt
.y
+= vs
.lineHeight
;
359 PRectangle rc
= ct
.CallTipStart(currentPos
, pt
,
361 vs
.styles
[STYLE_DEFAULT
].fontName
,
362 vs
.styles
[STYLE_DEFAULT
].sizeZoomed
,
364 vs
.styles
[STYLE_DEFAULT
].characterSet
,
366 // If the call-tip window would be out of the client
367 // space, adjust so it displays above the text.
368 PRectangle rcClient
= GetClientRectangle();
369 if (rc
.bottom
> rcClient
.bottom
) {
370 int offset
= vs
.lineHeight
+ rc
.Height();
374 // Now display the window.
375 CreateCallTipWindow(rc
);
376 ct
.wCallTip
.SetPositionRelative(rc
, wMain
);
380 void ScintillaBase::CallTipClick() {
382 scn
.nmhdr
.code
= SCN_CALLTIPCLICK
;
383 scn
.position
= ct
.clickPlace
;
387 void ScintillaBase::ContextMenu(Point pt
) {
388 if (displayPopupMenu
) {
389 bool writable
= !WndProc(SCI_GETREADONLY
, 0, 0);
391 AddToPopUp("Undo", idcmdUndo
, writable
&& pdoc
->CanUndo());
392 AddToPopUp("Redo", idcmdRedo
, writable
&& pdoc
->CanRedo());
394 AddToPopUp("Cut", idcmdCut
, writable
&& currentPos
!= anchor
);
395 AddToPopUp("Copy", idcmdCopy
, currentPos
!= anchor
);
396 AddToPopUp("Paste", idcmdPaste
, writable
&& WndProc(SCI_CANPASTE
, 0, 0));
397 AddToPopUp("Delete", idcmdDelete
, writable
&& currentPos
!= anchor
);
399 AddToPopUp("Select All", idcmdSelectAll
);
400 popup
.Show(pt
, wMain
);
404 void ScintillaBase::CancelModes() {
405 AutoCompleteCancel();
407 Editor::CancelModes();
410 void ScintillaBase::ButtonDown(Point pt
, unsigned int curTime
, bool shift
, bool ctrl
, bool alt
) {
412 Editor::ButtonDown(pt
, curTime
, shift
, ctrl
, alt
);
416 void ScintillaBase::SetLexer(uptr_t wParam
) {
417 lexLanguage
= wParam
;
418 lexCurrent
= LexerModule::Find(lexLanguage
);
420 lexCurrent
= LexerModule::Find(SCLEX_NULL
);
423 void ScintillaBase::SetLexerLanguage(const char *languageName
) {
424 lexLanguage
= SCLEX_CONTAINER
;
425 lexCurrent
= LexerModule::Find(languageName
);
427 lexCurrent
= LexerModule::Find(SCLEX_NULL
);
429 lexLanguage
= lexCurrent
->GetLanguage();
432 void ScintillaBase::Colourise(int start
, int end
) {
433 int lengthDoc
= pdoc
->Length();
436 int len
= end
- start
;
438 PLATFORM_ASSERT(len
>= 0);
439 PLATFORM_ASSERT(start
+ len
<= lengthDoc
);
441 //WindowAccessor styler(wMain.GetID(), props);
442 DocumentAccessor
styler(pdoc
, props
, wMain
.GetID());
446 styleStart
= styler
.StyleAt(start
- 1);
447 styler
.SetCodePage(pdoc
->dbcsCodePage
);
449 if (lexCurrent
&& (len
> 0)) { // Should always succeed as null lexer should always be available
450 lexCurrent
->Lex(start
, len
, styleStart
, keyWordLists
, styler
);
452 if (styler
.GetPropertyInt("fold")) {
453 lexCurrent
->Fold(start
, len
, styleStart
, keyWordLists
, styler
);
460 void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded
) {
462 if (lexLanguage
!= SCLEX_CONTAINER
) {
463 int endStyled
= WndProc(SCI_GETENDSTYLED
, 0, 0);
464 int lineEndStyled
= WndProc(SCI_LINEFROMPOSITION
, endStyled
, 0);
465 endStyled
= WndProc(SCI_POSITIONFROMLINE
, lineEndStyled
, 0);
466 Colourise(endStyled
, endStyleNeeded
);
470 Editor::NotifyStyleToNeeded(endStyleNeeded
);
473 sptr_t
ScintillaBase::WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) {
477 AutoCompleteStart(wParam
, reinterpret_cast<const char *>(lParam
));
480 case SCI_AUTOCCANCEL
:
481 AutoCompleteCancel();
484 case SCI_AUTOCACTIVE
:
487 case SCI_AUTOCPOSSTART
:
490 case SCI_AUTOCCOMPLETE
:
491 AutoCompleteCompleted();
494 case SCI_AUTOCSETSEPARATOR
:
495 ac
.SetSeparator(static_cast<char>(wParam
));
498 case SCI_AUTOCGETSEPARATOR
:
499 return ac
.GetSeparator();
502 ac
.SetStopChars(reinterpret_cast<char *>(lParam
));
505 case SCI_AUTOCSELECT
:
506 ac
.Select(reinterpret_cast<char *>(lParam
));
509 case SCI_AUTOCGETCURRENT
:
510 return AutoCompleteGetCurrent();
512 case SCI_AUTOCSETCANCELATSTART
:
513 ac
.cancelAtStartPos
= wParam
!= 0;
516 case SCI_AUTOCGETCANCELATSTART
:
517 return ac
.cancelAtStartPos
;
519 case SCI_AUTOCSETFILLUPS
:
520 ac
.SetFillUpChars(reinterpret_cast<char *>(lParam
));
523 case SCI_AUTOCSETCHOOSESINGLE
:
524 ac
.chooseSingle
= wParam
!= 0;
527 case SCI_AUTOCGETCHOOSESINGLE
:
528 return ac
.chooseSingle
;
530 case SCI_AUTOCSETIGNORECASE
:
531 ac
.ignoreCase
= wParam
!= 0;
534 case SCI_AUTOCGETIGNORECASE
:
535 return ac
.ignoreCase
;
537 case SCI_USERLISTSHOW
:
539 AutoCompleteStart(0, reinterpret_cast<const char *>(lParam
));
542 case SCI_AUTOCSETAUTOHIDE
:
543 ac
.autoHide
= wParam
!= 0;
546 case SCI_AUTOCGETAUTOHIDE
:
549 case SCI_AUTOCSETDROPRESTOFWORD
:
550 ac
.dropRestOfWord
= wParam
!= 0;
553 case SCI_AUTOCGETDROPRESTOFWORD
:
554 return ac
.dropRestOfWord
;
556 case SCI_REGISTERIMAGE
:
557 ac
.lb
->RegisterImage(wParam
, reinterpret_cast<const char *>(lParam
));
560 case SCI_CLEARREGISTEREDIMAGES
:
561 ac
.lb
->ClearRegisteredImages();
564 case SCI_AUTOCSETTYPESEPARATOR
:
565 ac
.SetTypesep(static_cast<char>(wParam
));
568 case SCI_AUTOCGETTYPESEPARATOR
:
569 return ac
.GetTypesep();
571 case SCI_CALLTIPSHOW
:
572 CallTipShow(LocationFromPosition(wParam
),
573 reinterpret_cast<const char *>(lParam
));
576 case SCI_CALLTIPCANCEL
:
580 case SCI_CALLTIPACTIVE
:
581 return ct
.inCallTipMode
;
583 case SCI_CALLTIPPOSSTART
:
584 return ct
.posStartCallTip
;
586 case SCI_CALLTIPSETHLT
:
587 ct
.SetHighlight(wParam
, lParam
);
590 case SCI_CALLTIPSETBACK
:
591 ct
.colourBG
= ColourDesired(wParam
);
592 InvalidateStyleRedraw();
595 case SCI_CALLTIPSETFORE
:
596 ct
.colourUnSel
= ColourDesired(wParam
);
597 InvalidateStyleRedraw();
600 case SCI_CALLTIPSETFOREHLT
:
601 ct
.colourSel
= ColourDesired(wParam
);
602 InvalidateStyleRedraw();
606 displayPopupMenu
= wParam
!= 0;
612 lexLanguage
= wParam
;
619 Colourise(wParam
, lParam
);
623 case SCI_SETPROPERTY
:
624 props
.Set(reinterpret_cast<const char *>(wParam
),
625 reinterpret_cast<const char *>(lParam
));
628 case SCI_SETKEYWORDS
:
629 if (wParam
< numWordLists
) {
630 keyWordLists
[wParam
]->Clear();
631 keyWordLists
[wParam
]->Set(reinterpret_cast<const char *>(lParam
));
635 case SCI_SETLEXERLANGUAGE
:
636 SetLexerLanguage(reinterpret_cast<const char *>(lParam
));
642 return Editor::WndProc(iMessage
, wParam
, lParam
);