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-2002 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() { 
  38         displayPopupMenu 
= true; 
  41         lexLanguage 
= SCLEX_CONTAINER
; 
  43         for (int wl 
= 0;wl 
< numWordLists
;wl
++) 
  44                 keyWordLists
[wl
] = new WordList
; 
  45         keyWordLists
[numWordLists
] = 0; 
  49 ScintillaBase::~ScintillaBase() { 
  51         for (int wl 
= 0;wl 
< numWordLists
;wl
++) 
  52                 delete keyWordLists
[wl
]; 
  56 void ScintillaBase::Finalise() { 
  61 void ScintillaBase::RefreshColourPalette(Palette 
&pal
, bool want
) { 
  62         Editor::RefreshColourPalette(pal
, want
); 
  63         ct
.RefreshColourPalette(pal
, want
); 
  66 void ScintillaBase::AddCharUTF(char *s
, unsigned int len
, bool treatAsDBCS
) { 
  67         bool isFillUp 
= ac
.Active() && ac
.IsFillUpChar(*s
); 
  69                 Editor::AddCharUTF(s
, len
, treatAsDBCS
); 
  72                 AutoCompleteChanged(s
[0]); 
  73                 // For fill ups add the character after the autocompletion has  
  74                 // triggered so containers see the key so can display a calltip. 
  76                         Editor::AddCharUTF(s
, len
, treatAsDBCS
); 
  81 void ScintillaBase::Command(int cmdId
) { 
  85         case idAutoComplete
:    // Nothing to do 
  89         case idCallTip
:         // Nothing to do 
  94                 WndProc(SCI_UNDO
, 0, 0); 
  98                 WndProc(SCI_REDO
, 0, 0); 
 102                 WndProc(SCI_CUT
, 0, 0); 
 106                 WndProc(SCI_COPY
, 0, 0); 
 110                 WndProc(SCI_PASTE
, 0, 0); 
 114                 WndProc(SCI_CLEAR
, 0, 0); 
 118                 WndProc(SCI_SELECTALL
, 0, 0); 
 123 int ScintillaBase::KeyCommand(unsigned int iMessage
) { 
 124         // Most key commands cancel autocompletion mode 
 132                         AutoCompleteMove( -1); 
 138                         AutoCompleteMove( -5); 
 141                         AutoCompleteMove( -5000); 
 144                         AutoCompleteMove(5000); 
 148                         AutoCompleteChanged(); 
 149                         EnsureCaretVisible(); 
 151                 case SCI_DELETEBACKNOTLINE
: 
 153                         AutoCompleteChanged(); 
 154                         EnsureCaretVisible(); 
 157                         AutoCompleteCompleted(); 
 160                         AutoCompleteCompleted(); 
 168         if (ct
.inCallTipMode
) { 
 170                     (iMessage 
!= SCI_CHARLEFT
) && 
 171                     (iMessage 
!= SCI_CHARLEFTEXTEND
) && 
 172                     (iMessage 
!= SCI_CHARRIGHT
) && 
 173                     (iMessage 
!= SCI_CHARLEFTEXTEND
) && 
 174                     (iMessage 
!= SCI_EDITTOGGLEOVERTYPE
) && 
 175                     (iMessage 
!= SCI_DELETEBACK
) && 
 176                     (iMessage 
!= SCI_DELETEBACKNOTLINE
) 
 180                 if ((iMessage 
== SCI_DELETEBACK
) || (iMessage 
== SCI_DELETEBACKNOTLINE
)) { 
 181                         if (currentPos 
<= ct
.posStartCallTip
) { 
 186         return Editor::KeyCommand(iMessage
); 
 189 void ScintillaBase::AutoCompleteDoubleClick(void* p
) { 
 190         ScintillaBase
* sci 
= reinterpret_cast<ScintillaBase
*>(p
); 
 191         sci
->AutoCompleteCompleted(); 
 194 void ScintillaBase::AutoCompleteStart(int lenEntered
, const char *list
) { 
 195         //Platform::DebugPrintf("AutoComplete %s\n", list); 
 198         if (ac
.chooseSingle 
&& (listType 
== 0)) { 
 199                 if (list 
&& !strchr(list
, ac
.GetSeparator())) { 
 201                                 SetEmptySelection(currentPos 
- lenEntered
); 
 202                                 pdoc
->DeleteChars(currentPos
, lenEntered
); 
 203                                 SetEmptySelection(currentPos
); 
 204                                 pdoc
->InsertString(currentPos
, list
); 
 205                                 SetEmptySelection(currentPos 
+ strlen(list
)); 
 207                                 SetEmptySelection(currentPos
); 
 208                                 pdoc
->InsertString(currentPos
, list 
+ lenEntered
); 
 209                                 SetEmptySelection(currentPos 
+ strlen(list 
+ lenEntered
)); 
 214         ac
.Start(wMain
, idAutoComplete
, currentPos
, lenEntered
); 
 216         PRectangle rcClient 
= GetClientRectangle(); 
 217         Point pt 
= LocationFromPosition(currentPos 
- lenEntered
); 
 221         if (pt
.x 
>= rcClient
.right 
- widthLB
) { 
 222                 HorizontalScrollTo(xOffset 
+ pt
.x 
- rcClient
.right 
+ widthLB
); 
 224                 pt 
= LocationFromPosition(currentPos
); 
 227         rcac
.left 
= pt
.x 
- 5; 
 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                 rcac
.top 
= pt
.y 
- heightLB
; 
 232                         heightLB 
+= rcac
.top
; 
 236                 rcac
.top 
= pt
.y 
+ vs
.lineHeight
; 
 238         rcac
.right 
= rcac
.left 
+ widthLB
; 
 239         rcac
.bottom 
= Platform::Minimum(rcac
.top 
+ heightLB
, rcClient
.bottom
); 
 240         ac
.lb
.SetPositionRelative(rcac
, wMain
); 
 241         ac
.lb
.SetFont(vs
.styles
[STYLE_DEFAULT
].font
); 
 242         ac
.lb
.SetAverageCharWidth(vs
.styles
[STYLE_DEFAULT
].aveCharWidth
); 
 243         ac
.lb
.SetDoubleClickAction(AutoCompleteDoubleClick
, this); 
 247         // Fiddle the position of the list so it is right next to the target and wide enough for all its strings 
 248         PRectangle rcList 
= ac
.lb
.GetDesiredRect(); 
 249         int heightAlloced 
= rcList
.bottom 
- rcList
.top
; 
 250         widthLB 
= Platform::Maximum(widthLB
, rcList
.right 
- rcList
.left
); 
 251         // Make an allowance for large strings in list 
 252         rcList
.left 
= pt
.x 
- 5; 
 253         rcList
.right 
= rcList
.left 
+ widthLB
; 
 254         if (pt
.y 
>= rcClient
.bottom 
- heightLB 
&&  // Wont fit below. 
 255                 pt
.y 
>= (rcClient
.bottom 
+ rcClient
.top
) / 2) { // and there is more room above. 
 256                 rcList
.top 
= pt
.y 
- heightAlloced
; 
 258                 rcList
.top 
= pt
.y 
+ vs
.lineHeight
; 
 260         rcList
.bottom 
= rcList
.top 
+ heightAlloced
; 
 261         ac
.lb
.SetPositionRelative(rcList
, wMain
); 
 263         if (lenEntered 
!= 0) { 
 264                 AutoCompleteMoveToCurrentWord(); 
 268 void ScintillaBase::AutoCompleteCancel() { 
 272 void ScintillaBase::AutoCompleteMove(int delta
) { 
 276 void ScintillaBase::AutoCompleteMoveToCurrentWord() { 
 277         char wordCurrent
[1000]; 
 279         int startWord 
= ac
.posStart 
- ac
.startLen
; 
 280         for (i 
= startWord
; i 
< currentPos
; i
++) 
 281                 wordCurrent
[i 
- startWord
] = pdoc
->CharAt(i
); 
 282         wordCurrent
[i 
- startWord
] = '\0'; 
 283         ac
.Select(wordCurrent
); 
 286 void ScintillaBase::AutoCompleteChanged(char ch
) { 
 287         if (ac
.IsFillUpChar(ch
)) { 
 288                 AutoCompleteCompleted(); 
 289         } else if (currentPos 
<= ac
.posStart 
- ac
.startLen
) { 
 291         } else if (ac
.cancelAtStartPos 
&& currentPos 
<= ac
.posStart
) { 
 293         } else if (ac
.IsStopChar(ch
)) { 
 296                 AutoCompleteMoveToCurrentWord(); 
 300 void ScintillaBase::AutoCompleteCompleted() { 
 301         int item 
= ac
.lb
.GetSelection(); 
 304                 ac
.lb
.GetValue(item
, selected
, sizeof(selected
)); 
 309                 userListSelected 
= selected
; 
 311                 scn
.nmhdr
.code 
= SCN_USERLISTSELECTION
; 
 313                 scn
.wParam 
= listType
; 
 315                 scn
.text 
= userListSelected
.c_str(); 
 320         Position firstPos 
= ac
.posStart 
- ac
.startLen
; 
 321         Position endPos 
= currentPos
; 
 322         if (ac
.dropRestOfWord
) 
 323                 endPos 
= pdoc
->ExtendWordSelect(endPos
, 1, true); 
 324         if (endPos 
< firstPos
) 
 326         pdoc
->BeginUndoAction(); 
 327         if (endPos 
!= firstPos
) { 
 328                 pdoc
->DeleteChars(firstPos
, endPos 
- firstPos
); 
 330         SetEmptySelection(ac
.posStart
); 
 332                 SString piece 
= selected
; 
 333                 pdoc
->InsertString(firstPos
, piece
.c_str()); 
 334                 SetEmptySelection(firstPos 
+ piece
.length()); 
 336         pdoc
->EndUndoAction(); 
 339 void ScintillaBase::ContextMenu(Point pt
) { 
 340         if (displayPopupMenu
) { 
 341                 bool writable 
= !WndProc(SCI_GETREADONLY
, 0, 0); 
 343                 AddToPopUp("Undo", idcmdUndo
, writable 
&& pdoc
->CanUndo()); 
 344                 AddToPopUp("Redo", idcmdRedo
, writable 
&& pdoc
->CanRedo()); 
 346                 AddToPopUp("Cut", idcmdCut
, writable 
&& currentPos 
!= anchor
); 
 347                 AddToPopUp("Copy", idcmdCopy
, currentPos 
!= anchor
); 
 348                 AddToPopUp("Paste", idcmdPaste
, writable 
&& WndProc(SCI_CANPASTE
, 0, 0)); 
 349                 AddToPopUp("Delete", idcmdDelete
, writable 
&& currentPos 
!= anchor
); 
 351                 AddToPopUp("Select All", idcmdSelectAll
); 
 352                 popup
.Show(pt
, wMain
); 
 356 void ScintillaBase::CancelModes() { 
 357         AutoCompleteCancel(); 
 359         Editor::CancelModes(); 
 362 void ScintillaBase::ButtonDown(Point pt
, unsigned int curTime
, bool shift
, bool ctrl
, bool alt
) { 
 364         Editor::ButtonDown(pt
, curTime
, shift
, ctrl
, alt
); 
 368 void ScintillaBase::SetLexer(uptr_t wParam
) { 
 369         lexLanguage 
= wParam
; 
 370         lexCurrent 
= LexerModule::Find(lexLanguage
); 
 372                 lexCurrent 
= LexerModule::Find(SCLEX_NULL
); 
 375 void ScintillaBase::SetLexerLanguage(const char *languageName
) { 
 376         lexLanguage 
= SCLEX_CONTAINER
; 
 377         lexCurrent 
= LexerModule::Find(languageName
); 
 379                 lexCurrent 
= LexerModule::Find(SCLEX_NULL
); 
 381                 lexLanguage 
= lexCurrent
->GetLanguage(); 
 384 void ScintillaBase::Colourise(int start
, int end
) { 
 385         int lengthDoc 
= pdoc
->Length(); 
 388         int len 
= end 
- start
; 
 390         PLATFORM_ASSERT(len 
>= 0); 
 391         PLATFORM_ASSERT(start 
+ len 
<= lengthDoc
); 
 393         //WindowAccessor styler(wMain.GetID(), props); 
 394         DocumentAccessor 
styler(pdoc
, props
, wMain
.GetID()); 
 398                 styleStart 
= styler
.StyleAt(start 
- 1); 
 399         styler
.SetCodePage(pdoc
->dbcsCodePage
); 
 401         if (lexCurrent 
&& (len 
> 0)) {  // Should always succeed as null lexer should always be available 
 402                 lexCurrent
->Lex(start
, len
, styleStart
, keyWordLists
, styler
); 
 404                 if (styler
.GetPropertyInt("fold")) { 
 405                         lexCurrent
->Fold(start
, len
, styleStart
, keyWordLists
, styler
); 
 412 void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded
) { 
 414         if (lexLanguage 
!= SCLEX_CONTAINER
) { 
 415                 int endStyled 
= WndProc(SCI_GETENDSTYLED
, 0, 0); 
 416                 int lineEndStyled 
= WndProc(SCI_LINEFROMPOSITION
, endStyled
, 0); 
 417                 endStyled 
= WndProc(SCI_POSITIONFROMLINE
, lineEndStyled
, 0); 
 418                 Colourise(endStyled
, endStyleNeeded
); 
 422         Editor::NotifyStyleToNeeded(endStyleNeeded
); 
 425 sptr_t 
ScintillaBase::WndProc(unsigned int iMessage
, uptr_t wParam
, sptr_t lParam
) { 
 429                 AutoCompleteStart(wParam
, reinterpret_cast<const char *>(lParam
)); 
 432         case SCI_AUTOCCANCEL
: 
 433                 AutoCompleteCancel(); 
 436         case SCI_AUTOCACTIVE
: 
 439         case SCI_AUTOCPOSSTART
: 
 442         case SCI_AUTOCCOMPLETE
: 
 443                 AutoCompleteCompleted(); 
 446         case SCI_AUTOCSETSEPARATOR
: 
 447                 ac
.SetSeparator(static_cast<char>(wParam
)); 
 450         case SCI_AUTOCGETSEPARATOR
: 
 451                 return ac
.GetSeparator(); 
 454                 ac
.SetStopChars(reinterpret_cast<char *>(lParam
)); 
 457         case SCI_AUTOCSELECT
: 
 458                 ac
.Select(reinterpret_cast<char *>(lParam
)); 
 461         case SCI_AUTOCSETCANCELATSTART
: 
 462                 ac
.cancelAtStartPos 
= wParam 
!= 0; 
 465         case SCI_AUTOCGETCANCELATSTART
: 
 466                 return ac
.cancelAtStartPos
; 
 468         case SCI_AUTOCSETFILLUPS
: 
 469                 ac
.SetFillUpChars(reinterpret_cast<char *>(lParam
)); 
 472         case SCI_AUTOCSETCHOOSESINGLE
: 
 473                 ac
.chooseSingle 
= wParam 
!= 0; 
 476         case SCI_AUTOCGETCHOOSESINGLE
: 
 477                 return ac
.chooseSingle
; 
 479         case SCI_AUTOCSETIGNORECASE
: 
 480                 ac
.ignoreCase 
= wParam 
!= 0; 
 483         case SCI_AUTOCGETIGNORECASE
: 
 484                 return ac
.ignoreCase
; 
 486         case SCI_USERLISTSHOW
: 
 488                 AutoCompleteStart(0, reinterpret_cast<const char *>(lParam
)); 
 491         case SCI_AUTOCSETAUTOHIDE
: 
 492                 ac
.autoHide 
= wParam 
!= 0; 
 495         case SCI_AUTOCGETAUTOHIDE
: 
 498         case SCI_AUTOCSETDROPRESTOFWORD
: 
 499                 ac
.dropRestOfWord 
= wParam 
!= 0; 
 502         case SCI_AUTOCGETDROPRESTOFWORD
: 
 503                 return ac
.dropRestOfWord
; 
 505         case SCI_CALLTIPSHOW
: { 
 506                         AutoCompleteCancel(); 
 507                         if (!ct
.wCallTip
.Created()) { 
 508                                 Point pt 
= LocationFromPosition(wParam
); 
 509                                 pt
.y 
+= vs
.lineHeight
; 
 510                                 PRectangle rc 
= ct
.CallTipStart(currentPos
, pt
, 
 511                                                                 reinterpret_cast<char *>(lParam
), 
 512                                                                 vs
.styles
[STYLE_DEFAULT
].fontName
, 
 513                                                                 vs
.styles
[STYLE_DEFAULT
].sizeZoomed
, 
 515                                 // If the call-tip window would be out of the client 
 516                                 // space, adjust so it displays above the text. 
 517                                 PRectangle rcClient 
= GetClientRectangle(); 
 518                                 if (rc
.bottom 
> rcClient
.bottom
) { 
 519                                         int offset 
= vs
.lineHeight 
+ rc
.Height(); 
 523                                 // Now display the window. 
 524                                 CreateCallTipWindow(rc
); 
 525                                 ct
.wCallTip
.SetPositionRelative(rc
, wMain
); 
 531         case SCI_CALLTIPCANCEL
: 
 535         case SCI_CALLTIPACTIVE
: 
 536                 return ct
.inCallTipMode
; 
 538         case SCI_CALLTIPPOSSTART
: 
 539                 return ct
.posStartCallTip
; 
 541         case SCI_CALLTIPSETHLT
: 
 542                 ct
.SetHighlight(wParam
, lParam
); 
 545         case SCI_CALLTIPSETBACK
: 
 546                 ct
.colourBG 
= ColourDesired(wParam
); 
 547                 InvalidateStyleRedraw(); 
 551                 displayPopupMenu 
= wParam 
!= 0; 
 557                 lexLanguage 
= wParam
; 
 564                 Colourise(wParam
, lParam
); 
 568         case SCI_SETPROPERTY
: 
 569                 props
.Set(reinterpret_cast<const char *>(wParam
), 
 570                           reinterpret_cast<const char *>(lParam
)); 
 573         case SCI_SETKEYWORDS
: 
 574                 if (wParam 
< numWordLists
) { 
 575                         keyWordLists
[wParam
]->Clear(); 
 576                         keyWordLists
[wParam
]->Set(reinterpret_cast<const char *>(lParam
)); 
 580         case SCI_SETLEXERLANGUAGE
: 
 581                 SetLexerLanguage(reinterpret_cast<const char *>(lParam
)); 
 587                 return Editor::WndProc(iMessage
, wParam
, lParam
);