📄 scintillabase.cxx
字号:
// Scintilla source code edit control
/** @file ScintillaBase.cxx
** An enhanced subclass of Editor with calltips, autocomplete and context menu.
**/
// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "Platform.h"
#include "Scintilla.h"
#include "PropSet.h"
#ifdef SCI_LEXER
#include "SciLexer.h"
#include "Accessor.h"
#include "DocumentAccessor.h"
#include "KeyWords.h"
#endif
#include "ContractionState.h"
#include "SVector.h"
#include "CellBuffer.h"
#include "CallTip.h"
#include "KeyMap.h"
#include "Indicator.h"
#include "XPM.h"
#include "LineMarker.h"
#include "Style.h"
#include "ViewStyle.h"
#include "AutoComplete.h"
#include "Document.h"
#include "Editor.h"
#include "ScintillaBase.h"
ScintillaBase::ScintillaBase() {
displayPopupMenu = true;
listType = 0;
maxListWidth = 0;
#ifdef SCI_LEXER
lexLanguage = SCLEX_CONTAINER;
lexCurrent = 0;
for (int wl = 0;wl < numWordLists;wl++)
keyWordLists[wl] = new WordList;
keyWordLists[numWordLists] = 0;
#endif
}
ScintillaBase::~ScintillaBase() {
#ifdef SCI_LEXER
for (int wl = 0;wl < numWordLists;wl++)
delete keyWordLists[wl];
#endif
}
void ScintillaBase::Finalise() {
Editor::Finalise();
popup.Destroy();
}
void ScintillaBase::RefreshColourPalette(Palette &pal, bool want) {
Editor::RefreshColourPalette(pal, want);
ct.RefreshColourPalette(pal, want);
}
void ScintillaBase::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
bool isFillUp = ac.Active() && ac.IsFillUpChar(*s);
if (!isFillUp) {
Editor::AddCharUTF(s, len, treatAsDBCS);
}
if (ac.Active()) {
AutoCompleteCharacterAdded(s[0]);
// For fill ups add the character after the autocompletion has
// triggered so containers see the key so can display a calltip.
if (isFillUp) {
Editor::AddCharUTF(s, len, treatAsDBCS);
}
}
}
void ScintillaBase::Command(int cmdId) {
switch (cmdId) {
case idAutoComplete: // Nothing to do
break;
case idCallTip: // Nothing to do
break;
case idcmdUndo:
WndProc(SCI_UNDO, 0, 0);
break;
case idcmdRedo:
WndProc(SCI_REDO, 0, 0);
break;
case idcmdCut:
WndProc(SCI_CUT, 0, 0);
break;
case idcmdCopy:
WndProc(SCI_COPY, 0, 0);
break;
case idcmdPaste:
WndProc(SCI_PASTE, 0, 0);
break;
case idcmdDelete:
WndProc(SCI_CLEAR, 0, 0);
break;
case idcmdSelectAll:
WndProc(SCI_SELECTALL, 0, 0);
break;
}
}
int ScintillaBase::KeyCommand(unsigned int iMessage) {
// Most key commands cancel autocompletion mode
if (ac.Active()) {
switch (iMessage) {
// Except for these
case SCI_LINEDOWN:
AutoCompleteMove(1);
return 0;
case SCI_LINEUP:
AutoCompleteMove( -1);
return 0;
case SCI_PAGEDOWN:
AutoCompleteMove(5);
return 0;
case SCI_PAGEUP:
AutoCompleteMove( -5);
return 0;
case SCI_VCHOME:
AutoCompleteMove( -5000);
return 0;
case SCI_LINEEND:
AutoCompleteMove(5000);
return 0;
case SCI_DELETEBACK:
DelCharBack(true);
AutoCompleteCharacterDeleted();
EnsureCaretVisible();
return 0;
case SCI_DELETEBACKNOTLINE:
DelCharBack(false);
AutoCompleteCharacterDeleted();
EnsureCaretVisible();
return 0;
case SCI_TAB:
AutoCompleteCompleted();
return 0;
case SCI_NEWLINE:
AutoCompleteCompleted();
return 0;
default:
ac.Cancel();
}
}
if (ct.inCallTipMode) {
if (
(iMessage != SCI_CHARLEFT) &&
(iMessage != SCI_CHARLEFTEXTEND) &&
(iMessage != SCI_CHARRIGHT) &&
(iMessage != SCI_CHARLEFTEXTEND) &&
(iMessage != SCI_EDITTOGGLEOVERTYPE) &&
(iMessage != SCI_DELETEBACK) &&
(iMessage != SCI_DELETEBACKNOTLINE)
) {
ct.CallTipCancel();
}
if ((iMessage == SCI_DELETEBACK) || (iMessage == SCI_DELETEBACKNOTLINE)) {
if (currentPos <= ct.posStartCallTip) {
ct.CallTipCancel();
}
}
}
return Editor::KeyCommand(iMessage);
}
void ScintillaBase::AutoCompleteDoubleClick(void* p) {
ScintillaBase* sci = reinterpret_cast<ScintillaBase*>(p);
sci->AutoCompleteCompleted();
}
void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) {
//Platform::DebugPrintf("AutoComplete %s\n", list);
ct.CallTipCancel();
if (ac.chooseSingle && (listType == 0)) {
if (list && !strchr(list, ac.GetSeparator())) {
const char *typeSep = strchr(list, ac.GetTypesep());
size_t lenInsert = (typeSep) ? (typeSep-list) : strlen(list);
if (ac.ignoreCase) {
SetEmptySelection(currentPos - lenEntered);
pdoc->DeleteChars(currentPos, lenEntered);
SetEmptySelection(currentPos);
pdoc->InsertString(currentPos, list, lenInsert);
SetEmptySelection(currentPos + lenInsert);
} else {
SetEmptySelection(currentPos);
pdoc->InsertString(currentPos, list + lenEntered, lenInsert - lenEntered);
SetEmptySelection(currentPos + lenInsert - lenEntered);
}
return;
}
}
ac.Start(wMain, idAutoComplete, currentPos, LocationFromPosition(currentPos),
lenEntered, vs.lineHeight, IsUnicodeMode());
PRectangle rcClient = GetClientRectangle();
Point pt = LocationFromPosition(currentPos - lenEntered);
int heightLB = 100;
int widthLB = 100;
if (pt.x >= rcClient.right - widthLB) {
HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB);
Redraw();
pt = LocationFromPosition(currentPos);
}
PRectangle rcac;
rcac.left = pt.x - ac.lb->CaretFromEdge();
if (pt.y >= rcClient.bottom - heightLB && // Wont fit below.
pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above.
rcac.top = pt.y - heightLB;
if (rcac.top < 0) {
heightLB += rcac.top;
rcac.top = 0;
}
} else {
rcac.top = pt.y + vs.lineHeight;
}
rcac.right = rcac.left + widthLB;
rcac.bottom = Platform::Minimum(rcac.top + heightLB, rcClient.bottom);
ac.lb->SetPositionRelative(rcac, wMain);
ac.lb->SetFont(vs.styles[STYLE_DEFAULT].font);
unsigned int aveCharWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
ac.lb->SetAverageCharWidth(aveCharWidth);
ac.lb->SetDoubleClickAction(AutoCompleteDoubleClick, this);
ac.SetList(list);
// Fiddle the position of the list so it is right next to the target and wide enough for all its strings
PRectangle rcList = ac.lb->GetDesiredRect();
int heightAlloced = rcList.bottom - rcList.top;
widthLB = Platform::Maximum(widthLB, rcList.right - rcList.left);
if (maxListWidth != 0)
widthLB = Platform::Minimum(widthLB, aveCharWidth*maxListWidth);
// Make an allowance for large strings in list
rcList.left = pt.x - ac.lb->CaretFromEdge();
rcList.right = rcList.left + widthLB;
if (((pt.y + vs.lineHeight) >= (rcClient.bottom - heightAlloced)) && // Wont fit below.
((pt.y + vs.lineHeight / 2) >= (rcClient.bottom + rcClient.top) / 2)) { // and there is more room above.
rcList.top = pt.y - heightAlloced;
} else {
rcList.top = pt.y + vs.lineHeight;
}
rcList.bottom = rcList.top + heightAlloced;
ac.lb->SetPositionRelative(rcList, wMain);
ac.Show(true);
if (lenEntered != 0) {
AutoCompleteMoveToCurrentWord();
}
}
void ScintillaBase::AutoCompleteCancel() {
ac.Cancel();
}
void ScintillaBase::AutoCompleteMove(int delta) {
ac.Move(delta);
}
void ScintillaBase::AutoCompleteMoveToCurrentWord() {
char wordCurrent[1000];
int i;
int startWord = ac.posStart - ac.startLen;
for (i = startWord; i < currentPos && i - startWord < 1000; i++)
wordCurrent[i - startWord] = pdoc->CharAt(i);
wordCurrent[Platform::Minimum(i - startWord, 999)] = '\0';
ac.Select(wordCurrent);
}
void ScintillaBase::AutoCompleteCharacterAdded(char ch) {
if (ac.IsFillUpChar(ch)) {
AutoCompleteCompleted();
} else if (ac.IsStopChar(ch)) {
ac.Cancel();
} else {
AutoCompleteMoveToCurrentWord();
}
}
void ScintillaBase::AutoCompleteCharacterDeleted() {
if (currentPos < ac.posStart - ac.startLen) {
ac.Cancel();
} else if (ac.cancelAtStartPos && (currentPos <= ac.posStart)) {
ac.Cancel();
} else {
AutoCompleteMoveToCurrentWord();
}
}
void ScintillaBase::AutoCompleteCompleted() {
int item = ac.lb->GetSelection();
char selected[1000];
selected[0] = '\0';
if (item != -1) {
ac.lb->GetValue(item, selected, sizeof(selected));
} else {
ac.Cancel();
return;
}
ac.Show(false);
listSelected = selected;
SCNotification scn = {0};
scn.nmhdr.code = listType > 0 ? SCN_USERLISTSELECTION : SCN_AUTOCSELECTION;
scn.message = 0;
scn.wParam = listType;
scn.listType = listType;
Position firstPos = ac.posStart - ac.startLen;
scn.lParam = firstPos;
scn.text = listSelected.c_str();
NotifyParent(scn);
if (!ac.Active())
return;
ac.Cancel();
if (listType > 0)
return;
Position endPos = currentPos;
if (ac.dropRestOfWord)
endPos = pdoc->ExtendWordSelect(endPos, 1, true);
if (endPos < firstPos)
return;
pdoc->BeginUndoAction();
if (endPos != firstPos) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -