📄 editor.cxx
字号:
// Scintilla source code edit control/** @file Editor.cxx ** Main code for the edit control. **/// Copyright 1998-2004 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"#ifndef PLAT_QT#define INCLUDE_DEPRECATED_FEATURES#endif#include "Scintilla.h"#include "SplitVector.h"#include "Partitioning.h"#include "RunStyles.h"#include "ContractionState.h"#include "CellBuffer.h"#include "KeyMap.h"#include "Indicator.h"#include "XPM.h"#include "LineMarker.h"#include "Style.h"#include "ViewStyle.h"#include "CharClassify.h"#include "Decoration.h"#include "Document.h"#include "PositionCache.h"#include "Editor.h"#ifdef SCI_NAMESPACEusing namespace Scintilla;#endif/* return whether this modification represents an operation that may reasonably be deferred (not done now OR [possibly] at all)*/static bool CanDeferToLastStep(const DocModification& mh) { if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) return true; // CAN skip if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO))) return false; // MUST do if (mh.modificationType & SC_MULTISTEPUNDOREDO) return true; // CAN skip return false; // PRESUMABLY must do}static bool CanEliminate(const DocModification& mh) { return (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;}/* return whether this modification represents the FINAL step in a [possibly lengthy] multi-step Undo/Redo sequence*/static bool IsLastStep(const DocModification& mh) { return (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;}Caret::Caret() : active(false), on(false), period(500) {}Timer::Timer() : ticking(false), ticksToWait(0), tickerID(0) {}Idler::Idler() : state(false), idlerID(0) {}static inline bool IsControlCharacter(int ch) { // iscntrl returns true for lots of chars > 127 which are displayable return ch >= 0 && ch < ' ';}Editor::Editor() { ctrlID = 0; stylesValid = false; printMagnification = 0; printColourMode = SC_PRINT_NORMAL; printWrapState = eWrapWord; cursorMode = SC_CURSORNORMAL; controlCharSymbol = 0; /* Draw the control characters */ hasFocus = false; hideSelection = false; inOverstrike = false; errorStatus = 0; mouseDownCaptures = true; bufferedDraw = true; twoPhaseDraw = true; lastClickTime = 0; dwellDelay = SC_TIME_FOREVER; ticksToDwell = SC_TIME_FOREVER; dwelling = false; ptMouseLast.x = 0; ptMouseLast.y = 0; inDragDrop = ddNone; dropWentOutside = false; posDrag = invalidPosition; posDrop = invalidPosition; selectionType = selChar; lastXChosen = 0; lineAnchor = 0; originalAnchorPos = 0; selType = selStream; moveExtendsSelection = false; xStartSelect = 0; xEndSelect = 0; primarySelection = true; caretXPolicy = CARET_SLOP | CARET_EVEN; caretXSlop = 50; caretYPolicy = CARET_EVEN; caretYSlop = 0; searchAnchor = 0; xOffset = 0; xCaretMargin = 50; horizontalScrollBarVisible = true; scrollWidth = 2000; trackLineWidth = false; lineWidthMaxSeen = 0; verticalScrollBarVisible = true; endAtLastLine = true; caretSticky = false; pixmapLine = Surface::Allocate(); pixmapSelMargin = Surface::Allocate(); pixmapSelPattern = Surface::Allocate(); pixmapIndentGuide = Surface::Allocate(); pixmapIndentGuideHighlight = Surface::Allocate(); currentPos = 0; anchor = 0; targetStart = 0; targetEnd = 0; searchFlags = 0; topLine = 0; posTopLine = 0; lengthForEncode = -1; needUpdateUI = true; braces[0] = invalidPosition; braces[1] = invalidPosition; bracesMatchStyle = STYLE_BRACEBAD; highlightGuideColumn = 0; theEdge = 0; paintState = notPainting; modEventMask = SC_MODEVENTMASKALL; pdoc = new Document(); pdoc->AddRef(); pdoc->AddWatcher(this, 0); recordingMacro = false; foldFlags = 0; wrapState = eWrapNone; wrapWidth = LineLayout::wrapWidthInfinite; wrapStart = wrapLineLarge; wrapEnd = wrapLineLarge; wrapVisualFlags = 0; wrapVisualFlagsLocation = 0; wrapVisualStartIndent = 0; actualWrapVisualStartIndent = 0; convertPastes = true; hsStart = -1; hsEnd = -1; llc.SetLevel(LineLayoutCache::llcCaret); posCache.SetSize(0x400);}Editor::~Editor() { pdoc->RemoveWatcher(this, 0); pdoc->Release(); pdoc = 0; DropGraphics(); delete pixmapLine; delete pixmapSelMargin; delete pixmapSelPattern; delete pixmapIndentGuide; delete pixmapIndentGuideHighlight;}void Editor::Finalise() { SetIdle(false); CancelModes();}void Editor::DropGraphics() { pixmapLine->Release(); pixmapSelMargin->Release(); pixmapSelPattern->Release(); pixmapIndentGuide->Release(); pixmapIndentGuideHighlight->Release();}void Editor::InvalidateStyleData() { stylesValid = false; DropGraphics(); palette.Release(); llc.Invalidate(LineLayout::llInvalid); posCache.Clear(); if (selType == selRectangle) { xStartSelect = XFromPosition(anchor); xEndSelect = XFromPosition(currentPos); }}void Editor::InvalidateStyleRedraw() { NeedWrapping(); InvalidateStyleData(); Redraw();}void Editor::RefreshColourPalette(Palette &pal, bool want) { vs.RefreshColourPalette(pal, want);}void Editor::RefreshStyleData() { if (!stylesValid) { stylesValid = true; AutoSurface surface(this); if (surface) { vs.Refresh(*surface); RefreshColourPalette(palette, true); palette.Allocate(wMain); RefreshColourPalette(palette, false); } SetScrollBars(); }}PRectangle Editor::GetClientRectangle() { return wMain.GetClientPosition();}PRectangle Editor::GetTextRectangle() { PRectangle rc = GetClientRectangle(); rc.left += vs.fixedColumnWidth; rc.right -= vs.rightMarginWidth; return rc;}int Editor::LinesOnScreen() { PRectangle rcClient = GetClientRectangle(); int htClient = rcClient.bottom - rcClient.top; //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1); return htClient / vs.lineHeight;}int Editor::LinesToScroll() { int retVal = LinesOnScreen() - 1; if (retVal < 1) return 1; else return retVal;}int Editor::MaxScrollPos() { //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n", //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1); int retVal = cs.LinesDisplayed(); if (endAtLastLine) { retVal -= LinesOnScreen(); } else { retVal--; } if (retVal < 0) { return 0; } else { return retVal; }}const char *ControlCharacterString(unsigned char ch) { const char *reps[] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" }; if (ch < (sizeof(reps) / sizeof(reps[0]))) { return reps[ch]; } else { return "BAD"; }}/** * Convenience class to ensure LineLayout objects are always disposed. */class AutoLineLayout { LineLayoutCache &llc; LineLayout *ll; AutoLineLayout &operator=(const AutoLineLayout &) { return * this; }public: AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {} ~AutoLineLayout() { llc.Dispose(ll); ll = 0; } LineLayout *operator->() const { return ll; } operator LineLayout *() const { return ll; } void Set(LineLayout *ll_) { llc.Dispose(ll); ll = ll_; }};#ifdef SCI_NAMESPACEnamespace Scintilla {#endif/** * Allows to iterate through the lines of a selection. * Althought it can be called for a stream selection, in most cases * it is inefficient and it should be used only for * a rectangular or a line selection. */class SelectionLineIterator {private: Editor *ed; int line; ///< Current line within the iteration. bool forward; ///< True if iterating by increasing line number, false otherwise. int selStart, selEnd; ///< Positions of the start and end of the selection relative to the start of the document. int minX, maxX; ///< Left and right of selection rectangle.public: int lineStart, lineEnd; ///< Line numbers, first and last lines of the selection. int startPos, endPos; ///< Positions of the beginning and end of the selection on the current line. void Reset() { if (forward) { line = lineStart; } else { line = lineEnd; } } SelectionLineIterator(Editor *ed_, bool forward_ = true) : line(0), startPos(0), endPos(0) { ed = ed_; forward = forward_; selStart = ed->SelectionStart(); selEnd = ed->SelectionEnd(); lineStart = ed->pdoc->LineFromPosition(selStart); lineEnd = ed->pdoc->LineFromPosition(selEnd); // Left of rectangle minX = Platform::Minimum(ed->xStartSelect, ed->xEndSelect); // Right of rectangle maxX = Platform::Maximum(ed->xStartSelect, ed->xEndSelect); Reset(); } ~SelectionLineIterator() {} void SetAt(int line) { if (line < lineStart || line > lineEnd) { startPos = endPos = INVALID_POSITION; } else { if (ed->selType == ed->selRectangle) { // Measure line and return character closest to minX startPos = ed->PositionFromLineX(line, minX); // Measure line and return character closest to maxX endPos = ed->PositionFromLineX(line, maxX); } else if (ed->selType == ed->selLines) { startPos = ed->pdoc->LineStart(line); endPos = ed->pdoc->LineStart(line + 1); } else { // Stream selection, here only for completion if (line == lineStart) { startPos = selStart; } else { startPos = ed->pdoc->LineStart(line); } if (line == lineEnd) { endPos = selEnd; } else { endPos = ed->pdoc->LineStart(line + 1); } } } } bool Iterate() { SetAt(line); if (forward) { line++; } else { line--; } return startPos != INVALID_POSITION; }};#ifdef SCI_NAMESPACE}#endifPoint Editor::LocationFromPosition(int pos) { Point pt; RefreshStyleData(); if (pos == INVALID_POSITION) return pt; int line = pdoc->LineFromPosition(pos); int lineVisible = cs.DisplayFromDoc(line); //Platform::DebugPrintf("line=%d\n", line); AutoSurface surface(this); AutoLineLayout ll(llc, RetrieveLineLayout(line)); if (surface && ll) { // -1 because of adding in for visible lines in following loop. pt.y = (lineVisible - topLine - 1) * vs.lineHeight; pt.x = 0; unsigned int posLineStart = pdoc->LineStart(line); LayoutLine(line, surface, vs, ll, wrapWidth); int posInLine = pos - posLineStart; // In case of very long line put x at arbitrary large position if (posInLine > ll->maxLineLength) { pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)]; } for (int subLine = 0; subLine < ll->lines; subLine++) { if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) { pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)]; if (actualWrapVisualStartIndent != 0) { int lineStart = ll->LineStart(subLine); if (lineStart != 0) // Wrapped pt.x += actualWrapVisualStartIndent * vs.aveCharWidth; } } if (posInLine >= ll->LineStart(subLine)) { pt.y += vs.lineHeight; } } pt.x += vs.fixedColumnWidth - xOffset; } return pt;}int Editor::XFromPosition(int pos) { Point pt = LocationFromPosition(pos); return pt.x - vs.fixedColumnWidth + xOffset;}int Editor::LineFromLocation(Point pt) { return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);}void Editor::SetTopLine(int topLineNew) { topLine = topLineNew; posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));}int Editor::PositionFromLocation(Point pt) { RefreshStyleData(); pt.x = pt.x - vs.fixedColumnWidth + xOffset; int visibleLine = pt.y / vs.lineHeight + topLine; if (pt.y < 0) { // Division rounds towards 0 visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine; } if (visibleLine < 0) visibleLine = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -